1/*	$NetBSD: npf_state_test.c,v 1.1.2.4 2012/11/18 21:48:56 riz Exp $	*/
2
3/*
4 * NPF state tracking test.
5 *
6 * Public Domain.
7 */
8
9#include <sys/types.h>
10#include <sys/kmem.h>
11
12#include "npf_impl.h"
13#include "npf_test.h"
14
15typedef struct {
16	int		tcpfl;		/* TCP flags. */
17	int		tlen;		/* TCP data length. */
18	uint32_t	seq;		/* SEQ number. */
19	uint32_t	ack;		/* ACK number. */
20	uint32_t	win;		/* TCP Window. */
21	int		flags;		/* Direction et al. */
22} tcp_meta_t;
23
24#define	S	TH_SYN
25#define	A	TH_ACK
26#define	F	TH_FIN
27#define	OUT	0x1
28#define	IN	0x2
29#define	ERR	0x4
30#define	CLEAR	.flags = 0
31
32static const tcp_meta_t packet_sequence[] = {
33	/*
34	 *	TCP data	SEQ	ACK		WIN
35	 */
36
37	/* Out of order ACK. */
38	{ S,	0,		9999,	0,		4096,	OUT	},
39	{ S|A,	0,		9,	10000,		2048,	IN	},
40	{ A,	0,		10000,	10,		4096,	OUT	},
41	/* --- */
42	{ A,	0,		10,	10000,		2048,	IN	},
43	{ A,	1000,		10000,	10,		4096,	OUT	},
44	{ A,	1000,		11000,	10,		4096,	OUT	},
45	{ A,	0,		10,	12000,		2048,	IN	},
46	{ A,	0,		10,	13000,		2048,	IN	},
47	{ A,	1000,		12000,	10,		4096,	OUT	},
48	{ A,	0,		10,	11000,		1048,	IN	},
49	/* --- */
50	{ A,	1000,		14000,	10,		4096,	OUT	},
51	{ A,	0,		10,	13000,		2048,	IN	},
52	{ CLEAR },
53
54	/* Retransmission after out of order ACK and missing ACK. */
55	{ S,	0,		9999,	0,		1000,	OUT	},
56	{ S|A,	0,		9,	10000,		4000,	IN	},
57	{ A,	0,		10000,	10,		1000,	OUT	},
58	/* --- */
59	{ A,	1000,		10000,	10,		1000,	OUT	},
60	{ A,	0,		10,	11000,		4000,	IN	},
61	{ A,	1000,		11000,	10,		1000,	OUT	},
62	{ A,	1000,		12000,	10,		1000,	OUT	},
63	{ A,	1000,		13000,	10,		1000,	OUT	},
64	{ A,	1000,		14000,	10,		1000,	OUT	},
65	/* --- Assume the first was delayed; second was lost after us. */
66	{ A,	0,		10,	15000,		4000,	IN	},
67	{ A,	0,		10,	15000,		2000,	IN	},
68	/* --- */
69	{ A,	1000,		12000,	10,		1000,	OUT	},
70	{ CLEAR },
71
72	/* FIN exchange with retransmit. */
73	{ S,	0,		999,	0,		1000,	OUT	},
74	{ S|A,	0,		9,	1000,		2000,	IN	},
75	{ A,	0,		1000,	10,		1000,	OUT	},
76	/* --- */
77	{ F,	0,		10,	1000,		2000,	IN	},
78	{ F,	0,		1000,	10,		1000,	OUT	},
79	{ A,	0,		1000,	11,		1000,	OUT	},
80	/* --- */
81	{ F,	0,		1000,	11,		1000,	OUT	},
82	{ F,	0,		1000,	11,		1000,	OUT	},
83	{ A,	0,		11,	1001,		2000,	OUT	},
84	{ CLEAR },
85
86	/* Out of window. */
87	{ S,	0,		9,	0,		8760,	OUT	},
88	{ S|A,	0,		9999,	10,		1000,	IN	},
89	{ A,	0,		10,	10000,		8760,	OUT	},
90	/* --- */
91	{ A,	1460,		10000,	10,		1000,	IN	},
92	{ A,	1460,		11460,	10,		1000,	IN	},
93	{ A,	0,		10,	12920,		8760,	OUT	},
94	{ A,	1460,		12920,	10,		1000,	IN	},
95	{ A,	0,		10,	14380,		8760,	OUT	},
96	{ A,	1460,		17300,	10,		1000,	IN	},
97	{ A,	0,		10,	14380,		8760,	OUT	},
98	{ A,	1460,		18760,	10,		1000,	IN	},
99	{ A,	0,		10,	14380,		8760,	OUT	},
100	{ A,	1460,		20220,	10,		1000,	IN	},
101	{ A,	0,		10,	14380,		8760,	OUT	},
102	{ A,	1460,		21680,	10,		1000,	IN	},
103	{ A,	0,		10,	14380,		8760,	OUT	},
104	/* --- */
105	{ A,	1460,		14380,	10,		1000,	IN	},
106	{ A,	1460,		23140,	10,		1000,	IN|ERR	},
107	{ CLEAR },
108
109};
110
111#undef S
112#undef A
113#undef F
114
115static struct mbuf *
116construct_packet(const tcp_meta_t *p)
117{
118	struct mbuf *m = mbuf_construct(IPPROTO_TCP);
119	struct ip *ip;
120	struct tcphdr *th;
121
122	th = mbuf_return_hdrs(m, false, &ip);
123
124	/* Imitate TCP payload, set TCP sequence numbers, flags and window. */
125	ip->ip_len = htons(sizeof(struct ip) + sizeof(struct tcphdr) + p->tlen);
126	th->th_seq = htonl(p->seq);
127	th->th_ack = htonl(p->ack);
128	th->th_flags = p->tcpfl;
129	th->th_win = htons(p->win);
130	return m;
131}
132
133static bool
134process_packet(const int i, npf_state_t *nst, bool *snew)
135{
136	const tcp_meta_t *p = &packet_sequence[i];
137	npf_cache_t npc = { .npc_info = 0 };
138	nbuf_t nbuf;
139	int ret;
140
141	if (p->flags == 0) {
142		npf_state_destroy(nst);
143		*snew = true;
144		return true;
145	}
146
147	const void *dummy_ifp = (void *)0xdeadbeef;
148	nbuf_init(&nbuf, construct_packet(p), dummy_ifp);
149	ret = npf_cache_all(&npc, &nbuf);
150	KASSERT((ret & NPC_IPFRAG) == 0);
151
152	if (*snew) {
153		ret = npf_state_init(&npc, &nbuf, nst);
154		KASSERT(ret == true);
155		*snew = false;
156	}
157	ret = npf_state_inspect(&npc, &nbuf, nst, p->flags == OUT);
158	m_freem(nbuf.nb_mbuf);
159
160	return ret ? true : (p->flags & ERR) != 0;
161}
162
163bool
164npf_state_test(bool verbose)
165{
166	npf_state_t nst;
167	bool snew = true;
168	bool ok = true;
169
170	for (u_int i = 0; i < __arraycount(packet_sequence); i++) {
171		if (process_packet(i, &nst, &snew)) {
172			continue;
173		}
174		if (verbose) {
175			printf("Failed on packet %d, state dump:\n", i);
176			npf_state_dump(&nst);
177		}
178		ok = false;
179	}
180	return ok;
181}
182