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 } 730