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