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