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