1 /*- 2 * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org> 3 * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $SourceForge: ng_netflow.c,v 1.30 2004/09/05 11:37:43 glebius Exp $ 28 */ 29 30 static const char rcs_id[] = 31 "@(#) $FreeBSD$"; 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/kernel.h> 36 #include <sys/limits.h> 37 #include <sys/mbuf.h> 38 #include <sys/socket.h> 39 #include <sys/syslog.h> 40 #include <sys/ctype.h> 41 42 #include <net/if.h> 43 #include <net/ethernet.h> 44 #include <net/if_arp.h> 45 #include <net/if_var.h> 46 #include <net/if_vlan_var.h> 47 #include <net/bpf.h> 48 #include <netinet/in.h> 49 #include <netinet/in_systm.h> 50 #include <netinet/ip.h> 51 #include <netinet/tcp.h> 52 #include <netinet/udp.h> 53 54 #include <netgraph/ng_message.h> 55 #include <netgraph/ng_parse.h> 56 #include <netgraph/netgraph.h> 57 #include <netgraph/netflow/netflow.h> 58 #include <netgraph/netflow/ng_netflow.h> 59 60 /* Netgraph methods */ 61 static ng_constructor_t ng_netflow_constructor; 62 static ng_rcvmsg_t ng_netflow_rcvmsg; 63 static ng_close_t ng_netflow_close; 64 static ng_shutdown_t ng_netflow_rmnode; 65 static ng_newhook_t ng_netflow_newhook; 66 static ng_rcvdata_t ng_netflow_rcvdata; 67 static ng_disconnect_t ng_netflow_disconnect; 68 69 /* Parse type for struct ng_netflow_info */ 70 static const struct ng_parse_struct_field ng_netflow_info_type_fields[] 71 = NG_NETFLOW_INFO_TYPE; 72 static const struct ng_parse_type ng_netflow_info_type = { 73 &ng_parse_struct_type, 74 &ng_netflow_info_type_fields 75 }; 76 77 /* Parse type for struct ng_netflow_ifinfo */ 78 static const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[] 79 = NG_NETFLOW_IFINFO_TYPE; 80 static const struct ng_parse_type ng_netflow_ifinfo_type = { 81 &ng_parse_struct_type, 82 &ng_netflow_ifinfo_type_fields 83 }; 84 85 /* Parse type for struct ng_netflow_setdlt */ 86 static const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[] 87 = NG_NETFLOW_SETDLT_TYPE; 88 static const struct ng_parse_type ng_netflow_setdlt_type = { 89 &ng_parse_struct_type, 90 &ng_netflow_setdlt_type_fields 91 }; 92 93 /* Parse type for ng_netflow_setifindex */ 94 static const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[] 95 = NG_NETFLOW_SETIFINDEX_TYPE; 96 static const struct ng_parse_type ng_netflow_setifindex_type = { 97 &ng_parse_struct_type, 98 &ng_netflow_setifindex_type_fields 99 }; 100 101 /* Parse type for ng_netflow_settimeouts */ 102 static const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[] 103 = NG_NETFLOW_SETTIMEOUTS_TYPE; 104 static const struct ng_parse_type ng_netflow_settimeouts_type = { 105 &ng_parse_struct_type, 106 &ng_netflow_settimeouts_type_fields 107 }; 108 109 /* Parse type for ng_netflow_setconfig */ 110 static const struct ng_parse_struct_field ng_netflow_setconfig_type_fields[] 111 = NG_NETFLOW_SETCONFIG_TYPE; 112 static const struct ng_parse_type ng_netflow_setconfig_type = { 113 &ng_parse_struct_type, 114 &ng_netflow_setconfig_type_fields 115 }; 116 117 /* List of commands and how to convert arguments to/from ASCII */ 118 static const struct ng_cmdlist ng_netflow_cmds[] = { 119 { 120 NGM_NETFLOW_COOKIE, 121 NGM_NETFLOW_INFO, 122 "info", 123 NULL, 124 &ng_netflow_info_type 125 }, 126 { 127 NGM_NETFLOW_COOKIE, 128 NGM_NETFLOW_IFINFO, 129 "ifinfo", 130 &ng_parse_uint16_type, 131 &ng_netflow_ifinfo_type 132 }, 133 { 134 NGM_NETFLOW_COOKIE, 135 NGM_NETFLOW_SETDLT, 136 "setdlt", 137 &ng_netflow_setdlt_type, 138 NULL 139 }, 140 { 141 NGM_NETFLOW_COOKIE, 142 NGM_NETFLOW_SETIFINDEX, 143 "setifindex", 144 &ng_netflow_setifindex_type, 145 NULL 146 }, 147 { 148 NGM_NETFLOW_COOKIE, 149 NGM_NETFLOW_SETTIMEOUTS, 150 "settimeouts", 151 &ng_netflow_settimeouts_type, 152 NULL 153 }, 154 { 155 NGM_NETFLOW_COOKIE, 156 NGM_NETFLOW_SETCONFIG, 157 "setconfig", 158 &ng_netflow_setconfig_type, 159 NULL 160 }, 161 { 0 } 162 }; 163 164 165 /* Netgraph node type descriptor */ 166 static struct ng_type ng_netflow_typestruct = { 167 .version = NG_ABI_VERSION, 168 .name = NG_NETFLOW_NODE_TYPE, 169 .constructor = ng_netflow_constructor, 170 .rcvmsg = ng_netflow_rcvmsg, 171 .close = ng_netflow_close, 172 .shutdown = ng_netflow_rmnode, 173 .newhook = ng_netflow_newhook, 174 .rcvdata = ng_netflow_rcvdata, 175 .disconnect = ng_netflow_disconnect, 176 .cmdlist = ng_netflow_cmds, 177 }; 178 NETGRAPH_INIT(netflow, &ng_netflow_typestruct); 179 180 /* Called at node creation */ 181 static int 182 ng_netflow_constructor(node_p node) 183 { 184 priv_p priv; 185 int error = 0, i; 186 187 /* Initialize private data */ 188 priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT); 189 if (priv == NULL) 190 return (ENOMEM); 191 bzero(priv, sizeof(*priv)); 192 193 /* Make node and its data point at each other */ 194 NG_NODE_SET_PRIVATE(node, priv); 195 priv->node = node; 196 197 /* Initialize timeouts to default values */ 198 priv->info.nfinfo_inact_t = INACTIVE_TIMEOUT; 199 priv->info.nfinfo_act_t = ACTIVE_TIMEOUT; 200 201 /* Set default config */ 202 for (i = 0; i < NG_NETFLOW_MAXIFACES; i++) 203 priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS; 204 205 /* Initialize callout handle */ 206 callout_init(&priv->exp_callout, CALLOUT_MPSAFE); 207 208 /* Allocate memory and set up flow cache */ 209 if ((error = ng_netflow_cache_init(priv))) 210 return (error); 211 212 return (0); 213 } 214 215 /* 216 * ng_netflow supports two hooks: data and export. 217 * Incoming traffic is expected on data, and expired 218 * netflow datagrams are sent to export. 219 */ 220 static int 221 ng_netflow_newhook(node_p node, hook_p hook, const char *name) 222 { 223 const priv_p priv = NG_NODE_PRIVATE(node); 224 225 if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */ 226 strlen(NG_NETFLOW_HOOK_DATA)) == 0) { 227 iface_p iface; 228 int ifnum = -1; 229 const char *cp; 230 char *eptr; 231 232 cp = name + strlen(NG_NETFLOW_HOOK_DATA); 233 if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 234 return (EINVAL); 235 236 ifnum = (int)strtoul(cp, &eptr, 10); 237 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES) 238 return (EINVAL); 239 240 /* See if hook is already connected */ 241 if (priv->ifaces[ifnum].hook != NULL) 242 return (EISCONN); 243 244 iface = &priv->ifaces[ifnum]; 245 246 /* Link private info and hook together */ 247 NG_HOOK_SET_PRIVATE(hook, iface); 248 iface->hook = hook; 249 250 /* 251 * In most cases traffic accounting is done on an 252 * Ethernet interface, so default data link type 253 * will be DLT_EN10MB. 254 */ 255 iface->info.ifinfo_dlt = DLT_EN10MB; 256 257 } else if (strncmp(name, NG_NETFLOW_HOOK_OUT, 258 strlen(NG_NETFLOW_HOOK_OUT)) == 0) { 259 iface_p iface; 260 int ifnum = -1; 261 const char *cp; 262 char *eptr; 263 264 cp = name + strlen(NG_NETFLOW_HOOK_OUT); 265 if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 266 return (EINVAL); 267 268 ifnum = (int)strtoul(cp, &eptr, 10); 269 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES) 270 return (EINVAL); 271 272 /* See if hook is already connected */ 273 if (priv->ifaces[ifnum].out != NULL) 274 return (EISCONN); 275 276 iface = &priv->ifaces[ifnum]; 277 278 /* Link private info and hook together */ 279 NG_HOOK_SET_PRIVATE(hook, iface); 280 iface->out = hook; 281 282 } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) { 283 284 if (priv->export != NULL) 285 return (EISCONN); 286 287 priv->export = hook; 288 289 #if 0 /* TODO: profile & test first */ 290 /* 291 * We send export dgrams in interrupt handlers and in 292 * callout threads. We'd better queue data for later 293 * netgraph ISR processing. 294 */ 295 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 296 #endif 297 298 /* Exporter is ready. Let's schedule expiry. */ 299 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire, 300 (void *)priv); 301 } else 302 return (EINVAL); 303 304 return (0); 305 } 306 307 /* Get a netgraph control message. */ 308 static int 309 ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook) 310 { 311 const priv_p priv = NG_NODE_PRIVATE(node); 312 struct ng_mesg *resp = NULL; 313 int error = 0; 314 struct ng_mesg *msg; 315 316 NGI_GET_MSG(item, msg); 317 318 /* Deal with message according to cookie and command */ 319 switch (msg->header.typecookie) { 320 case NGM_NETFLOW_COOKIE: 321 switch (msg->header.cmd) { 322 case NGM_NETFLOW_INFO: 323 { 324 struct ng_netflow_info *i; 325 326 NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info), 327 M_NOWAIT); 328 i = (struct ng_netflow_info *)resp->data; 329 ng_netflow_copyinfo(priv, i); 330 331 break; 332 } 333 case NGM_NETFLOW_IFINFO: 334 { 335 struct ng_netflow_ifinfo *i; 336 const uint16_t *index; 337 338 if (msg->header.arglen != sizeof(uint16_t)) 339 ERROUT(EINVAL); 340 341 index = (uint16_t *)msg->data; 342 if (*index >= NG_NETFLOW_MAXIFACES) 343 ERROUT(EINVAL); 344 345 /* connected iface? */ 346 if (priv->ifaces[*index].hook == NULL) 347 ERROUT(EINVAL); 348 349 NG_MKRESPONSE(resp, msg, 350 sizeof(struct ng_netflow_ifinfo), M_NOWAIT); 351 i = (struct ng_netflow_ifinfo *)resp->data; 352 memcpy((void *)i, (void *)&priv->ifaces[*index].info, 353 sizeof(priv->ifaces[*index].info)); 354 355 break; 356 } 357 case NGM_NETFLOW_SETDLT: 358 { 359 struct ng_netflow_setdlt *set; 360 struct ng_netflow_iface *iface; 361 362 if (msg->header.arglen != sizeof(struct ng_netflow_setdlt)) 363 ERROUT(EINVAL); 364 365 set = (struct ng_netflow_setdlt *)msg->data; 366 if (set->iface >= NG_NETFLOW_MAXIFACES) 367 ERROUT(EINVAL); 368 iface = &priv->ifaces[set->iface]; 369 370 /* connected iface? */ 371 if (iface->hook == NULL) 372 ERROUT(EINVAL); 373 374 switch (set->dlt) { 375 case DLT_EN10MB: 376 iface->info.ifinfo_dlt = DLT_EN10MB; 377 break; 378 case DLT_RAW: 379 iface->info.ifinfo_dlt = DLT_RAW; 380 break; 381 default: 382 ERROUT(EINVAL); 383 } 384 break; 385 } 386 case NGM_NETFLOW_SETIFINDEX: 387 { 388 struct ng_netflow_setifindex *set; 389 struct ng_netflow_iface *iface; 390 391 if (msg->header.arglen != sizeof(struct ng_netflow_setifindex)) 392 ERROUT(EINVAL); 393 394 set = (struct ng_netflow_setifindex *)msg->data; 395 if (set->iface >= NG_NETFLOW_MAXIFACES) 396 ERROUT(EINVAL); 397 iface = &priv->ifaces[set->iface]; 398 399 /* connected iface? */ 400 if (iface->hook == NULL) 401 ERROUT(EINVAL); 402 403 iface->info.ifinfo_index = set->index; 404 405 break; 406 } 407 case NGM_NETFLOW_SETTIMEOUTS: 408 { 409 struct ng_netflow_settimeouts *set; 410 411 if (msg->header.arglen != sizeof(struct ng_netflow_settimeouts)) 412 ERROUT(EINVAL); 413 414 set = (struct ng_netflow_settimeouts *)msg->data; 415 416 priv->info.nfinfo_inact_t = set->inactive_timeout; 417 priv->info.nfinfo_act_t = set->active_timeout; 418 419 break; 420 } 421 case NGM_NETFLOW_SETCONFIG: 422 { 423 struct ng_netflow_setconfig *set; 424 425 if (msg->header.arglen != sizeof(struct ng_netflow_settimeouts)) 426 ERROUT(EINVAL); 427 428 set = (struct ng_netflow_setconfig *)msg->data; 429 430 if (set->iface >= NG_NETFLOW_MAXIFACES) 431 ERROUT(EINVAL); 432 433 priv->ifaces[set->iface].info.conf = set->conf; 434 435 break; 436 } 437 case NGM_NETFLOW_SHOW: 438 { 439 uint32_t *last; 440 441 if (msg->header.arglen != sizeof(uint32_t)) 442 ERROUT(EINVAL); 443 444 last = (uint32_t *)msg->data; 445 446 NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT); 447 448 if (!resp) 449 ERROUT(ENOMEM); 450 451 error = ng_netflow_flow_show(priv, *last, resp); 452 453 break; 454 } 455 default: 456 ERROUT(EINVAL); /* unknown command */ 457 break; 458 } 459 break; 460 default: 461 ERROUT(EINVAL); /* incorrect cookie */ 462 break; 463 } 464 465 /* 466 * Take care of synchronous response, if any. 467 * Free memory and return. 468 */ 469 done: 470 NG_RESPOND_MSG(error, node, item, resp); 471 NG_FREE_MSG(msg); 472 473 return (error); 474 } 475 476 /* Receive data on hook. */ 477 static int 478 ng_netflow_rcvdata (hook_p hook, item_p item) 479 { 480 const node_p node = NG_HOOK_NODE(hook); 481 const priv_p priv = NG_NODE_PRIVATE(node); 482 const iface_p iface = NG_HOOK_PRIVATE(hook); 483 hook_p out; 484 struct mbuf *m = NULL; 485 struct ip *ip; 486 struct m_tag *mtag; 487 int pullup_len = 0; 488 int error = 0, bypass = 0; 489 unsigned int src_if_index; 490 491 if (hook == priv->export) { 492 /* 493 * Data arrived on export hook. 494 * This must not happen. 495 */ 496 log(LOG_ERR, "ng_netflow: incoming data on export hook!\n"); 497 ERROUT(EINVAL); 498 }; 499 500 if (hook == iface->hook) { 501 if ((iface->info.conf & NG_NETFLOW_CONF_INGRESS) == 0) 502 bypass = 1; 503 out = iface->out; 504 } else if (hook == iface->out) { 505 if ((iface->info.conf & NG_NETFLOW_CONF_EGRESS) == 0) 506 bypass = 1; 507 out = iface->hook; 508 } else 509 ERROUT(EINVAL); 510 511 if ((!bypass) && 512 (iface->info.conf & (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE))) { 513 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW, 514 MTAG_NETFLOW_CALLED, NULL); 515 while (mtag != NULL) { 516 if ((iface->info.conf & NG_NETFLOW_CONF_ONCE) || 517 ((ng_ID_t *)(mtag + 1))[0] == NG_NODE_ID(node)) { 518 bypass = 1; 519 break; 520 } 521 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW, 522 MTAG_NETFLOW_CALLED, mtag); 523 } 524 } 525 526 if (bypass) { 527 if (out == NULL) 528 ERROUT(ENOTCONN); 529 530 NG_FWD_ITEM_HOOK(error, item, out); 531 return (error); 532 } 533 534 if (iface->info.conf & (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE)) { 535 mtag = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_CALLED, 536 sizeof(ng_ID_t), M_NOWAIT); 537 if (mtag) { 538 ((ng_ID_t *)(mtag + 1))[0] = NG_NODE_ID(node); 539 m_tag_prepend(NGI_M(item), mtag); 540 } 541 } 542 543 NGI_GET_M(item, m); 544 545 /* Increase counters. */ 546 iface->info.ifinfo_packets++; 547 548 /* 549 * Depending on interface data link type and packet contents 550 * we pullup enough data, so that ng_netflow_flow_add() does not 551 * need to know about mbuf at all. We keep current length of data 552 * needed to be contiguous in pullup_len. mtod() is done at the 553 * very end one more time, since m can had changed after pulluping. 554 * 555 * In case of unrecognized data we don't return error, but just 556 * pass data to downstream hook, if it is available. 557 */ 558 559 #define M_CHECK(length) do { \ 560 pullup_len += length; \ 561 if ((m)->m_pkthdr.len < (pullup_len)) { \ 562 error = EINVAL; \ 563 goto bypass; \ 564 } \ 565 if ((m)->m_len < (pullup_len) && \ 566 (((m) = m_pullup((m),(pullup_len))) == NULL)) { \ 567 error = ENOBUFS; \ 568 goto done; \ 569 } \ 570 } while (0) 571 572 switch (iface->info.ifinfo_dlt) { 573 case DLT_EN10MB: /* Ethernet */ 574 { 575 struct ether_header *eh; 576 uint16_t etype; 577 578 M_CHECK(sizeof(struct ether_header)); 579 eh = mtod(m, struct ether_header *); 580 581 /* Make sure this is IP frame. */ 582 etype = ntohs(eh->ether_type); 583 switch (etype) { 584 case ETHERTYPE_IP: 585 M_CHECK(sizeof(struct ip)); 586 eh = mtod(m, struct ether_header *); 587 ip = (struct ip *)(eh + 1); 588 break; 589 case ETHERTYPE_VLAN: 590 { 591 struct ether_vlan_header *evh; 592 593 M_CHECK(sizeof(struct ether_vlan_header) - 594 sizeof(struct ether_header)); 595 evh = mtod(m, struct ether_vlan_header *); 596 if (ntohs(evh->evl_proto) == ETHERTYPE_IP) { 597 M_CHECK(sizeof(struct ip)); 598 ip = (struct ip *)(evh + 1); 599 break; 600 } 601 } 602 default: 603 goto bypass; /* pass this frame */ 604 } 605 break; 606 } 607 case DLT_RAW: /* IP packets */ 608 M_CHECK(sizeof(struct ip)); 609 ip = mtod(m, struct ip *); 610 break; 611 default: 612 goto bypass; 613 break; 614 } 615 616 if ((ip->ip_off & htons(IP_OFFMASK)) == 0) { 617 /* 618 * In case of IP header with options, we haven't pulled 619 * up enough, yet. 620 */ 621 pullup_len += (ip->ip_hl << 2) - sizeof(struct ip); 622 623 switch (ip->ip_p) { 624 case IPPROTO_TCP: 625 M_CHECK(sizeof(struct tcphdr)); 626 break; 627 case IPPROTO_UDP: 628 M_CHECK(sizeof(struct udphdr)); 629 break; 630 } 631 } 632 633 switch (iface->info.ifinfo_dlt) { 634 case DLT_EN10MB: 635 { 636 struct ether_header *eh; 637 638 eh = mtod(m, struct ether_header *); 639 switch (ntohs(eh->ether_type)) { 640 case ETHERTYPE_IP: 641 ip = (struct ip *)(eh + 1); 642 break; 643 case ETHERTYPE_VLAN: 644 { 645 struct ether_vlan_header *evh; 646 647 evh = mtod(m, struct ether_vlan_header *); 648 ip = (struct ip *)(evh + 1); 649 break; 650 } 651 default: 652 panic("ng_netflow entered deadcode"); 653 } 654 break; 655 } 656 case DLT_RAW: 657 ip = mtod(m, struct ip *); 658 break; 659 default: 660 panic("ng_netflow entered deadcode"); 661 } 662 663 #undef M_CHECK 664 665 /* Determine packet input interface. Prefer configured. */ 666 src_if_index = 0; 667 if (hook == iface->out || iface->info.ifinfo_index == 0) { 668 if (m->m_pkthdr.rcvif != NULL) 669 src_if_index = m->m_pkthdr.rcvif->if_index; 670 } else 671 src_if_index = iface->info.ifinfo_index; 672 673 error = ng_netflow_flow_add(priv, ip, src_if_index); 674 675 bypass: 676 if (out != NULL) { 677 /* XXX: error gets overwritten here */ 678 NG_FWD_NEW_DATA(error, item, out, m); 679 return (error); 680 } 681 done: 682 if (item) 683 NG_FREE_ITEM(item); 684 if (m) 685 NG_FREE_M(m); 686 687 return (error); 688 } 689 690 /* We will be shut down in a moment */ 691 static int 692 ng_netflow_close(node_p node) 693 { 694 const priv_p priv = NG_NODE_PRIVATE(node); 695 696 callout_drain(&priv->exp_callout); 697 ng_netflow_cache_flush(priv); 698 699 return (0); 700 } 701 702 /* Do local shutdown processing. */ 703 static int 704 ng_netflow_rmnode(node_p node) 705 { 706 const priv_p priv = NG_NODE_PRIVATE(node); 707 708 NG_NODE_SET_PRIVATE(node, NULL); 709 NG_NODE_UNREF(priv->node); 710 711 free(priv, M_NETGRAPH); 712 713 return (0); 714 } 715 716 /* Hook disconnection. */ 717 static int 718 ng_netflow_disconnect(hook_p hook) 719 { 720 node_p node = NG_HOOK_NODE(hook); 721 priv_p priv = NG_NODE_PRIVATE(node); 722 iface_p iface = NG_HOOK_PRIVATE(hook); 723 724 if (iface != NULL) { 725 if (iface->hook == hook) 726 iface->hook = NULL; 727 if (iface->out == hook) 728 iface->out = NULL; 729 } 730 731 /* if export hook disconnected stop running expire(). */ 732 if (hook == priv->export) { 733 callout_drain(&priv->exp_callout); 734 priv->export = NULL; 735 } 736 737 /* Removal of the last link destroys the node. */ 738 if (NG_NODE_NUMHOOKS(node) == 0) 739 ng_rmnode_self(node); 740 741 return (0); 742 } 743