xref: /netbsd-src/usr.sbin/npf/npftest/libnpftest/npf_nbuf_test.c (revision f916b9b1ff9c7fa95cc504cdcf5a9d088475af06)
1 /*
2  * NPF nbuf interface tests.
3  *
4  * Public Domain.
5  */
6 
7 #ifdef _KERNEL
8 #include <sys/types.h>
9 #include <sys/kmem.h>
10 #endif
11 
12 #include "npf_impl.h"
13 #include "npf_test.h"
14 
15 #define	MBUF_CHAIN_LEN		128
16 
17 CTASSERT((MBUF_CHAIN_LEN % sizeof(uint32_t)) == 0);
18 
19 static void
mbuf_consistency_check(nbuf_t * nbuf)20 mbuf_consistency_check(nbuf_t *nbuf)
21 {
22 	struct mbuf *m = nbuf_head_mbuf(nbuf);
23 
24 	while (m) {
25 		assert(m->m_type != MT_FREE);
26 		m = m->m_next;
27 	}
28 }
29 
30 static char *
parse_nbuf_chain(struct mbuf * m)31 parse_nbuf_chain(struct mbuf *m)
32 {
33 	ifnet_t *dummy_ifp = npf_test_addif(IFNAME_TEST, false, false);
34 	char *s = kmem_zalloc(MBUF_CHAIN_LEN + 1, KM_SLEEP);
35 	nbuf_t nbuf;
36 	void *nptr;
37 	int n;
38 
39 	nbuf_init(npf_getkernctx(), &nbuf, m, dummy_ifp);
40 
41 	nptr = nbuf_advance(&nbuf, (random() % 16) + 1, (random() % 16) + 1);
42 	mbuf_consistency_check(&nbuf);
43 	assert(nptr != NULL);
44 	nbuf_reset(&nbuf);
45 
46 	for (n = 0; ; ) {
47 		char d[4 + 1];
48 
49 		nptr = nbuf_ensure_contig(&nbuf, sizeof(uint32_t));
50 		if (nptr == NULL) {
51 			break;
52 		}
53 		mbuf_consistency_check(&nbuf);
54 		memcpy(&d, nptr, sizeof(uint32_t));
55 
56 		d[sizeof(d) - 1] = '\0';
57 		strcat(s, d);
58 
59 		if (n + sizeof(uint32_t) == MBUF_CHAIN_LEN) {
60 			assert(nbuf_advance(&nbuf, sizeof(uint32_t) - 1, 0));
61 			assert(!nbuf_advance(&nbuf, 1, 0));
62 			break;
63 		}
64 		if (!nbuf_advance(&nbuf, sizeof(uint32_t), 0)) {
65 			break;
66 		}
67 		n += sizeof(uint32_t);
68 	}
69 	mbuf_consistency_check(&nbuf);
70 	m_freem(nbuf_head_mbuf(&nbuf));
71 	return s;
72 }
73 
74 static char *
mbuf_getstring(struct mbuf * m)75 mbuf_getstring(struct mbuf *m)
76 {
77 	char *s = kmem_zalloc(MBUF_CHAIN_LEN + 1, KM_SLEEP);
78 	unsigned tlen = 0;
79 
80 	while (m) {
81 		int len = m->m_len;
82 		char *d = m->m_data;
83 		while (len--) {
84 			s[tlen++] = *d++;
85 		}
86 		m = m->m_next;
87 	}
88 	return s;
89 }
90 
91 static struct mbuf *
mbuf_alloc_with_off(size_t off,int len)92 mbuf_alloc_with_off(size_t off, int len)
93 {
94 	struct mbuf *m;
95 
96 	KASSERT(off + len < MLEN);
97 	m = m_get(M_WAITOK, MT_DATA);
98 	m->m_data = (char *)m->m_data + off;
99 	m->m_len = len;
100 	return m;
101 }
102 
103 /*
104  * Create an mbuf chain, each of 1 byte size.
105  */
106 static struct mbuf *
mbuf_bytesize(size_t clen)107 mbuf_bytesize(size_t clen)
108 {
109 	struct mbuf *m0 = NULL, *m = NULL;
110 	unsigned i, n;
111 
112 	/* Chain of clen (e.g. 128) mbufs, each storing 1 byte of data. */
113 	for (i = 0, n = 0; i < clen; i++) {
114 		/* Range of offset: 0 .. 15. */
115 		m0 = mbuf_alloc_with_off(n & 0xf, 1);
116 
117 		/* Fill data with letters from 'a' to 'z'. */
118 		memset(m0->m_data, 'a' + n, 1);
119 		n = ('a' + n) < 'z' ? n + 1 : 0;
120 
121 		/* Next mbuf.. */
122 		m0->m_next = m;
123 		m = m0;
124 	}
125 
126 	m0 = m_gethdr(M_WAITOK, MT_HEADER);
127 	m0->m_pkthdr.len = clen;
128 	m0->m_len = 0;
129 	m0->m_next = m;
130 	return m0;
131 }
132 
133 /*
134  * Generate random number of mbufs, with random offsets and lengths.
135  */
136 static struct mbuf *
mbuf_random_len(size_t chain_len)137 mbuf_random_len(size_t chain_len)
138 {
139 	struct mbuf *m0 = NULL, *m = NULL;
140 	unsigned tlen = 0, n = 0;
141 
142 	while (tlen < chain_len) {
143 		unsigned off, len;
144 		char *d;
145 
146 		/* Random offset and length range: 1 .. 16. */
147 		off = (random() % 16) + 1;
148 		len = (random() % 16) + 1;
149 
150 		/* Do not exceed 128 bytes of total length. */
151 		if (tlen + len > chain_len) {
152 			len = chain_len - tlen;
153 		}
154 		tlen += len;
155 
156 		/* Consistently fill data with letters from 'a' to 'z'. */
157 		m0 = mbuf_alloc_with_off(off, len);
158 		d = m0->m_data;
159 		while (len--) {
160 			*d++ = ('a' + n);
161 			n = ('a' + n) < 'z' ? n + 1 : 0;
162 		}
163 
164 		/* Next mbuf.. */
165 		m0->m_next = m;
166 		m = m0;
167 	}
168 	KASSERT(tlen == chain_len);
169 
170 	m0 = m_gethdr(M_WAITOK, MT_HEADER);
171 	m0->m_pkthdr.len = tlen;
172 	m0->m_next = m;
173 	m0->m_len = 0;
174 	return m0;
175 }
176 
177 static bool
validate_mbuf_data(char * bufa,char * bufb)178 validate_mbuf_data(char *bufa, char *bufb)
179 {
180 	bool ok = strcmp(bufa, bufb) == 0;
181 
182 	if (!ok) {
183 		printf("Buffer A: %s\nBuffer B: %s\n", bufa, bufb);
184 	}
185 	kmem_free(bufa, MBUF_CHAIN_LEN + 1);
186 	kmem_free(bufb, MBUF_CHAIN_LEN + 1);
187 	return ok;
188 }
189 
190 bool
npf_nbuf_test(bool verbose)191 npf_nbuf_test(bool verbose)
192 {
193 	struct mbuf *m;
194 	char *bufa, *bufb;
195 	unsigned n = 10000;
196 	bool ok;
197 
198 	while (n--) {
199 		m = mbuf_random_len(MBUF_CHAIN_LEN);
200 		bufa = mbuf_getstring(m);
201 		bufb = parse_nbuf_chain(m);
202 		ok = validate_mbuf_data(bufa, bufb);
203 		CHECK_TRUE(ok);
204 	}
205 
206 	m = mbuf_bytesize(MBUF_CHAIN_LEN);
207 	bufa = mbuf_getstring(m);
208 	bufb = parse_nbuf_chain(m);
209 	ok = validate_mbuf_data(bufa, bufb);
210 	CHECK_TRUE(ok);
211 
212 	(void)verbose;
213 	return true;
214 }
215