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