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 NG_FREE_M(m); 686 drop: 687 NG_FREE_ITEM(item); 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 } 729