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
ng_checksum_constructor(node_p node)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
ng_checksum_newhook(node_p node,hook_p hook,const char * name)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
ng_checksum_rcvmsg(node_p node,item_p item,hook_p lasthook)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
checksum_ipv4(priv_p priv,struct mbuf * m,int l3_offset)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
checksum_ipv6(priv_p priv,struct mbuf * m,int l3_offset)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
ng_checksum_rcvdata(hook_p hook,item_p item)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
ng_checksum_shutdown(node_p node)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
ng_checksum_disconnect(hook_p hook)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 }
727