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