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 NG_FREE_M(m);
687 drop:
688 NG_FREE_ITEM(item);
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 }
Cache object: 00ea5e74f61957c0464e5a709f91ca94
|