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