1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2010-2011 Alexander V. Chernikov <melifaro@ipfw.ru> 5 * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org> 6 * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $SourceForge: ng_netflow.c,v 1.30 2004/09/05 11:37:43 glebius Exp $ 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include "opt_inet.h" 37 #include "opt_inet6.h" 38 #include "opt_route.h" 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/counter.h> 43 #include <sys/kernel.h> 44 #include <sys/ktr.h> 45 #include <sys/limits.h> 46 #include <sys/malloc.h> 47 #include <sys/mbuf.h> 48 #include <sys/socket.h> 49 #include <sys/syslog.h> 50 #include <sys/ctype.h> 51 #include <vm/uma.h> 52 53 #include <net/if.h> 54 #include <net/ethernet.h> 55 #include <net/route.h> 56 #include <net/if_arp.h> 57 #include <net/if_var.h> 58 #include <net/if_private.h> 59 #include <net/if_vlan_var.h> 60 #include <net/bpf.h> 61 #include <netinet/in.h> 62 #include <netinet/in_systm.h> 63 #include <netinet/ip.h> 64 #include <netinet/ip6.h> 65 #include <netinet/tcp.h> 66 #include <netinet/udp.h> 67 #include <netinet/sctp.h> 68 69 #include <netgraph/ng_message.h> 70 #include <netgraph/ng_parse.h> 71 #include <netgraph/netgraph.h> 72 #include <netgraph/netflow/netflow.h> 73 #include <netgraph/netflow/netflow_v9.h> 74 #include <netgraph/netflow/ng_netflow.h> 75 76 /* Netgraph methods */ 77 static ng_constructor_t ng_netflow_constructor; 78 static ng_rcvmsg_t ng_netflow_rcvmsg; 79 static ng_close_t ng_netflow_close; 80 static ng_shutdown_t ng_netflow_rmnode; 81 static ng_newhook_t ng_netflow_newhook; 82 static ng_rcvdata_t ng_netflow_rcvdata; 83 static ng_disconnect_t ng_netflow_disconnect; 84 85 /* Parse type for struct ng_netflow_info */ 86 static const struct ng_parse_struct_field ng_netflow_info_type_fields[] 87 = NG_NETFLOW_INFO_TYPE; 88 static const struct ng_parse_type ng_netflow_info_type = { 89 &ng_parse_struct_type, 90 &ng_netflow_info_type_fields 91 }; 92 93 /* Parse type for struct ng_netflow_ifinfo */ 94 static const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[] 95 = NG_NETFLOW_IFINFO_TYPE; 96 static const struct ng_parse_type ng_netflow_ifinfo_type = { 97 &ng_parse_struct_type, 98 &ng_netflow_ifinfo_type_fields 99 }; 100 101 /* Parse type for struct ng_netflow_setdlt */ 102 static const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[] 103 = NG_NETFLOW_SETDLT_TYPE; 104 static const struct ng_parse_type ng_netflow_setdlt_type = { 105 &ng_parse_struct_type, 106 &ng_netflow_setdlt_type_fields 107 }; 108 109 /* Parse type for ng_netflow_setifindex */ 110 static const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[] 111 = NG_NETFLOW_SETIFINDEX_TYPE; 112 static const struct ng_parse_type ng_netflow_setifindex_type = { 113 &ng_parse_struct_type, 114 &ng_netflow_setifindex_type_fields 115 }; 116 117 /* Parse type for ng_netflow_settimeouts */ 118 static const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[] 119 = NG_NETFLOW_SETTIMEOUTS_TYPE; 120 static const struct ng_parse_type ng_netflow_settimeouts_type = { 121 &ng_parse_struct_type, 122 &ng_netflow_settimeouts_type_fields 123 }; 124 125 /* Parse type for ng_netflow_setconfig */ 126 static const struct ng_parse_struct_field ng_netflow_setconfig_type_fields[] 127 = NG_NETFLOW_SETCONFIG_TYPE; 128 static const struct ng_parse_type ng_netflow_setconfig_type = { 129 &ng_parse_struct_type, 130 &ng_netflow_setconfig_type_fields 131 }; 132 133 /* Parse type for ng_netflow_settemplate */ 134 static const struct ng_parse_struct_field ng_netflow_settemplate_type_fields[] 135 = NG_NETFLOW_SETTEMPLATE_TYPE; 136 static const struct ng_parse_type ng_netflow_settemplate_type = { 137 &ng_parse_struct_type, 138 &ng_netflow_settemplate_type_fields 139 }; 140 141 /* Parse type for ng_netflow_setmtu */ 142 static const struct ng_parse_struct_field ng_netflow_setmtu_type_fields[] 143 = NG_NETFLOW_SETMTU_TYPE; 144 static const struct ng_parse_type ng_netflow_setmtu_type = { 145 &ng_parse_struct_type, 146 &ng_netflow_setmtu_type_fields 147 }; 148 149 /* Parse type for struct ng_netflow_v9info */ 150 static const struct ng_parse_struct_field ng_netflow_v9info_type_fields[] 151 = NG_NETFLOW_V9INFO_TYPE; 152 static const struct ng_parse_type ng_netflow_v9info_type = { 153 &ng_parse_struct_type, 154 &ng_netflow_v9info_type_fields 155 }; 156 157 /* List of commands and how to convert arguments to/from ASCII */ 158 static const struct ng_cmdlist ng_netflow_cmds[] = { 159 { 160 NGM_NETFLOW_COOKIE, 161 NGM_NETFLOW_INFO, 162 "info", 163 NULL, 164 &ng_netflow_info_type 165 }, 166 { 167 NGM_NETFLOW_COOKIE, 168 NGM_NETFLOW_IFINFO, 169 "ifinfo", 170 &ng_parse_uint16_type, 171 &ng_netflow_ifinfo_type 172 }, 173 { 174 NGM_NETFLOW_COOKIE, 175 NGM_NETFLOW_SETDLT, 176 "setdlt", 177 &ng_netflow_setdlt_type, 178 NULL 179 }, 180 { 181 NGM_NETFLOW_COOKIE, 182 NGM_NETFLOW_SETIFINDEX, 183 "setifindex", 184 &ng_netflow_setifindex_type, 185 NULL 186 }, 187 { 188 NGM_NETFLOW_COOKIE, 189 NGM_NETFLOW_SETTIMEOUTS, 190 "settimeouts", 191 &ng_netflow_settimeouts_type, 192 NULL 193 }, 194 { 195 NGM_NETFLOW_COOKIE, 196 NGM_NETFLOW_SETCONFIG, 197 "setconfig", 198 &ng_netflow_setconfig_type, 199 NULL 200 }, 201 { 202 NGM_NETFLOW_COOKIE, 203 NGM_NETFLOW_SETTEMPLATE, 204 "settemplate", 205 &ng_netflow_settemplate_type, 206 NULL 207 }, 208 { 209 NGM_NETFLOW_COOKIE, 210 NGM_NETFLOW_SETMTU, 211 "setmtu", 212 &ng_netflow_setmtu_type, 213 NULL 214 }, 215 { 216 NGM_NETFLOW_COOKIE, 217 NGM_NETFLOW_V9INFO, 218 "v9info", 219 NULL, 220 &ng_netflow_v9info_type 221 }, 222 { 0 } 223 }; 224 225 /* Netgraph node type descriptor */ 226 static struct ng_type ng_netflow_typestruct = { 227 .version = NG_ABI_VERSION, 228 .name = NG_NETFLOW_NODE_TYPE, 229 .constructor = ng_netflow_constructor, 230 .rcvmsg = ng_netflow_rcvmsg, 231 .close = ng_netflow_close, 232 .shutdown = ng_netflow_rmnode, 233 .newhook = ng_netflow_newhook, 234 .rcvdata = ng_netflow_rcvdata, 235 .disconnect = ng_netflow_disconnect, 236 .cmdlist = ng_netflow_cmds, 237 }; 238 NETGRAPH_INIT(netflow, &ng_netflow_typestruct); 239 240 /* Called at node creation */ 241 static int 242 ng_netflow_constructor(node_p node) 243 { 244 priv_p priv; 245 int i; 246 247 /* Initialize private data */ 248 priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 249 250 /* Initialize fib data */ 251 priv->maxfibs = rt_numfibs; 252 priv->fib_data = malloc(sizeof(fib_export_p) * priv->maxfibs, 253 M_NETGRAPH, M_WAITOK | M_ZERO); 254 255 /* Make node and its data point at each other */ 256 NG_NODE_SET_PRIVATE(node, priv); 257 priv->node = node; 258 259 /* Initialize timeouts to default values */ 260 priv->nfinfo_inact_t = INACTIVE_TIMEOUT; 261 priv->nfinfo_act_t = ACTIVE_TIMEOUT; 262 263 /* Set default config */ 264 for (i = 0; i < NG_NETFLOW_MAXIFACES; i++) 265 priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS; 266 267 /* Initialize callout handle */ 268 callout_init(&priv->exp_callout, 1); 269 270 /* Allocate memory and set up flow cache */ 271 ng_netflow_cache_init(priv); 272 273 return (0); 274 } 275 276 /* 277 * ng_netflow supports two hooks: data and export. 278 * Incoming traffic is expected on data, and expired 279 * netflow datagrams are sent to export. 280 */ 281 static int 282 ng_netflow_newhook(node_p node, hook_p hook, const char *name) 283 { 284 const priv_p priv = NG_NODE_PRIVATE(node); 285 286 if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */ 287 strlen(NG_NETFLOW_HOOK_DATA)) == 0) { 288 iface_p iface; 289 int ifnum = -1; 290 const char *cp; 291 char *eptr; 292 293 cp = name + strlen(NG_NETFLOW_HOOK_DATA); 294 if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 295 return (EINVAL); 296 297 ifnum = (int)strtoul(cp, &eptr, 10); 298 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES) 299 return (EINVAL); 300 301 /* See if hook is already connected */ 302 if (priv->ifaces[ifnum].hook != NULL) 303 return (EISCONN); 304 305 iface = &priv->ifaces[ifnum]; 306 307 /* Link private info and hook together */ 308 NG_HOOK_SET_PRIVATE(hook, iface); 309 iface->hook = hook; 310 311 /* 312 * In most cases traffic accounting is done on an 313 * Ethernet interface, so default data link type 314 * will be DLT_EN10MB. 315 */ 316 iface->info.ifinfo_dlt = DLT_EN10MB; 317 318 } else if (strncmp(name, NG_NETFLOW_HOOK_OUT, 319 strlen(NG_NETFLOW_HOOK_OUT)) == 0) { 320 iface_p iface; 321 int ifnum = -1; 322 const char *cp; 323 char *eptr; 324 325 cp = name + strlen(NG_NETFLOW_HOOK_OUT); 326 if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 327 return (EINVAL); 328 329 ifnum = (int)strtoul(cp, &eptr, 10); 330 if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES) 331 return (EINVAL); 332 333 /* See if hook is already connected */ 334 if (priv->ifaces[ifnum].out != NULL) 335 return (EISCONN); 336 337 iface = &priv->ifaces[ifnum]; 338 339 /* Link private info and hook together */ 340 NG_HOOK_SET_PRIVATE(hook, iface); 341 iface->out = hook; 342 343 } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) { 344 if (priv->export != NULL) 345 return (EISCONN); 346 347 /* Netflow version 5 supports 32-bit counters only */ 348 if (CNTR_MAX == UINT64_MAX) 349 return (EINVAL); 350 351 priv->export = hook; 352 353 /* Exporter is ready. Let's schedule expiry. */ 354 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire, 355 (void *)priv); 356 } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT9) == 0) { 357 if (priv->export9 != NULL) 358 return (EISCONN); 359 360 priv->export9 = hook; 361 362 /* Exporter is ready. Let's schedule expiry. */ 363 callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire, 364 (void *)priv); 365 } else 366 return (EINVAL); 367 368 return (0); 369 } 370 371 /* Get a netgraph control message. */ 372 static int 373 ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook) 374 { 375 const priv_p priv = NG_NODE_PRIVATE(node); 376 struct ng_mesg *resp = NULL; 377 int error = 0; 378 struct ng_mesg *msg; 379 380 NGI_GET_MSG(item, msg); 381 382 /* Deal with message according to cookie and command */ 383 switch (msg->header.typecookie) { 384 case NGM_NETFLOW_COOKIE: 385 switch (msg->header.cmd) { 386 case NGM_NETFLOW_INFO: 387 { 388 struct ng_netflow_info *i; 389 390 NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info), 391 M_NOWAIT); 392 i = (struct ng_netflow_info *)resp->data; 393 ng_netflow_copyinfo(priv, i); 394 395 break; 396 } 397 case NGM_NETFLOW_IFINFO: 398 { 399 struct ng_netflow_ifinfo *i; 400 const uint16_t *index; 401 402 if (msg->header.arglen != sizeof(uint16_t)) 403 ERROUT(EINVAL); 404 405 index = (uint16_t *)msg->data; 406 if (*index >= NG_NETFLOW_MAXIFACES) 407 ERROUT(EINVAL); 408 409 /* connected iface? */ 410 if (priv->ifaces[*index].hook == NULL) 411 ERROUT(EINVAL); 412 413 NG_MKRESPONSE(resp, msg, 414 sizeof(struct ng_netflow_ifinfo), M_NOWAIT); 415 i = (struct ng_netflow_ifinfo *)resp->data; 416 memcpy((void *)i, (void *)&priv->ifaces[*index].info, 417 sizeof(priv->ifaces[*index].info)); 418 419 break; 420 } 421 case NGM_NETFLOW_SETDLT: 422 { 423 struct ng_netflow_setdlt *set; 424 struct ng_netflow_iface *iface; 425 426 if (msg->header.arglen != 427 sizeof(struct ng_netflow_setdlt)) 428 ERROUT(EINVAL); 429 430 set = (struct ng_netflow_setdlt *)msg->data; 431 if (set->iface >= NG_NETFLOW_MAXIFACES) 432 ERROUT(EINVAL); 433 iface = &priv->ifaces[set->iface]; 434 435 /* connected iface? */ 436 if (iface->hook == NULL) 437 ERROUT(EINVAL); 438 439 switch (set->dlt) { 440 case DLT_EN10MB: 441 iface->info.ifinfo_dlt = DLT_EN10MB; 442 break; 443 case DLT_RAW: 444 iface->info.ifinfo_dlt = DLT_RAW; 445 break; 446 default: 447 ERROUT(EINVAL); 448 } 449 break; 450 } 451 case NGM_NETFLOW_SETIFINDEX: 452 { 453 struct ng_netflow_setifindex *set; 454 struct ng_netflow_iface *iface; 455 456 if (msg->header.arglen != 457 sizeof(struct ng_netflow_setifindex)) 458 ERROUT(EINVAL); 459 460 set = (struct ng_netflow_setifindex *)msg->data; 461 if (set->iface >= NG_NETFLOW_MAXIFACES) 462 ERROUT(EINVAL); 463 iface = &priv->ifaces[set->iface]; 464 465 /* connected iface? */ 466 if (iface->hook == NULL) 467 ERROUT(EINVAL); 468 469 iface->info.ifinfo_index = set->index; 470 471 break; 472 } 473 case NGM_NETFLOW_SETTIMEOUTS: 474 { 475 struct ng_netflow_settimeouts *set; 476 477 if (msg->header.arglen != 478 sizeof(struct ng_netflow_settimeouts)) 479 ERROUT(EINVAL); 480 481 set = (struct ng_netflow_settimeouts *)msg->data; 482 483 priv->nfinfo_inact_t = set->inactive_timeout; 484 priv->nfinfo_act_t = set->active_timeout; 485 486 break; 487 } 488 case NGM_NETFLOW_SETCONFIG: 489 { 490 struct ng_netflow_setconfig *set; 491 492 if (msg->header.arglen != 493 sizeof(struct ng_netflow_setconfig)) 494 ERROUT(EINVAL); 495 496 set = (struct ng_netflow_setconfig *)msg->data; 497 498 if (set->iface >= NG_NETFLOW_MAXIFACES) 499 ERROUT(EINVAL); 500 501 priv->ifaces[set->iface].info.conf = set->conf; 502 503 break; 504 } 505 case NGM_NETFLOW_SETTEMPLATE: 506 { 507 struct ng_netflow_settemplate *set; 508 509 if (msg->header.arglen != 510 sizeof(struct ng_netflow_settemplate)) 511 ERROUT(EINVAL); 512 513 set = (struct ng_netflow_settemplate *)msg->data; 514 515 priv->templ_packets = set->packets; 516 priv->templ_time = set->time; 517 518 break; 519 } 520 case NGM_NETFLOW_SETMTU: 521 { 522 struct ng_netflow_setmtu *set; 523 524 if (msg->header.arglen != 525 sizeof(struct ng_netflow_setmtu)) 526 ERROUT(EINVAL); 527 528 set = (struct ng_netflow_setmtu *)msg->data; 529 if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU)) 530 ERROUT(EINVAL); 531 532 priv->mtu = set->mtu; 533 534 break; 535 } 536 case NGM_NETFLOW_SHOW: 537 if (msg->header.arglen != 538 sizeof(struct ngnf_show_header)) 539 ERROUT(EINVAL); 540 541 NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT); 542 543 if (!resp) 544 ERROUT(ENOMEM); 545 546 error = ng_netflow_flow_show(priv, 547 (struct ngnf_show_header *)msg->data, 548 (struct ngnf_show_header *)resp->data); 549 550 if (error) 551 NG_FREE_MSG(resp); 552 553 break; 554 case NGM_NETFLOW_V9INFO: 555 { 556 struct ng_netflow_v9info *i; 557 558 NG_MKRESPONSE(resp, msg, 559 sizeof(struct ng_netflow_v9info), M_NOWAIT); 560 i = (struct ng_netflow_v9info *)resp->data; 561 ng_netflow_copyv9info(priv, i); 562 563 break; 564 } 565 default: 566 ERROUT(EINVAL); /* unknown command */ 567 break; 568 } 569 break; 570 default: 571 ERROUT(EINVAL); /* incorrect cookie */ 572 break; 573 } 574 575 /* 576 * Take care of synchronous response, if any. 577 * Free memory and return. 578 */ 579 done: 580 NG_RESPOND_MSG(error, node, item, resp); 581 NG_FREE_MSG(msg); 582 583 return (error); 584 } 585 586 /* Receive data on hook. */ 587 static int 588 ng_netflow_rcvdata (hook_p hook, item_p item) 589 { 590 const node_p node = NG_HOOK_NODE(hook); 591 const priv_p priv = NG_NODE_PRIVATE(node); 592 const iface_p iface = NG_HOOK_PRIVATE(hook); 593 hook_p out; 594 struct mbuf *m = NULL, *m_old = NULL; 595 struct ip *ip = NULL; 596 struct ip6_hdr *ip6 = NULL; 597 struct m_tag *mtag; 598 int pullup_len = 0; 599 uint8_t acct = 0, bypass = 0; 600 int error = 0, l3_off = 0; 601 #if defined(INET) || defined(INET6) 602 int off; 603 uint8_t flags = 0, upper_proto = 0; 604 unsigned int src_if_index; 605 caddr_t upper_ptr = NULL; 606 #endif 607 fib_export_p fe; 608 uint32_t fib; 609 610 if ((hook == priv->export) || (hook == priv->export9)) { 611 /* 612 * Data arrived on export hook. 613 * This must not happen. 614 */ 615 log(LOG_ERR, "ng_netflow: incoming data on export hook!\n"); 616 ERROUT(EINVAL); 617 } 618 619 if (hook == iface->hook) { 620 if ((iface->info.conf & NG_NETFLOW_CONF_INGRESS) == 0) 621 bypass = 1; 622 out = iface->out; 623 } else if (hook == iface->out) { 624 if ((iface->info.conf & NG_NETFLOW_CONF_EGRESS) == 0) 625 bypass = 1; 626 out = iface->hook; 627 } else 628 ERROUT(EINVAL); 629 630 if ((!bypass) && (iface->info.conf & 631 (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE))) { 632 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW, 633 MTAG_NETFLOW_CALLED, NULL); 634 while (mtag != NULL) { 635 if ((iface->info.conf & NG_NETFLOW_CONF_ONCE) || 636 ((ng_ID_t *)(mtag + 1))[0] == NG_NODE_ID(node)) { 637 bypass = 1; 638 break; 639 } 640 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW, 641 MTAG_NETFLOW_CALLED, mtag); 642 } 643 } 644 645 if (bypass) { 646 if (out == NULL) 647 ERROUT(ENOTCONN); 648 649 NG_FWD_ITEM_HOOK(error, item, out); 650 return (error); 651 } 652 653 if (iface->info.conf & 654 (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE)) { 655 mtag = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_CALLED, 656 sizeof(ng_ID_t), M_NOWAIT); 657 if (mtag) { 658 ((ng_ID_t *)(mtag + 1))[0] = NG_NODE_ID(node); 659 m_tag_prepend(NGI_M(item), mtag); 660 } 661 } 662 663 #if defined(INET) || defined(INET6) 664 /* Import configuration flags related to flow creation */ 665 flags = iface->info.conf & NG_NETFLOW_FLOW_FLAGS; 666 #endif 667 668 NGI_GET_M(item, m); 669 m_old = m; 670 671 /* Increase counters. */ 672 iface->info.ifinfo_packets++; 673 674 /* 675 * Depending on interface data link type and packet contents 676 * we pullup enough data, so that ng_netflow_flow_add() does not 677 * need to know about mbuf at all. We keep current length of data 678 * needed to be contiguous in pullup_len. mtod() is done at the 679 * very end one more time, since m can had changed after pulluping. 680 * 681 * In case of unrecognized data we don't return error, but just 682 * pass data to downstream hook, if it is available. 683 */ 684 685 #define M_CHECK(length) do { \ 686 pullup_len += length; \ 687 if (((m)->m_pkthdr.len < (pullup_len)) || \ 688 ((pullup_len) > MHLEN)) { \ 689 error = EINVAL; \ 690 goto bypass; \ 691 } \ 692 if ((m)->m_len < (pullup_len) && \ 693 (((m) = m_pullup((m),(pullup_len))) == NULL)) { \ 694 error = ENOBUFS; \ 695 goto done; \ 696 } \ 697 } while (0) 698 699 switch (iface->info.ifinfo_dlt) { 700 case DLT_EN10MB: /* Ethernet */ 701 { 702 struct ether_header *eh; 703 uint16_t etype; 704 705 M_CHECK(sizeof(struct ether_header)); 706 eh = mtod(m, struct ether_header *); 707 708 /* Make sure this is IP frame. */ 709 etype = ntohs(eh->ether_type); 710 switch (etype) { 711 #ifdef INET 712 case ETHERTYPE_IP: 713 M_CHECK(sizeof(struct ip)); 714 eh = mtod(m, struct ether_header *); 715 ip = (struct ip *)(eh + 1); 716 l3_off = sizeof(struct ether_header); 717 break; 718 #endif 719 #ifdef INET6 720 case ETHERTYPE_IPV6: 721 /* 722 * m_pullup() called by M_CHECK() pullups 723 * kern.ipc.max_protohdr (default 60 bytes) 724 * which is enough. 725 */ 726 M_CHECK(sizeof(struct ip6_hdr)); 727 eh = mtod(m, struct ether_header *); 728 ip6 = (struct ip6_hdr *)(eh + 1); 729 l3_off = sizeof(struct ether_header); 730 break; 731 #endif 732 case ETHERTYPE_VLAN: 733 { 734 struct ether_vlan_header *evh; 735 736 M_CHECK(sizeof(struct ether_vlan_header) - 737 sizeof(struct ether_header)); 738 evh = mtod(m, struct ether_vlan_header *); 739 etype = ntohs(evh->evl_proto); 740 l3_off = sizeof(struct ether_vlan_header); 741 742 if (etype == ETHERTYPE_IP) { 743 #ifdef INET 744 M_CHECK(sizeof(struct ip)); 745 ip = (struct ip *)(evh + 1); 746 break; 747 #endif 748 #ifdef INET6 749 } else if (etype == ETHERTYPE_IPV6) { 750 M_CHECK(sizeof(struct ip6_hdr)); 751 ip6 = (struct ip6_hdr *)(evh + 1); 752 break; 753 #endif 754 } 755 } 756 default: 757 goto bypass; /* pass this frame */ 758 } 759 break; 760 } 761 case DLT_RAW: /* IP packets */ 762 M_CHECK(sizeof(struct ip)); 763 ip = mtod(m, struct ip *); 764 /* l3_off is already zero */ 765 #ifdef INET6 766 /* 767 * If INET6 is not defined IPv6 packets 768 * will be discarded in ng_netflow_flow_add(). 769 */ 770 if (ip->ip_v == IP6VERSION) { 771 ip = NULL; 772 M_CHECK(sizeof(struct ip6_hdr) - sizeof(struct ip)); 773 ip6 = mtod(m, struct ip6_hdr *); 774 } 775 #endif 776 #ifndef INET 777 ip = NULL; 778 #endif 779 break; 780 default: 781 goto bypass; 782 break; 783 } 784 785 #if defined(INET) || defined(INET6) 786 off = pullup_len; 787 #endif 788 789 if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) { 790 if ((ip->ip_v != IPVERSION) || 791 ((ip->ip_hl << 2) < sizeof(struct ip))) 792 goto bypass; 793 /* 794 * In case of IPv4 header with options, we haven't pulled 795 * up enough, yet. 796 */ 797 M_CHECK((ip->ip_hl << 2) - sizeof(struct ip)); 798 799 #if defined(INET) || defined(INET6) 800 /* Save upper layer offset and proto */ 801 off = pullup_len; 802 upper_proto = ip->ip_p; 803 #endif 804 805 /* 806 * XXX: in case of wrong upper layer header we will 807 * forward this packet but skip this record in netflow. 808 */ 809 switch (ip->ip_p) { 810 case IPPROTO_TCP: 811 M_CHECK(sizeof(struct tcphdr)); 812 break; 813 case IPPROTO_UDP: 814 M_CHECK(sizeof(struct udphdr)); 815 break; 816 case IPPROTO_SCTP: 817 M_CHECK(sizeof(struct sctphdr)); 818 break; 819 } 820 } else if (ip != NULL) { 821 /* 822 * Nothing to save except upper layer proto, 823 * since this is a packet fragment. 824 */ 825 #if defined(INET) || defined(INET6) 826 flags |= NG_NETFLOW_IS_FRAG; 827 upper_proto = ip->ip_p; 828 #endif 829 if ((ip->ip_v != IPVERSION) || 830 ((ip->ip_hl << 2) < sizeof(struct ip))) 831 goto bypass; 832 #ifdef INET6 833 } else if (ip6 != NULL) { 834 int cur = ip6->ip6_nxt, hdr_off = 0; 835 struct ip6_ext *ip6e; 836 struct ip6_frag *ip6f; 837 838 if (priv->export9 == NULL) 839 goto bypass; 840 841 /* Save upper layer info. */ 842 off = pullup_len; 843 upper_proto = cur; 844 845 if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) 846 goto bypass; 847 848 /* 849 * Loop through IPv6 extended headers to get upper 850 * layer header / frag. 851 */ 852 for (;;) { 853 switch (cur) { 854 /* 855 * Same as in IPv4, we can forward a 'bad' 856 * packet without accounting. 857 */ 858 case IPPROTO_TCP: 859 M_CHECK(sizeof(struct tcphdr)); 860 goto loopend; 861 case IPPROTO_UDP: 862 M_CHECK(sizeof(struct udphdr)); 863 goto loopend; 864 case IPPROTO_SCTP: 865 M_CHECK(sizeof(struct sctphdr)); 866 goto loopend; 867 868 /* Loop until 'real' upper layer headers */ 869 case IPPROTO_HOPOPTS: 870 case IPPROTO_ROUTING: 871 case IPPROTO_DSTOPTS: 872 M_CHECK(sizeof(struct ip6_ext)); 873 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + 874 off); 875 upper_proto = ip6e->ip6e_nxt; 876 hdr_off = (ip6e->ip6e_len + 1) << 3; 877 break; 878 879 /* RFC4302, can be before DSTOPTS */ 880 case IPPROTO_AH: 881 M_CHECK(sizeof(struct ip6_ext)); 882 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + 883 off); 884 upper_proto = ip6e->ip6e_nxt; 885 hdr_off = (ip6e->ip6e_len + 2) << 2; 886 break; 887 888 case IPPROTO_FRAGMENT: 889 M_CHECK(sizeof(struct ip6_frag)); 890 ip6f = (struct ip6_frag *)(mtod(m, caddr_t) + 891 off); 892 upper_proto = ip6f->ip6f_nxt; 893 hdr_off = sizeof(struct ip6_frag); 894 off += hdr_off; 895 flags |= NG_NETFLOW_IS_FRAG; 896 goto loopend; 897 898 #if 0 899 case IPPROTO_NONE: 900 goto loopend; 901 #endif 902 /* 903 * Any unknown header (new extension or IPv6/IPv4 904 * header for tunnels) ends loop. 905 */ 906 default: 907 goto loopend; 908 } 909 910 off += hdr_off; 911 cur = upper_proto; 912 } 913 #endif 914 } 915 #undef M_CHECK 916 917 #ifdef INET6 918 loopend: 919 #endif 920 /* Just in case of real reallocation in M_CHECK() / m_pullup() */ 921 if (m != m_old) { 922 priv->nfinfo_realloc_mbuf++; 923 /* Restore ip/ipv6 pointer */ 924 if (ip != NULL) 925 ip = (struct ip *)(mtod(m, caddr_t) + l3_off); 926 else if (ip6 != NULL) 927 ip6 = (struct ip6_hdr *)(mtod(m, caddr_t) + l3_off); 928 } 929 930 #if defined(INET) || defined(INET6) 931 upper_ptr = (caddr_t)(mtod(m, caddr_t) + off); 932 933 /* Determine packet input interface. Prefer configured. */ 934 src_if_index = 0; 935 if (hook == iface->out || iface->info.ifinfo_index == 0) { 936 if (m->m_pkthdr.rcvif != NULL) 937 src_if_index = m->m_pkthdr.rcvif->if_index; 938 } else 939 src_if_index = iface->info.ifinfo_index; 940 #endif 941 942 /* Check packet FIB */ 943 fib = M_GETFIB(m); 944 if (fib >= priv->maxfibs) { 945 CTR2(KTR_NET, "ng_netflow_rcvdata(): packet fib %d is out of " 946 "range of available fibs: 0 .. %d", 947 fib, priv->maxfibs); 948 goto bypass; 949 } 950 951 if ((fe = priv_to_fib(priv, fib)) == NULL) { 952 /* Setup new FIB */ 953 if (ng_netflow_fib_init(priv, fib) != 0) { 954 /* malloc() failed */ 955 goto bypass; 956 } 957 958 fe = priv_to_fib(priv, fib); 959 } 960 961 #ifdef INET 962 if (ip != NULL) 963 error = ng_netflow_flow_add(priv, fe, ip, upper_ptr, 964 upper_proto, flags, src_if_index); 965 #endif 966 #if defined(INET6) && defined(INET) 967 else 968 #endif 969 #ifdef INET6 970 if (ip6 != NULL) 971 error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr, 972 upper_proto, flags, src_if_index); 973 #endif 974 else 975 goto bypass; 976 977 acct = 1; 978 bypass: 979 if (out != NULL) { 980 if (acct == 0) { 981 /* Accounting failure */ 982 if (ip != NULL) { 983 counter_u64_add(priv->nfinfo_spackets, 1); 984 counter_u64_add(priv->nfinfo_sbytes, 985 m->m_pkthdr.len); 986 } else if (ip6 != NULL) { 987 counter_u64_add(priv->nfinfo_spackets6, 1); 988 counter_u64_add(priv->nfinfo_sbytes6, 989 m->m_pkthdr.len); 990 } 991 } 992 993 /* XXX: error gets overwritten here */ 994 NG_FWD_NEW_DATA(error, item, out, m); 995 return (error); 996 } 997 done: 998 if (item) 999 NG_FREE_ITEM(item); 1000 if (m) 1001 NG_FREE_M(m); 1002 1003 return (error); 1004 } 1005 1006 /* We will be shut down in a moment */ 1007 static int 1008 ng_netflow_close(node_p node) 1009 { 1010 const priv_p priv = NG_NODE_PRIVATE(node); 1011 1012 callout_drain(&priv->exp_callout); 1013 ng_netflow_cache_flush(priv); 1014 1015 return (0); 1016 } 1017 1018 /* Do local shutdown processing. */ 1019 static int 1020 ng_netflow_rmnode(node_p node) 1021 { 1022 const priv_p priv = NG_NODE_PRIVATE(node); 1023 1024 NG_NODE_SET_PRIVATE(node, NULL); 1025 NG_NODE_UNREF(priv->node); 1026 1027 free(priv->fib_data, M_NETGRAPH); 1028 free(priv, M_NETGRAPH); 1029 1030 return (0); 1031 } 1032 1033 /* Hook disconnection. */ 1034 static int 1035 ng_netflow_disconnect(hook_p hook) 1036 { 1037 node_p node = NG_HOOK_NODE(hook); 1038 priv_p priv = NG_NODE_PRIVATE(node); 1039 iface_p iface = NG_HOOK_PRIVATE(hook); 1040 1041 if (iface != NULL) { 1042 if (iface->hook == hook) 1043 iface->hook = NULL; 1044 if (iface->out == hook) 1045 iface->out = NULL; 1046 } 1047 1048 /* if export hook disconnected stop running expire(). */ 1049 if (hook == priv->export) { 1050 if (priv->export9 == NULL) 1051 callout_drain(&priv->exp_callout); 1052 priv->export = NULL; 1053 } 1054 1055 if (hook == priv->export9) { 1056 if (priv->export == NULL) 1057 callout_drain(&priv->exp_callout); 1058 priv->export9 = NULL; 1059 } 1060 1061 /* Removal of the last link destroys the node. */ 1062 if (NG_NODE_NUMHOOKS(node) == 0) 1063 ng_rmnode_self(node); 1064 1065 return (0); 1066 } 1067