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 #include "opt_inet.h" 35 #include "opt_inet6.h" 36 #include "opt_route.h" 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/counter.h> 41 #include <sys/kernel.h> 42 #include <sys/ktr.h> 43 #include <sys/limits.h> 44 #include <sys/malloc.h> 45 #include <sys/mbuf.h> 46 #include <sys/socket.h> 47 #include <sys/syslog.h> 48 #include <sys/ctype.h> 49 #include <vm/uma.h> 50 51 #include <net/if.h> 52 #include <net/ethernet.h> 53 #include <net/route.h> 54 #include <net/if_arp.h> 55 #include <net/if_var.h> 56 #include <net/if_private.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; 597 uint8_t acct = 0, bypass = 0; 598 int error = 0, l3_off = 0; 599 #if defined(INET) || defined(INET6) 600 int off; 601 uint8_t flags = 0, upper_proto = 0; 602 unsigned int src_if_index; 603 caddr_t upper_ptr = NULL; 604 #endif 605 fib_export_p fe; 606 uint32_t fib; 607 608 if ((hook == priv->export) || (hook == priv->export9)) { 609 /* 610 * Data arrived on export hook. 611 * This must not happen. 612 */ 613 log(LOG_ERR, "ng_netflow: incoming data on export hook!\n"); 614 ERROUT(EINVAL); 615 } 616 617 if (hook == iface->hook) { 618 if ((iface->info.conf & NG_NETFLOW_CONF_INGRESS) == 0) 619 bypass = 1; 620 out = iface->out; 621 } else if (hook == iface->out) { 622 if ((iface->info.conf & NG_NETFLOW_CONF_EGRESS) == 0) 623 bypass = 1; 624 out = iface->hook; 625 } else 626 ERROUT(EINVAL); 627 628 if ((!bypass) && (iface->info.conf & 629 (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE))) { 630 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW, 631 MTAG_NETFLOW_CALLED, NULL); 632 while (mtag != NULL) { 633 if ((iface->info.conf & NG_NETFLOW_CONF_ONCE) || 634 ((ng_ID_t *)(mtag + 1))[0] == NG_NODE_ID(node)) { 635 bypass = 1; 636 break; 637 } 638 mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW, 639 MTAG_NETFLOW_CALLED, mtag); 640 } 641 } 642 643 if (bypass) { 644 if (out == NULL) 645 ERROUT(ENOTCONN); 646 647 NG_FWD_ITEM_HOOK(error, item, out); 648 return (error); 649 } 650 651 if (iface->info.conf & 652 (NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE)) { 653 mtag = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_CALLED, 654 sizeof(ng_ID_t), M_NOWAIT); 655 if (mtag) { 656 ((ng_ID_t *)(mtag + 1))[0] = NG_NODE_ID(node); 657 m_tag_prepend(NGI_M(item), mtag); 658 } 659 } 660 661 #if defined(INET) || defined(INET6) 662 /* Import configuration flags related to flow creation */ 663 flags = iface->info.conf & NG_NETFLOW_FLOW_FLAGS; 664 #endif 665 666 NGI_GET_M(item, m); 667 m_old = m; 668 669 /* Increase counters. */ 670 iface->info.ifinfo_packets++; 671 672 /* 673 * Depending on interface data link type and packet contents 674 * we pullup enough data, so that ng_netflow_flow_add() does not 675 * need to know about mbuf at all. We keep current length of data 676 * needed to be contiguous in pullup_len. mtod() is done at the 677 * very end one more time, since m can had changed after pulluping. 678 * 679 * In case of unrecognized data we don't return error, but just 680 * pass data to downstream hook, if it is available. 681 */ 682 683 #define M_CHECK(length) do { \ 684 pullup_len += length; \ 685 if (((m)->m_pkthdr.len < (pullup_len)) || \ 686 ((pullup_len) > MHLEN)) { \ 687 error = EINVAL; \ 688 goto bypass; \ 689 } \ 690 if ((m)->m_len < (pullup_len) && \ 691 (((m) = m_pullup((m),(pullup_len))) == NULL)) { \ 692 error = ENOBUFS; \ 693 goto done; \ 694 } \ 695 } while (0) 696 697 switch (iface->info.ifinfo_dlt) { 698 case DLT_EN10MB: /* Ethernet */ 699 { 700 struct ether_header *eh; 701 uint16_t etype; 702 703 M_CHECK(sizeof(struct ether_header)); 704 eh = mtod(m, struct ether_header *); 705 706 /* Make sure this is IP frame. */ 707 etype = ntohs(eh->ether_type); 708 switch (etype) { 709 #ifdef INET 710 case ETHERTYPE_IP: 711 M_CHECK(sizeof(struct ip)); 712 eh = mtod(m, struct ether_header *); 713 ip = (struct ip *)(eh + 1); 714 l3_off = sizeof(struct ether_header); 715 break; 716 #endif 717 #ifdef INET6 718 case ETHERTYPE_IPV6: 719 /* 720 * m_pullup() called by M_CHECK() pullups 721 * kern.ipc.max_protohdr (default 60 bytes) 722 * which is enough. 723 */ 724 M_CHECK(sizeof(struct ip6_hdr)); 725 eh = mtod(m, struct ether_header *); 726 ip6 = (struct ip6_hdr *)(eh + 1); 727 l3_off = sizeof(struct ether_header); 728 break; 729 #endif 730 case ETHERTYPE_VLAN: 731 { 732 struct ether_vlan_header *evh; 733 734 M_CHECK(sizeof(struct ether_vlan_header) - 735 sizeof(struct ether_header)); 736 evh = mtod(m, struct ether_vlan_header *); 737 etype = ntohs(evh->evl_proto); 738 l3_off = sizeof(struct ether_vlan_header); 739 740 if (etype == ETHERTYPE_IP) { 741 #ifdef INET 742 M_CHECK(sizeof(struct ip)); 743 ip = (struct ip *)(evh + 1); 744 break; 745 #endif 746 #ifdef INET6 747 } else if (etype == ETHERTYPE_IPV6) { 748 M_CHECK(sizeof(struct ip6_hdr)); 749 ip6 = (struct ip6_hdr *)(evh + 1); 750 break; 751 #endif 752 } 753 } 754 default: 755 goto bypass; /* pass this frame */ 756 } 757 break; 758 } 759 case DLT_RAW: /* IP packets */ 760 M_CHECK(sizeof(struct ip)); 761 ip = mtod(m, struct ip *); 762 /* l3_off is already zero */ 763 #ifdef INET6 764 /* 765 * If INET6 is not defined IPv6 packets 766 * will be discarded in ng_netflow_flow_add(). 767 */ 768 if (ip->ip_v == IP6VERSION) { 769 ip = NULL; 770 M_CHECK(sizeof(struct ip6_hdr) - sizeof(struct ip)); 771 ip6 = mtod(m, struct ip6_hdr *); 772 } 773 #endif 774 #ifndef INET 775 ip = NULL; 776 #endif 777 break; 778 default: 779 goto bypass; 780 break; 781 } 782 783 #if defined(INET) || defined(INET6) 784 off = pullup_len; 785 #endif 786 787 if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) { 788 if ((ip->ip_v != IPVERSION) || 789 ((ip->ip_hl << 2) < sizeof(struct ip))) 790 goto bypass; 791 /* 792 * In case of IPv4 header with options, we haven't pulled 793 * up enough, yet. 794 */ 795 M_CHECK((ip->ip_hl << 2) - sizeof(struct ip)); 796 797 #if defined(INET) || defined(INET6) 798 /* Save upper layer offset and proto */ 799 off = pullup_len; 800 upper_proto = ip->ip_p; 801 #endif 802 803 /* 804 * XXX: in case of wrong upper layer header we will 805 * forward this packet but skip this record in netflow. 806 */ 807 switch (ip->ip_p) { 808 case IPPROTO_TCP: 809 M_CHECK(sizeof(struct tcphdr)); 810 break; 811 case IPPROTO_UDP: 812 M_CHECK(sizeof(struct udphdr)); 813 break; 814 case IPPROTO_SCTP: 815 M_CHECK(sizeof(struct sctphdr)); 816 break; 817 } 818 } else if (ip != NULL) { 819 /* 820 * Nothing to save except upper layer proto, 821 * since this is a packet fragment. 822 */ 823 #if defined(INET) || defined(INET6) 824 flags |= NG_NETFLOW_IS_FRAG; 825 upper_proto = ip->ip_p; 826 #endif 827 if ((ip->ip_v != IPVERSION) || 828 ((ip->ip_hl << 2) < sizeof(struct ip))) 829 goto bypass; 830 #ifdef INET6 831 } else if (ip6 != NULL) { 832 int cur = ip6->ip6_nxt, hdr_off = 0; 833 struct ip6_ext *ip6e; 834 struct ip6_frag *ip6f; 835 836 if (priv->export9 == NULL) 837 goto bypass; 838 839 /* Save upper layer info. */ 840 off = pullup_len; 841 upper_proto = cur; 842 843 if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) 844 goto bypass; 845 846 /* 847 * Loop through IPv6 extended headers to get upper 848 * layer header / frag. 849 */ 850 for (;;) { 851 switch (cur) { 852 /* 853 * Same as in IPv4, we can forward a 'bad' 854 * packet without accounting. 855 */ 856 case IPPROTO_TCP: 857 M_CHECK(sizeof(struct tcphdr)); 858 goto loopend; 859 case IPPROTO_UDP: 860 M_CHECK(sizeof(struct udphdr)); 861 goto loopend; 862 case IPPROTO_SCTP: 863 M_CHECK(sizeof(struct sctphdr)); 864 goto loopend; 865 866 /* Loop until 'real' upper layer headers */ 867 case IPPROTO_HOPOPTS: 868 case IPPROTO_ROUTING: 869 case IPPROTO_DSTOPTS: 870 M_CHECK(sizeof(struct ip6_ext)); 871 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + 872 off); 873 upper_proto = ip6e->ip6e_nxt; 874 hdr_off = (ip6e->ip6e_len + 1) << 3; 875 break; 876 877 /* RFC4302, can be before DSTOPTS */ 878 case IPPROTO_AH: 879 M_CHECK(sizeof(struct ip6_ext)); 880 ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + 881 off); 882 upper_proto = ip6e->ip6e_nxt; 883 hdr_off = (ip6e->ip6e_len + 2) << 2; 884 break; 885 886 case IPPROTO_FRAGMENT: 887 M_CHECK(sizeof(struct ip6_frag)); 888 ip6f = (struct ip6_frag *)(mtod(m, caddr_t) + 889 off); 890 upper_proto = ip6f->ip6f_nxt; 891 hdr_off = sizeof(struct ip6_frag); 892 off += hdr_off; 893 flags |= NG_NETFLOW_IS_FRAG; 894 goto loopend; 895 896 #if 0 897 case IPPROTO_NONE: 898 goto loopend; 899 #endif 900 /* 901 * Any unknown header (new extension or IPv6/IPv4 902 * header for tunnels) ends loop. 903 */ 904 default: 905 goto loopend; 906 } 907 908 off += hdr_off; 909 cur = upper_proto; 910 } 911 #endif 912 } 913 #undef M_CHECK 914 915 #ifdef INET6 916 loopend: 917 #endif 918 /* Just in case of real reallocation in M_CHECK() / m_pullup() */ 919 if (m != m_old) { 920 priv->nfinfo_realloc_mbuf++; 921 /* Restore ip/ipv6 pointer */ 922 if (ip != NULL) 923 ip = (struct ip *)(mtod(m, caddr_t) + l3_off); 924 else if (ip6 != NULL) 925 ip6 = (struct ip6_hdr *)(mtod(m, caddr_t) + l3_off); 926 } 927 928 #if defined(INET) || defined(INET6) 929 upper_ptr = (caddr_t)(mtod(m, caddr_t) + off); 930 931 /* Determine packet input interface. Prefer configured. */ 932 src_if_index = 0; 933 if (hook == iface->out || iface->info.ifinfo_index == 0) { 934 if (m->m_pkthdr.rcvif != NULL) 935 src_if_index = m->m_pkthdr.rcvif->if_index; 936 } else 937 src_if_index = iface->info.ifinfo_index; 938 #endif 939 940 /* Check packet FIB */ 941 fib = M_GETFIB(m); 942 if (fib >= priv->maxfibs) { 943 CTR2(KTR_NET, "ng_netflow_rcvdata(): packet fib %d is out of " 944 "range of available fibs: 0 .. %d", 945 fib, priv->maxfibs); 946 goto bypass; 947 } 948 949 if ((fe = priv_to_fib(priv, fib)) == NULL) { 950 /* Setup new FIB */ 951 if (ng_netflow_fib_init(priv, fib) != 0) { 952 /* malloc() failed */ 953 goto bypass; 954 } 955 956 fe = priv_to_fib(priv, fib); 957 } 958 959 #ifdef INET 960 if (ip != NULL) 961 error = ng_netflow_flow_add(priv, fe, ip, upper_ptr, 962 upper_proto, flags, src_if_index); 963 #endif 964 #if defined(INET6) && defined(INET) 965 else 966 #endif 967 #ifdef INET6 968 if (ip6 != NULL) 969 error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr, 970 upper_proto, flags, src_if_index); 971 #endif 972 else 973 goto bypass; 974 975 acct = 1; 976 bypass: 977 if (out != NULL) { 978 if (acct == 0) { 979 /* Accounting failure */ 980 if (ip != NULL) { 981 counter_u64_add(priv->nfinfo_spackets, 1); 982 counter_u64_add(priv->nfinfo_sbytes, 983 m->m_pkthdr.len); 984 } else if (ip6 != NULL) { 985 counter_u64_add(priv->nfinfo_spackets6, 1); 986 counter_u64_add(priv->nfinfo_sbytes6, 987 m->m_pkthdr.len); 988 } 989 } 990 991 /* XXX: error gets overwritten here */ 992 NG_FWD_NEW_DATA(error, item, out, m); 993 return (error); 994 } 995 done: 996 if (item) 997 NG_FREE_ITEM(item); 998 if (m) 999 NG_FREE_M(m); 1000 1001 return (error); 1002 } 1003 1004 /* We will be shut down in a moment */ 1005 static int 1006 ng_netflow_close(node_p node) 1007 { 1008 const priv_p priv = NG_NODE_PRIVATE(node); 1009 1010 callout_drain(&priv->exp_callout); 1011 ng_netflow_cache_flush(priv); 1012 1013 return (0); 1014 } 1015 1016 /* Do local shutdown processing. */ 1017 static int 1018 ng_netflow_rmnode(node_p node) 1019 { 1020 const priv_p priv = NG_NODE_PRIVATE(node); 1021 1022 NG_NODE_SET_PRIVATE(node, NULL); 1023 NG_NODE_UNREF(priv->node); 1024 1025 free(priv->fib_data, M_NETGRAPH); 1026 free(priv, M_NETGRAPH); 1027 1028 return (0); 1029 } 1030 1031 /* Hook disconnection. */ 1032 static int 1033 ng_netflow_disconnect(hook_p hook) 1034 { 1035 node_p node = NG_HOOK_NODE(hook); 1036 priv_p priv = NG_NODE_PRIVATE(node); 1037 iface_p iface = NG_HOOK_PRIVATE(hook); 1038 1039 if (iface != NULL) { 1040 if (iface->hook == hook) 1041 iface->hook = NULL; 1042 if (iface->out == hook) 1043 iface->out = NULL; 1044 } 1045 1046 /* if export hook disconnected stop running expire(). */ 1047 if (hook == priv->export) { 1048 if (priv->export9 == NULL) 1049 callout_drain(&priv->exp_callout); 1050 priv->export = NULL; 1051 } 1052 1053 if (hook == priv->export9) { 1054 if (priv->export == NULL) 1055 callout_drain(&priv->exp_callout); 1056 priv->export9 = NULL; 1057 } 1058 1059 /* Removal of the last link destroys the node. */ 1060 if (NG_NODE_NUMHOOKS(node) == 0) 1061 ng_rmnode_self(node); 1062 1063 return (0); 1064 } 1065