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