Added
Link Here
|
1 |
/*- |
2 |
* Copyright (c) 2015 Dmitry Vagin <daemon.hammer@ya.ru> |
3 |
* All rights reserved. |
4 |
* |
5 |
* Redistribution and use in source and binary forms, with or without |
6 |
* modification, are permitted provided that the following conditions |
7 |
* are met: |
8 |
* 1. Redistributions of source code must retain the above copyright |
9 |
* notice, this list of conditions and the following disclaimer. |
10 |
* 2. Redistributions in binary form must reproduce the above copyright |
11 |
* notice, this list of conditions and the following disclaimer in the |
12 |
* documentation and/or other materials provided with the distribution. |
13 |
* |
14 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
15 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
17 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
18 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
19 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
20 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
21 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
22 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
23 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
24 |
* SUCH DAMAGE. |
25 |
* |
26 |
*/ |
27 |
|
28 |
#include <sys/cdefs.h> |
29 |
__FBSDID("$FreeBSD$"); |
30 |
|
31 |
#include "opt_inet.h" |
32 |
#include "opt_inet6.h" |
33 |
|
34 |
#include <sys/param.h> |
35 |
#include <sys/systm.h> |
36 |
#include <sys/kernel.h> |
37 |
#include <sys/endian.h> |
38 |
#include <sys/malloc.h> |
39 |
#include <sys/mbuf.h> |
40 |
#include <sys/socket.h> |
41 |
|
42 |
#include <net/bpf.h> |
43 |
#include <net/ethernet.h> |
44 |
#include <net/if.h> |
45 |
#include <net/if_vlan_var.h> |
46 |
|
47 |
#include <netinet/in.h> |
48 |
#include <netinet/ip.h> |
49 |
#include <netinet/ip6.h> |
50 |
#include <netinet/tcp.h> |
51 |
#include <netinet/udp.h> |
52 |
#include <machine/in_cksum.h> |
53 |
|
54 |
#include <netgraph/ng_message.h> |
55 |
#include <netgraph/ng_parse.h> |
56 |
#include <netgraph/netgraph.h> |
57 |
|
58 |
#include <netgraph/ng_checksum.h> |
59 |
|
60 |
/* private data */ |
61 |
struct ng_checksum_priv { |
62 |
hook_p in; |
63 |
hook_p out; |
64 |
uint8_t dlt; /* DLT_XXX from bpf.h */ |
65 |
struct ng_checksum_config *conf; |
66 |
struct ng_checksum_stats stats; |
67 |
}; |
68 |
|
69 |
typedef struct ng_checksum_priv *priv_p; |
70 |
|
71 |
/* Netgraph methods */ |
72 |
static ng_constructor_t ng_checksum_constructor; |
73 |
static ng_rcvmsg_t ng_checksum_rcvmsg; |
74 |
static ng_shutdown_t ng_checksum_shutdown; |
75 |
static ng_newhook_t ng_checksum_newhook; |
76 |
static ng_rcvdata_t ng_checksum_rcvdata; |
77 |
static ng_disconnect_t ng_checksum_disconnect; |
78 |
|
79 |
#define ERROUT(x) { error = (x); goto done; } |
80 |
|
81 |
static const struct ng_parse_struct_field ng_checksum_config_type_fields[] |
82 |
= NG_CHECKSUM_CONFIG_TYPE; |
83 |
static const struct ng_parse_type ng_checksum_config_type = { |
84 |
&ng_parse_struct_type, |
85 |
&ng_checksum_config_type_fields |
86 |
}; |
87 |
|
88 |
static const struct ng_parse_struct_field ng_checksum_stats_fields[] |
89 |
= NG_CHECKSUM_STATS_TYPE; |
90 |
static const struct ng_parse_type ng_checksum_stats_type = { |
91 |
&ng_parse_struct_type, |
92 |
&ng_checksum_stats_fields |
93 |
}; |
94 |
|
95 |
static const struct ng_cmdlist ng_checksum_cmdlist[] = { |
96 |
{ |
97 |
NGM_CHECKSUM_COOKIE, |
98 |
NGM_CHECKSUM_GETDLT, |
99 |
"getdlt", |
100 |
NULL, |
101 |
&ng_parse_uint8_type |
102 |
}, |
103 |
{ |
104 |
NGM_CHECKSUM_COOKIE, |
105 |
NGM_CHECKSUM_SETDLT, |
106 |
"setdlt", |
107 |
&ng_parse_uint8_type, |
108 |
NULL |
109 |
}, |
110 |
{ |
111 |
NGM_CHECKSUM_COOKIE, |
112 |
NGM_CHECKSUM_GETCONFIG, |
113 |
"getconfig", |
114 |
NULL, |
115 |
&ng_checksum_config_type |
116 |
}, |
117 |
{ |
118 |
NGM_CHECKSUM_COOKIE, |
119 |
NGM_CHECKSUM_SETCONFIG, |
120 |
"setconfig", |
121 |
&ng_checksum_config_type, |
122 |
NULL |
123 |
}, |
124 |
{ |
125 |
NGM_CHECKSUM_COOKIE, |
126 |
NGM_CHECKSUM_GET_STATS, |
127 |
"getstats", |
128 |
NULL, |
129 |
&ng_checksum_stats_type |
130 |
}, |
131 |
{ |
132 |
NGM_CHECKSUM_COOKIE, |
133 |
NGM_CHECKSUM_CLR_STATS, |
134 |
"clrstats", |
135 |
NULL, |
136 |
NULL |
137 |
}, |
138 |
{ |
139 |
NGM_CHECKSUM_COOKIE, |
140 |
NGM_CHECKSUM_GETCLR_STATS, |
141 |
"getclrstats", |
142 |
NULL, |
143 |
&ng_checksum_stats_type |
144 |
}, |
145 |
{ 0 } |
146 |
}; |
147 |
|
148 |
static struct ng_type typestruct = { |
149 |
.version = NG_ABI_VERSION, |
150 |
.name = NG_CHECKSUM_NODE_TYPE, |
151 |
.constructor = ng_checksum_constructor, |
152 |
.rcvmsg = ng_checksum_rcvmsg, |
153 |
.shutdown = ng_checksum_shutdown, |
154 |
.newhook = ng_checksum_newhook, |
155 |
.rcvdata = ng_checksum_rcvdata, |
156 |
.disconnect = ng_checksum_disconnect, |
157 |
.cmdlist = ng_checksum_cmdlist, |
158 |
}; |
159 |
|
160 |
NETGRAPH_INIT(checksum, &typestruct); |
161 |
|
162 |
static int |
163 |
ng_checksum_constructor(node_p node) |
164 |
{ |
165 |
priv_p priv; |
166 |
|
167 |
priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK|M_ZERO); |
168 |
priv->dlt = DLT_RAW; |
169 |
|
170 |
NG_NODE_SET_PRIVATE(node, priv); |
171 |
|
172 |
return (0); |
173 |
} |
174 |
|
175 |
static int |
176 |
ng_checksum_newhook(node_p node, hook_p hook, const char *name) |
177 |
{ |
178 |
const priv_p priv = NG_NODE_PRIVATE(node); |
179 |
|
180 |
if (strncmp(name, NG_CHECKSUM_HOOK_IN, strlen(NG_CHECKSUM_HOOK_IN)) == 0) { |
181 |
priv->in = hook; |
182 |
} else if (strncmp(name, NG_CHECKSUM_HOOK_OUT, strlen(NG_CHECKSUM_HOOK_OUT)) == 0) { |
183 |
priv->out = hook; |
184 |
} else |
185 |
return (EINVAL); |
186 |
|
187 |
return (0); |
188 |
} |
189 |
|
190 |
static int |
191 |
ng_checksum_rcvmsg(node_p node, item_p item, hook_p lasthook) |
192 |
{ |
193 |
const priv_p priv = NG_NODE_PRIVATE(node); |
194 |
struct ng_checksum_config *conf, *newconf; |
195 |
struct ng_mesg *msg; |
196 |
struct ng_mesg *resp = NULL; |
197 |
int error = 0; |
198 |
|
199 |
NGI_GET_MSG(item, msg); |
200 |
|
201 |
if (msg->header.typecookie != NGM_CHECKSUM_COOKIE) |
202 |
ERROUT(EINVAL); |
203 |
|
204 |
switch (msg->header.cmd) |
205 |
{ |
206 |
case NGM_CHECKSUM_GETDLT: |
207 |
NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK); |
208 |
|
209 |
if (resp == NULL) |
210 |
ERROUT(ENOMEM); |
211 |
|
212 |
*((uint8_t *) resp->data) = priv->dlt; |
213 |
|
214 |
break; |
215 |
|
216 |
case NGM_CHECKSUM_SETDLT: |
217 |
if (msg->header.arglen != sizeof(uint8_t)) |
218 |
ERROUT(EINVAL); |
219 |
|
220 |
switch (*(uint8_t *) msg->data) |
221 |
{ |
222 |
case DLT_EN10MB: |
223 |
case DLT_RAW: |
224 |
priv->dlt = *(uint8_t *) msg->data; |
225 |
break; |
226 |
|
227 |
default: |
228 |
ERROUT(EINVAL); |
229 |
} |
230 |
|
231 |
break; |
232 |
|
233 |
case NGM_CHECKSUM_GETCONFIG: |
234 |
if (priv->conf == NULL) |
235 |
ERROUT(0); |
236 |
|
237 |
NG_MKRESPONSE(resp, msg, sizeof(struct ng_checksum_config), M_WAITOK); |
238 |
|
239 |
if (resp == NULL) |
240 |
ERROUT(ENOMEM); |
241 |
|
242 |
bcopy(priv->conf, resp->data, sizeof(struct ng_checksum_config)); |
243 |
|
244 |
break; |
245 |
|
246 |
case NGM_CHECKSUM_SETCONFIG: |
247 |
conf = (struct ng_checksum_config *) msg->data; |
248 |
|
249 |
if (msg->header.arglen != sizeof(struct ng_checksum_config)) |
250 |
ERROUT(EINVAL); |
251 |
|
252 |
conf->csum_flags &= NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6; |
253 |
conf->csum_offload &= NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6; |
254 |
|
255 |
newconf = malloc(sizeof(struct ng_checksum_config), M_NETGRAPH, M_WAITOK|M_ZERO); |
256 |
|
257 |
bcopy(conf, newconf, sizeof(struct ng_checksum_config)); |
258 |
|
259 |
if (priv->conf) |
260 |
free(priv->conf, M_NETGRAPH); |
261 |
|
262 |
priv->conf = newconf; |
263 |
|
264 |
break; |
265 |
|
266 |
case NGM_CHECKSUM_GET_STATS: |
267 |
case NGM_CHECKSUM_CLR_STATS: |
268 |
case NGM_CHECKSUM_GETCLR_STATS: |
269 |
if (msg->header.cmd != NGM_CHECKSUM_CLR_STATS) { |
270 |
NG_MKRESPONSE(resp, msg, sizeof(struct ng_checksum_stats), M_WAITOK); |
271 |
|
272 |
if (resp == NULL) |
273 |
ERROUT(ENOMEM); |
274 |
|
275 |
bcopy(&(priv->stats), resp->data, sizeof(struct ng_checksum_stats)); |
276 |
} |
277 |
|
278 |
if (msg->header.cmd != NGM_CHECKSUM_GET_STATS) |
279 |
bzero(&(priv->stats), sizeof(struct ng_checksum_stats)); |
280 |
|
281 |
break; |
282 |
|
283 |
default: |
284 |
ERROUT(EINVAL); |
285 |
} |
286 |
|
287 |
done: |
288 |
NG_RESPOND_MSG(error, node, item, resp); |
289 |
NG_FREE_MSG(msg); |
290 |
|
291 |
return (error); |
292 |
} |
293 |
|
294 |
#define PULLUP_CHECK(mbuf, length) do { \ |
295 |
pullup_len += length; \ |
296 |
if (((mbuf)->m_pkthdr.len < pullup_len) || \ |
297 |
(pullup_len > MHLEN)) { \ |
298 |
return (EINVAL); \ |
299 |
} \ |
300 |
if ((mbuf)->m_len < pullup_len && \ |
301 |
(((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) { \ |
302 |
return (ENOBUFS); \ |
303 |
} \ |
304 |
} while (0) |
305 |
|
306 |
#ifdef INET |
307 |
static int |
308 |
checksum_ipv4(priv_p priv, struct mbuf *m, int l3_offset) |
309 |
{ |
310 |
struct ip *ip4; |
311 |
int pullup_len; |
312 |
int hlen, plen; |
313 |
int processed = 0; |
314 |
|
315 |
pullup_len = l3_offset; |
316 |
|
317 |
PULLUP_CHECK(m, sizeof(struct ip)); |
318 |
ip4 = (struct ip *) mtodo(m, l3_offset); |
319 |
|
320 |
if (ip4->ip_v != IPVERSION) |
321 |
return (EOPNOTSUPP); |
322 |
|
323 |
hlen = ip4->ip_hl << 2; |
324 |
plen = ntohs(ip4->ip_len); |
325 |
|
326 |
if (hlen < sizeof(struct ip) || m->m_pkthdr.len < l3_offset + plen) |
327 |
return (EINVAL); |
328 |
|
329 |
if (m->m_pkthdr.csum_flags & CSUM_IP) { |
330 |
ip4->ip_sum = 0; |
331 |
|
332 |
if ((priv->conf->csum_offload & CSUM_IP) == 0) { |
333 |
if (hlen == sizeof(struct ip)) |
334 |
ip4->ip_sum = in_cksum_hdr(ip4); |
335 |
else |
336 |
ip4->ip_sum = in_cksum_skip(m, l3_offset + hlen, l3_offset); |
337 |
|
338 |
m->m_pkthdr.csum_flags &= ~CSUM_IP; |
339 |
} |
340 |
|
341 |
processed = 1; |
342 |
} |
343 |
|
344 |
pullup_len = l3_offset + hlen; |
345 |
|
346 |
/* We can not calculate a checksum fragmented packets */ |
347 |
if (ip4->ip_off & htons(IP_MF|IP_OFFMASK)) { |
348 |
m->m_pkthdr.csum_flags &= ~(CSUM_TCP|CSUM_UDP); |
349 |
return (0); |
350 |
} |
351 |
|
352 |
switch (ip4->ip_p) |
353 |
{ |
354 |
case IPPROTO_TCP: |
355 |
if (m->m_pkthdr.csum_flags & CSUM_TCP) { |
356 |
struct tcphdr *th; |
357 |
|
358 |
PULLUP_CHECK(m, sizeof(struct tcphdr)); |
359 |
th = (struct tcphdr *) mtodo(m, l3_offset + hlen); |
360 |
|
361 |
th->th_sum = in_pseudo(ip4->ip_src.s_addr, |
362 |
ip4->ip_dst.s_addr, htons(ip4->ip_p + plen - hlen)); |
363 |
|
364 |
if ((priv->conf->csum_offload & CSUM_TCP) == 0) { |
365 |
th->th_sum = in_cksum_skip(m, l3_offset + plen, l3_offset + hlen); |
366 |
m->m_pkthdr.csum_flags &= ~CSUM_TCP; |
367 |
} |
368 |
|
369 |
processed = 1; |
370 |
} |
371 |
|
372 |
m->m_pkthdr.csum_flags &= ~CSUM_UDP; |
373 |
break; |
374 |
|
375 |
case IPPROTO_UDP: |
376 |
if (m->m_pkthdr.csum_flags & CSUM_UDP) { |
377 |
struct udphdr *uh; |
378 |
|
379 |
PULLUP_CHECK(m, sizeof(struct udphdr)); |
380 |
uh = (struct udphdr *) mtodo(m, l3_offset + hlen); |
381 |
|
382 |
uh->uh_sum = in_pseudo(ip4->ip_src.s_addr, |
383 |
ip4->ip_dst.s_addr, htons(ip4->ip_p + plen - hlen)); |
384 |
|
385 |
if ((priv->conf->csum_offload & CSUM_UDP) == 0) { |
386 |
uh->uh_sum = in_cksum_skip(m, |
387 |
l3_offset + plen, l3_offset + hlen); |
388 |
|
389 |
if (uh->uh_sum == 0) |
390 |
uh->uh_sum = 0xffff; |
391 |
|
392 |
m->m_pkthdr.csum_flags &= ~CSUM_UDP; |
393 |
} |
394 |
|
395 |
processed = 1; |
396 |
} |
397 |
|
398 |
m->m_pkthdr.csum_flags &= ~CSUM_TCP; |
399 |
break; |
400 |
|
401 |
default: |
402 |
m->m_pkthdr.csum_flags &= ~(CSUM_TCP|CSUM_UDP); |
403 |
break; |
404 |
} |
405 |
|
406 |
m->m_pkthdr.csum_flags &= ~NG_CHECKSUM_CSUM_IPV6; |
407 |
|
408 |
if (processed) |
409 |
priv->stats.processed++; |
410 |
|
411 |
return (0); |
412 |
} |
413 |
#endif /* INET */ |
414 |
|
415 |
#ifdef INET6 |
416 |
static int |
417 |
checksum_ipv6(priv_p priv, struct mbuf *m, int l3_offset) |
418 |
{ |
419 |
struct ip6_hdr *ip6; |
420 |
struct ip6_ext *ip6e = NULL; |
421 |
int pullup_len; |
422 |
int hlen, plen; |
423 |
int nxt; |
424 |
int processed = 0; |
425 |
|
426 |
pullup_len = l3_offset; |
427 |
|
428 |
PULLUP_CHECK(m, sizeof(struct ip6_hdr)); |
429 |
ip6 = (struct ip6_hdr *) mtodo(m, l3_offset); |
430 |
|
431 |
if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) |
432 |
return (EOPNOTSUPP); |
433 |
|
434 |
hlen = sizeof(struct ip6_hdr); |
435 |
plen = ntohs(ip6->ip6_plen) + hlen; |
436 |
|
437 |
if (m->m_pkthdr.len < l3_offset + plen) |
438 |
return (EINVAL); |
439 |
|
440 |
nxt = ip6->ip6_nxt; |
441 |
|
442 |
for (;;) { |
443 |
switch (nxt) |
444 |
{ |
445 |
case IPPROTO_DSTOPTS: |
446 |
case IPPROTO_HOPOPTS: |
447 |
case IPPROTO_ROUTING: |
448 |
PULLUP_CHECK(m, sizeof(struct ip6_ext)); |
449 |
ip6e = (struct ip6_ext *) mtodo(m, l3_offset + hlen); |
450 |
nxt = ip6e->ip6e_nxt; |
451 |
hlen += (ip6e->ip6e_len + 1) << 3; |
452 |
pullup_len = l3_offset + hlen; |
453 |
break; |
454 |
|
455 |
case IPPROTO_AH: |
456 |
PULLUP_CHECK(m, sizeof(struct ip6_ext)); |
457 |
ip6e = (struct ip6_ext *) mtodo(m, l3_offset + hlen); |
458 |
nxt = ip6e->ip6e_nxt; |
459 |
hlen += (ip6e->ip6e_len + 2) << 2; |
460 |
pullup_len = l3_offset + hlen; |
461 |
break; |
462 |
|
463 |
case IPPROTO_FRAGMENT: |
464 |
/* We can not calculate a checksum fragmented packets */ |
465 |
m->m_pkthdr.csum_flags &= ~(CSUM_TCP_IPV6|CSUM_UDP_IPV6); |
466 |
return (0); |
467 |
|
468 |
default: |
469 |
goto loopend; |
470 |
} |
471 |
|
472 |
if (nxt == 0) |
473 |
return (EINVAL); |
474 |
} |
475 |
|
476 |
loopend: |
477 |
|
478 |
switch (nxt) |
479 |
{ |
480 |
case IPPROTO_TCP: |
481 |
if (m->m_pkthdr.csum_flags & CSUM_TCP_IPV6) { |
482 |
struct tcphdr *th; |
483 |
|
484 |
PULLUP_CHECK(m, sizeof(struct tcphdr)); |
485 |
th = (struct tcphdr *) mtodo(m, l3_offset + hlen); |
486 |
|
487 |
th->th_sum = in6_cksum_pseudo(ip6, plen - hlen, nxt, 0); |
488 |
|
489 |
if ((priv->conf->csum_offload & CSUM_TCP_IPV6) == 0) { |
490 |
th->th_sum = in_cksum_skip(m, l3_offset + plen, l3_offset + hlen); |
491 |
m->m_pkthdr.csum_flags &= ~CSUM_TCP_IPV6; |
492 |
} |
493 |
|
494 |
processed = 1; |
495 |
} |
496 |
|
497 |
m->m_pkthdr.csum_flags &= ~CSUM_UDP_IPV6; |
498 |
break; |
499 |
|
500 |
case IPPROTO_UDP: |
501 |
if (m->m_pkthdr.csum_flags & CSUM_UDP_IPV6) { |
502 |
struct udphdr *uh; |
503 |
|
504 |
PULLUP_CHECK(m, sizeof(struct udphdr)); |
505 |
uh = (struct udphdr *) mtodo(m, l3_offset + hlen); |
506 |
|
507 |
uh->uh_sum = in6_cksum_pseudo(ip6, plen - hlen, nxt, 0); |
508 |
|
509 |
if ((priv->conf->csum_offload & CSUM_UDP_IPV6) == 0) { |
510 |
uh->uh_sum = in_cksum_skip(m, |
511 |
l3_offset + plen, l3_offset + hlen); |
512 |
|
513 |
if (uh->uh_sum == 0) |
514 |
uh->uh_sum = 0xffff; |
515 |
|
516 |
m->m_pkthdr.csum_flags &= ~CSUM_UDP_IPV6; |
517 |
} |
518 |
|
519 |
processed = 1; |
520 |
} |
521 |
|
522 |
m->m_pkthdr.csum_flags &= ~CSUM_TCP_IPV6; |
523 |
break; |
524 |
|
525 |
default: |
526 |
m->m_pkthdr.csum_flags &= ~(CSUM_TCP_IPV6|CSUM_UDP_IPV6); |
527 |
break; |
528 |
} |
529 |
|
530 |
m->m_pkthdr.csum_flags &= ~NG_CHECKSUM_CSUM_IPV4; |
531 |
|
532 |
if (processed) |
533 |
priv->stats.processed++; |
534 |
|
535 |
return (0); |
536 |
} |
537 |
#endif /* INET6 */ |
538 |
|
539 |
#undef PULLUP_CHECK |
540 |
|
541 |
static int |
542 |
ng_checksum_rcvdata(hook_p hook, item_p item) |
543 |
{ |
544 |
const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); |
545 |
struct mbuf *m; |
546 |
hook_p out; |
547 |
int error = 0; |
548 |
|
549 |
priv->stats.received++; |
550 |
|
551 |
NGI_GET_M(item, m); |
552 |
|
553 |
#define PULLUP_CHECK(mbuf, length) do { \ |
554 |
pullup_len += length; \ |
555 |
if (((mbuf)->m_pkthdr.len < pullup_len) || \ |
556 |
(pullup_len > MHLEN)) { \ |
557 |
error = EINVAL; \ |
558 |
goto bypass; \ |
559 |
} \ |
560 |
if ((mbuf)->m_len < pullup_len && \ |
561 |
(((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) { \ |
562 |
error = ENOBUFS; \ |
563 |
goto drop; \ |
564 |
} \ |
565 |
} while (0) |
566 |
|
567 |
if (!(priv->conf && hook == priv->in && m && (m->m_flags & M_PKTHDR))) |
568 |
goto bypass; |
569 |
|
570 |
m->m_pkthdr.csum_flags |= priv->conf->csum_flags; |
571 |
|
572 |
if (m->m_pkthdr.csum_flags & (NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6)) |
573 |
{ |
574 |
struct ether_header *eh; |
575 |
struct ng_checksum_vlan_header *vh; |
576 |
int pullup_len = 0; |
577 |
uint16_t etype; |
578 |
|
579 |
m = m_unshare(m, M_NOWAIT); |
580 |
|
581 |
if (m == NULL) |
582 |
ERROUT(ENOMEM); |
583 |
|
584 |
switch (priv->dlt) |
585 |
{ |
586 |
case DLT_EN10MB: |
587 |
PULLUP_CHECK(m, sizeof(struct ether_header)); |
588 |
eh = mtod(m, struct ether_header *); |
589 |
etype = ntohs(eh->ether_type); |
590 |
|
591 |
for (;;) { /* QinQ support */ |
592 |
switch (etype) |
593 |
{ |
594 |
case 0x8100: |
595 |
case 0x88A8: |
596 |
case 0x9100: |
597 |
PULLUP_CHECK(m, sizeof(struct ng_checksum_vlan_header)); |
598 |
vh = (struct ng_checksum_vlan_header *) mtodo(m, |
599 |
pullup_len - sizeof(struct ng_checksum_vlan_header)); |
600 |
etype = ntohs(vh->etype); |
601 |
break; |
602 |
|
603 |
default: |
604 |
goto loopend; |
605 |
} |
606 |
} |
607 |
loopend: |
608 |
#ifdef INET |
609 |
if (etype == ETHERTYPE_IP && |
610 |
(m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV4)) { |
611 |
error = checksum_ipv4(priv, m, pullup_len); |
612 |
if (error == ENOBUFS) |
613 |
goto drop; |
614 |
} else |
615 |
#endif |
616 |
#ifdef INET6 |
617 |
if (etype == ETHERTYPE_IPV6 && |
618 |
(m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV6)) { |
619 |
error = checksum_ipv6(priv, m, pullup_len); |
620 |
if (error == ENOBUFS) |
621 |
goto drop; |
622 |
} else |
623 |
#endif |
624 |
{ |
625 |
m->m_pkthdr.csum_flags &= |
626 |
~(NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6); |
627 |
} |
628 |
|
629 |
break; |
630 |
|
631 |
case DLT_RAW: |
632 |
#ifdef INET |
633 |
if (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV4) |
634 |
{ |
635 |
error = checksum_ipv4(priv, m, pullup_len); |
636 |
|
637 |
if (error == 0) |
638 |
goto bypass; |
639 |
else if (error == ENOBUFS) |
640 |
goto drop; |
641 |
} |
642 |
#endif |
643 |
#ifdef INET6 |
644 |
if (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV6) |
645 |
{ |
646 |
error = checksum_ipv6(priv, m, pullup_len); |
647 |
|
648 |
if (error == 0) |
649 |
goto bypass; |
650 |
else if (error == ENOBUFS) |
651 |
goto drop; |
652 |
} |
653 |
#endif |
654 |
if (error) |
655 |
m->m_pkthdr.csum_flags &= |
656 |
~(NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6); |
657 |
|
658 |
break; |
659 |
|
660 |
default: |
661 |
ERROUT(EINVAL); |
662 |
} |
663 |
} |
664 |
|
665 |
#undef PULLUP_CHECK |
666 |
|
667 |
bypass: |
668 |
out = NULL; |
669 |
|
670 |
if (hook == priv->in) { |
671 |
/* return frames on 'in' hook if 'out' not connected */ |
672 |
out = priv->out ? priv->out : priv->in; |
673 |
} else if (hook == priv->out && priv->in) { |
674 |
/* pass frames on 'out' hook if 'in' connected */ |
675 |
out = priv->in; |
676 |
} |
677 |
|
678 |
if (out == NULL) |
679 |
ERROUT(0); |
680 |
|
681 |
NG_FWD_NEW_DATA(error, item, out, m); |
682 |
|
683 |
return (error); |
684 |
|
685 |
done: |
686 |
drop: |
687 |
NG_FREE_ITEM(item); |
688 |
NG_FREE_M(m); |
689 |
|
690 |
priv->stats.dropped++; |
691 |
|
692 |
return (error); |
693 |
} |
694 |
|
695 |
static int |
696 |
ng_checksum_shutdown(node_p node) |
697 |
{ |
698 |
const priv_p priv = NG_NODE_PRIVATE(node); |
699 |
|
700 |
NG_NODE_SET_PRIVATE(node, NULL); |
701 |
NG_NODE_UNREF(node); |
702 |
|
703 |
if (priv->conf) |
704 |
free(priv->conf, M_NETGRAPH); |
705 |
|
706 |
free(priv, M_NETGRAPH); |
707 |
|
708 |
return (0); |
709 |
} |
710 |
|
711 |
static int |
712 |
ng_checksum_disconnect(hook_p hook) |
713 |
{ |
714 |
priv_p priv; |
715 |
|
716 |
priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); |
717 |
|
718 |
if (hook == priv->in) |
719 |
priv->in = NULL; |
720 |
|
721 |
if (hook == priv->out) |
722 |
priv->out = NULL; |
723 |
|
724 |
if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 && |
725 |
NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */ |
726 |
ng_rmnode_self(NG_HOOK_NODE(hook)); |
727 |
|
728 |
return (0); |
729 |
} |