1/*	$NetBSD: npfstream.c,v 1.9 2019/07/23 00:52:02 rmind Exp $	*/
2
3/*
4 * NPF stream processor.
5 *
6 * Public Domain.
7 */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <stdbool.h>
12#include <string.h>
13#include <inttypes.h>
14#include <err.h>
15#include <pcap.h>
16
17#include <arpa/inet.h>
18
19#if !defined(_NPF_STANDALONE)
20#include <net/if.h>
21#include <net/ethertypes.h>
22#include <net/if_ether.h>
23#include <netinet/in_systm.h>
24#include <netinet/ip.h>
25#include <netinet/ip.h>
26#include <netinet/tcp.h>
27
28#include <rump/rump.h>
29#endif
30
31#include "npftest.h"
32
33static struct in_addr	initial_ip;
34static int		snd_packet_no = 0;
35static int		rcv_packet_no = 0;
36
37static void
38process_tcpip(const void *data, size_t len, FILE *fp, ifnet_t *ifp)
39{
40	const struct ether_header *eth = data;
41	const struct ip *ip;
42	const struct tcphdr *th;
43	unsigned hlen, tcpdlen;
44	int error, packetno;
45	const void *p;
46	tcp_seq seq;
47	bool forw;
48
49	if (ntohs(eth->ether_type) != ETHERTYPE_IP) {
50		p = (const char *)data + 4;
51	} else {
52		p = eth + 1;
53	}
54	ip = (const struct ip *)p;
55	hlen = ip->ip_hl << 2;
56	p = (const uint8_t *)ip + hlen;
57	th = (const struct tcphdr *)p;
58
59	tcpdlen = ntohs(ip->ip_len) - hlen - (th->th_off << 2);
60	if (th->th_flags & TH_SYN) {
61		tcpdlen++;
62	}
63	if (th->th_flags & TH_FIN) {
64		tcpdlen++;
65	}
66	seq = ntohl(th->th_seq);
67
68	if (snd_packet_no == 0) {
69		memcpy(&initial_ip, &ip->ip_src, sizeof(struct in_addr));
70	}
71
72	forw = (initial_ip.s_addr == ip->ip_src.s_addr);
73	packetno = forw ? ++snd_packet_no : ++rcv_packet_no;
74
75	int64_t result[11];
76	memset(result, 0, sizeof(result));
77
78	len = ntohs(ip->ip_len);
79	error = rumpns_npf_test_statetrack(ip, len, ifp, forw, result);
80
81	fprintf(fp, "%s%2x %5d %3d %11u %11u %11u %11u %12" PRIxPTR,
82	    forw ? ">" : "<", (th->th_flags & (TH_SYN | TH_ACK | TH_FIN)),
83	    packetno, error, (unsigned)seq, (unsigned)ntohl(th->th_ack),
84	    tcpdlen, ntohs(th->th_win), (uintptr_t)result[0]);
85
86	for (unsigned i = 1; i < __arraycount(result); i++) {
87		fprintf(fp, "%11" PRIu64 " ", result[i]);
88	}
89	fputs("\n", fp);
90}
91
92int
93process_stream(const char *input, const char *output, ifnet_t *ifp)
94{
95	pcap_t *pcap;
96	char pcap_errbuf[PCAP_ERRBUF_SIZE];
97	struct pcap_pkthdr *phdr;
98	const uint8_t *data;
99	FILE *fp;
100
101	pcap = pcap_open_offline(input, pcap_errbuf);
102	if (pcap == NULL) {
103		errx(EXIT_FAILURE, "pcap_open_offline failed: %s", pcap_errbuf);
104	}
105	fp = output ? fopen(output, "w") : stdout;
106	if (fp == NULL) {
107		err(EXIT_FAILURE, "fopen");
108	}
109	fprintf(fp, "#FL %5s %3s %11s %11s %11s %11s %11s %11s %11s "
110	    "%11s %11s %11s %5s %11s %11s %11s %5s\n",
111	    "No", "Err", "Seq", "Ack", "TCP Len", "Win",
112	    "Stream", "RetVal", "State",
113	    "F.END", "F.MAXEND", "F.MAXWIN", "F.WSC",
114	    "T.END", "T.MAXEND", "T.MAXWIN", "T.WSC");
115	while (pcap_next_ex(pcap, &phdr, &data) > 0) {
116		if (phdr->len != phdr->caplen) {
117			warnx("process_stream: truncated packet");
118		}
119		process_tcpip(data, phdr->caplen, fp, ifp);
120	}
121	pcap_close(pcap);
122	fclose(fp);
123
124	return 0;
125}
126