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