npf_mbuf_subr.c revision 1.12 1 /*
2 * NPF testing - helper routines.
3 *
4 * Public Domain.
5 */
6
7 #ifdef _KERNEL
8 #include <sys/types.h>
9 #include <sys/kmem.h>
10 #include <net/if_ether.h>
11 #endif
12
13 #include "npf_impl.h"
14 #include "npf_test.h"
15
16
17 #if defined(_NPF_STANDALONE)
18 struct mbuf *
19 npfkern_m_get(npf_t *npf __unused, unsigned flags, size_t space)
20 {
21 unsigned mlen = offsetof(struct mbuf, m_data0[space]);
22 struct mbuf *m;
23
24 m = calloc(1, mlen);
25 if (m) {
26 m->m_type = 1;
27 m->m_flags = flags;
28 m->m_data = m->m_data0;
29 }
30 return m;
31 }
32 #else
33 struct mbuf *
34 npfkern_m_get(npf_t *npf __unused, unsigned flags, size_t space)
35 {
36 return m_get(flags, space);
37 }
38 #endif
39
40 static void *
41 npfkern_m_getdata(const struct mbuf *m)
42 {
43 return m->m_data;
44 }
45
46 static struct mbuf *
47 npfkern_m_next(struct mbuf *m)
48 {
49 return m->m_next;
50 }
51
52 static size_t
53 npfkern_m_buflen(const struct mbuf *m)
54 {
55 return m->m_len;
56 }
57
58 size_t
59 npfkern_m_length(const struct mbuf *m)
60 {
61 const struct mbuf *m0;
62 unsigned pktlen = 0;
63
64 if ((m->m_flags & M_PKTHDR) != 0)
65 return m->m_pkthdr.len;
66 for (m0 = m; m0 != NULL; m0 = m0->m_next)
67 pktlen += m0->m_len;
68 return pktlen;
69 }
70
71 void
72 npfkern_m_freem(struct mbuf *m)
73 {
74 #ifdef _NPF_STANDALONE
75 struct mbuf *n;
76
77 do {
78 n = m->m_next;
79 m->m_type = MT_FREE;
80 free(m);
81 m = n;
82 } while (m);
83 #else
84 m_freem(m);
85 #endif
86 }
87
88 static bool
89 npfkern_m_ensure_contig(struct mbuf **m0, size_t len)
90 {
91 struct mbuf *m1;
92 unsigned tlen;
93 char *dptr;
94
95 tlen = npfkern_m_length(*m0);
96 if ((m1 = npfkern_m_get(NULL, M_PKTHDR, tlen)) == NULL) {
97 return false;
98 }
99 m1->m_pkthdr.len = m1->m_len = tlen;
100 dptr = m1->m_data;
101 for (struct mbuf *m = *m0; m != NULL; m = m->m_next) {
102 memcpy(dptr, m->m_data, m->m_len);
103 dptr += m->m_len;
104 }
105 npfkern_m_freem(*m0);
106 *m0 = m1;
107 (void)len;
108 return true;
109 }
110
111
112 struct mbuf *
113 mbuf_getwithdata(const void *data, size_t len)
114 {
115 struct mbuf *m;
116 void *dst;
117
118 m = m_gethdr(M_WAITOK, MT_HEADER);
119 assert(m != NULL);
120 dst = mtod(m, void *);
121 memcpy(dst, data, len);
122 m->m_pkthdr.len = len;
123 m->m_len = len;
124 return m;
125 }
126
127 struct mbuf *
128 mbuf_construct_ether(int proto)
129 {
130 struct mbuf *m0, *m1;
131 struct ether_header *ethdr;
132
133 m0 = m_gethdr(M_WAITOK, MT_HEADER);
134 ethdr = mtod(m0, struct ether_header *);
135 ethdr->ether_type = htons(ETHERTYPE_IP);
136 m0->m_pkthdr.len = sizeof(struct ether_header);
137 m0->m_len = sizeof(struct ether_header);
138
139 m1 = mbuf_construct(proto);
140 m0->m_next = m1;
141 m1->m_next = NULL;
142 return m0;
143 }
144
145 static int
146 mbuf_fill_proto(int proto, void *l4data)
147 {
148 struct tcphdr *th;
149 int size = 0;
150
151 switch (proto) {
152 case IPPROTO_TCP:
153 th = l4data;
154 th->th_off = sizeof(struct tcphdr) >> 2;
155 size = sizeof(struct tcphdr);
156 break;
157 case IPPROTO_UDP:
158 size = sizeof(struct udphdr);
159 break;
160 case IPPROTO_ICMP:
161 size = offsetof(struct icmp, icmp_data);
162 break;
163 }
164 return size;
165 }
166
167 struct mbuf *
168 mbuf_construct(int proto)
169 {
170 struct mbuf *m;
171 struct ip *iphdr;
172 void *l4data;
173 int size;
174
175 m = m_gethdr(M_WAITOK, MT_HEADER);
176 iphdr = mtod(m, struct ip *);
177
178 iphdr->ip_v = IPVERSION;
179 iphdr->ip_hl = sizeof(struct ip) >> 2;
180 iphdr->ip_off = 0;
181 iphdr->ip_ttl = 64;
182 iphdr->ip_p = proto;
183
184 size = sizeof(struct ip);
185 l4data = (void *)(iphdr + 1);
186 size += mbuf_fill_proto(proto, l4data);
187 iphdr->ip_len = htons(size);
188
189 m->m_pkthdr.len = size;
190 m->m_len = size;
191 m->m_next = NULL;
192 return m;
193 }
194
195 struct mbuf *
196 mbuf_construct6(int proto)
197 {
198 struct mbuf *m;
199 struct ip6_hdr *ip6;
200 void *l4data;
201 int size;
202
203 m = m_gethdr(M_WAITOK, MT_HEADER);
204 ip6 = mtod(m, struct ip6_hdr *);
205
206 ip6->ip6_vfc = IPV6_VERSION;
207 ip6->ip6_nxt = proto;
208 ip6->ip6_hlim = 64;
209
210 size = sizeof(struct ip6_hdr);
211 l4data = (void *)(ip6 + 1);
212 size += mbuf_fill_proto(proto, l4data);
213 ip6->ip6_plen = htons(size);
214
215 m->m_pkthdr.len = size;
216 m->m_len = size;
217 m->m_next = NULL;
218 return m;
219 }
220
221 void *
222 mbuf_return_hdrs(struct mbuf *m, bool ether, struct ip **ip)
223 {
224 struct ip *iphdr;
225
226 if (ether) {
227 struct mbuf *mn = m->m_next;
228 iphdr = mtod(mn, struct ip *);
229 } else {
230 iphdr = mtod(m, struct ip *);
231 }
232 *ip = iphdr;
233 return (void *)(iphdr + 1);
234 }
235
236 void *
237 mbuf_return_hdrs6(struct mbuf *m, struct ip6_hdr **ip6)
238 {
239 struct ip6_hdr *ip6hdr = mtod(m, struct ip6_hdr *);
240
241 *ip6 = ip6hdr;
242 return (void *)(ip6hdr + 1);
243 }
244
245 void
246 mbuf_icmp_append(struct mbuf *m, struct mbuf *m_orig)
247 {
248 struct ip *iphdr = mtod(m, struct ip *);
249 const size_t hlen = iphdr->ip_hl << 2;
250 void *p = (uint8_t *)iphdr + hlen;
251 struct icmp *ic = (struct icmp *)p;
252 const size_t addlen = m_length(m_orig);
253
254 iphdr->ip_len = htons(ntohs(iphdr->ip_len) + addlen);
255 memcpy(&ic->icmp_ip, mtod(m_orig, struct ip *), addlen);
256 m->m_pkthdr.len += addlen;
257 m->m_len += addlen;
258 m_freem(m_orig);
259 }
260
261 struct mbuf *
262 mbuf_get_pkt(int af, int proto, const char *src, const char *dst,
263 int sport, int dport)
264 {
265 struct mbuf *m;
266 struct ip *ip;
267 struct ip6_hdr *ip6;
268 struct tcphdr *th;
269 struct udphdr *uh;
270 void *p, *ipsrc, *ipdst;
271
272 switch (af) {
273 case AF_INET6:
274 m = mbuf_construct6(proto);
275 p = mbuf_return_hdrs6(m, &ip6);
276 ipsrc = &ip6->ip6_src;
277 ipdst = &ip6->ip6_dst;
278 break;
279 case AF_INET:
280 default:
281 m = mbuf_construct(proto);
282 p = mbuf_return_hdrs(m, false, &ip);
283 ipsrc = &ip->ip_src.s_addr;
284 ipdst = &ip->ip_dst.s_addr;
285 }
286
287 npf_inet_pton(af, src, ipsrc);
288 npf_inet_pton(af, dst, ipdst);
289
290 switch (proto) {
291 case IPPROTO_TCP:
292 th = p;
293 th->th_sport = htons(sport);
294 th->th_dport = htons(dport);
295 break;
296 case IPPROTO_UDP:
297 uh = p;
298 uh->uh_sport = htons(sport);
299 uh->uh_dport = htons(dport);
300 break;
301 default:
302 KASSERT(false);
303 }
304 return m;
305 }
306
307 struct mbuf *
308 mbuf_get_frame(const char *src, const char *dst, uint16_t type)
309 {
310 struct mbuf *m0;
311 struct ether_header *ethdr;
312
313 uint8_t saddr[ETHER_ADDR_LEN];
314 uint8_t daddr[ETHER_ADDR_LEN];
315
316 (void)ether_aton_r(saddr, ETHER_ADDR_LEN, src);
317 (void)ether_aton_r(daddr, ETHER_ADDR_LEN, dst);
318
319 m0 = m_gethdr(M_WAITOK, MT_HEADER);
320 ethdr = mtod(m0, struct ether_header *);
321 ethdr->ether_type = type;
322 memcpy(ethdr->ether_dhost, daddr, sizeof(ethdr->ether_dhost));
323 memcpy(ethdr->ether_shost, saddr, sizeof(ethdr->ether_shost));
324 m0->m_pkthdr.len = sizeof(struct ether_header);
325 m0->m_len = sizeof(struct ether_header);
326 m0->m_next = NULL;
327
328 return m0;
329 }
330
331 npf_cache_t *
332 get_cached_pkt(struct mbuf *m, const char *ifname, uint32_t layer)
333 {
334 ifnet_t *ifp = npf_test_getif(ifname ? ifname : IFNAME_DUMMY);
335 npf_cache_t *npc = kmem_zalloc(sizeof(npf_cache_t), KM_SLEEP);
336 nbuf_t *nbuf = kmem_zalloc(sizeof(nbuf_t), KM_SLEEP);
337 int ret;
338
339 npc->npc_info = 0;
340 npc->npc_ctx = npf_getkernctx();
341
342 nbuf_init(npc->npc_ctx, nbuf, m, ifp);
343 npc->npc_nbuf = nbuf;
344
345 if (layer & NPF_RULE_LAYER_3)
346 ret = npf_cache_all(npc);
347 else
348 ret = npf_cache_ether(npc);
349 assert(ret); (void)ret;
350
351 return npc;
352 }
353
354 void
355 put_cached_pkt(npf_cache_t *npc)
356 {
357 struct mbuf *m = nbuf_head_mbuf(npc->npc_nbuf);
358 kmem_free(npc->npc_nbuf, sizeof(nbuf_t));
359 kmem_free(npc, sizeof(npf_cache_t));
360 m_freem(m);
361 }
362
363 const npf_mbufops_t npftest_mbufops = {
364 .alloc = npfkern_m_get,
365 .free = npfkern_m_freem,
366 .getdata = npfkern_m_getdata,
367 .getnext = npfkern_m_next,
368 .getlen = npfkern_m_buflen,
369 .getchainlen = npfkern_m_length,
370 .ensure_contig = npfkern_m_ensure_contig,
371 .ensure_writable = NULL,
372 };
373