]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/netgraph/ng_checksum.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / sys / netgraph / ng_checksum.c
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 }