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