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