Path: blob/main/sys/netgraph/netflow/ng_netflow.c
104897 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2010-2011 Alexander V. Chernikov <[email protected]>4* Copyright (c) 2004-2005 Gleb Smirnoff <[email protected]>5* Copyright (c) 2001-2003 Roman V. Palagin <[email protected]>6* All rights reserved.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*29* $SourceForge: ng_netflow.c,v 1.30 2004/09/05 11:37:43 glebius Exp $30*/3132#include <sys/cdefs.h>33#include "opt_inet.h"34#include "opt_inet6.h"35#include "opt_route.h"3637#include <sys/param.h>38#include <sys/systm.h>39#include <sys/counter.h>40#include <sys/kernel.h>41#include <sys/ktr.h>42#include <sys/limits.h>43#include <sys/malloc.h>44#include <sys/mbuf.h>45#include <sys/socket.h>46#include <sys/syslog.h>47#include <sys/ctype.h>48#include <vm/uma.h>4950#include <net/if.h>51#include <net/ethernet.h>52#include <net/route.h>53#include <net/if_arp.h>54#include <net/if_var.h>55#include <net/if_private.h>56#include <net/if_vlan_var.h>57#include <net/bpf.h>58#include <netinet/in.h>59#include <netinet/in_systm.h>60#include <netinet/ip.h>61#include <netinet/ip6.h>62#include <netinet/tcp.h>63#include <netinet/udp.h>64#include <netinet/sctp.h>6566#include <netgraph/ng_message.h>67#include <netgraph/ng_parse.h>68#include <netgraph/netgraph.h>69#include <netgraph/netflow/netflow.h>70#include <netgraph/netflow/netflow_v9.h>71#include <netgraph/netflow/ng_netflow.h>7273/* Netgraph methods */74static ng_constructor_t ng_netflow_constructor;75static ng_rcvmsg_t ng_netflow_rcvmsg;76static ng_close_t ng_netflow_close;77static ng_shutdown_t ng_netflow_rmnode;78static ng_newhook_t ng_netflow_newhook;79static ng_rcvdata_t ng_netflow_rcvdata;80static ng_disconnect_t ng_netflow_disconnect;8182/* Parse type for struct ng_netflow_info */83static const struct ng_parse_struct_field ng_netflow_info_type_fields[]84= NG_NETFLOW_INFO_TYPE;85static const struct ng_parse_type ng_netflow_info_type = {86&ng_parse_struct_type,87&ng_netflow_info_type_fields88};8990/* Parse type for struct ng_netflow_ifinfo */91static const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[]92= NG_NETFLOW_IFINFO_TYPE;93static const struct ng_parse_type ng_netflow_ifinfo_type = {94&ng_parse_struct_type,95&ng_netflow_ifinfo_type_fields96};9798/* Parse type for struct ng_netflow_setdlt */99static const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[]100= NG_NETFLOW_SETDLT_TYPE;101static const struct ng_parse_type ng_netflow_setdlt_type = {102&ng_parse_struct_type,103&ng_netflow_setdlt_type_fields104};105106/* Parse type for ng_netflow_setifindex */107static const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[]108= NG_NETFLOW_SETIFINDEX_TYPE;109static const struct ng_parse_type ng_netflow_setifindex_type = {110&ng_parse_struct_type,111&ng_netflow_setifindex_type_fields112};113114/* Parse type for ng_netflow_settimeouts */115static const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[]116= NG_NETFLOW_SETTIMEOUTS_TYPE;117static const struct ng_parse_type ng_netflow_settimeouts_type = {118&ng_parse_struct_type,119&ng_netflow_settimeouts_type_fields120};121122/* Parse type for ng_netflow_setconfig */123static const struct ng_parse_struct_field ng_netflow_setconfig_type_fields[]124= NG_NETFLOW_SETCONFIG_TYPE;125static const struct ng_parse_type ng_netflow_setconfig_type = {126&ng_parse_struct_type,127&ng_netflow_setconfig_type_fields128};129130/* Parse type for ng_netflow_settemplate */131static const struct ng_parse_struct_field ng_netflow_settemplate_type_fields[]132= NG_NETFLOW_SETTEMPLATE_TYPE;133static const struct ng_parse_type ng_netflow_settemplate_type = {134&ng_parse_struct_type,135&ng_netflow_settemplate_type_fields136};137138/* Parse type for ng_netflow_setmtu */139static const struct ng_parse_struct_field ng_netflow_setmtu_type_fields[]140= NG_NETFLOW_SETMTU_TYPE;141static const struct ng_parse_type ng_netflow_setmtu_type = {142&ng_parse_struct_type,143&ng_netflow_setmtu_type_fields144};145146/* Parse type for struct ng_netflow_v9info */147static const struct ng_parse_struct_field ng_netflow_v9info_type_fields[]148= NG_NETFLOW_V9INFO_TYPE;149static const struct ng_parse_type ng_netflow_v9info_type = {150&ng_parse_struct_type,151&ng_netflow_v9info_type_fields152};153154/* List of commands and how to convert arguments to/from ASCII */155static const struct ng_cmdlist ng_netflow_cmds[] = {156{157NGM_NETFLOW_COOKIE,158NGM_NETFLOW_INFO,159"info",160NULL,161&ng_netflow_info_type162},163{164NGM_NETFLOW_COOKIE,165NGM_NETFLOW_IFINFO,166"ifinfo",167&ng_parse_uint16_type,168&ng_netflow_ifinfo_type169},170{171NGM_NETFLOW_COOKIE,172NGM_NETFLOW_SETDLT,173"setdlt",174&ng_netflow_setdlt_type,175NULL176},177{178NGM_NETFLOW_COOKIE,179NGM_NETFLOW_SETIFINDEX,180"setifindex",181&ng_netflow_setifindex_type,182NULL183},184{185NGM_NETFLOW_COOKIE,186NGM_NETFLOW_SETTIMEOUTS,187"settimeouts",188&ng_netflow_settimeouts_type,189NULL190},191{192NGM_NETFLOW_COOKIE,193NGM_NETFLOW_SETCONFIG,194"setconfig",195&ng_netflow_setconfig_type,196NULL197},198{199NGM_NETFLOW_COOKIE,200NGM_NETFLOW_SETTEMPLATE,201"settemplate",202&ng_netflow_settemplate_type,203NULL204},205{206NGM_NETFLOW_COOKIE,207NGM_NETFLOW_SETMTU,208"setmtu",209&ng_netflow_setmtu_type,210NULL211},212{213NGM_NETFLOW_COOKIE,214NGM_NETFLOW_V9INFO,215"v9info",216NULL,217&ng_netflow_v9info_type218},219{ 0 }220};221222/* Netgraph node type descriptor */223static struct ng_type ng_netflow_typestruct = {224.version = NG_ABI_VERSION,225.name = NG_NETFLOW_NODE_TYPE,226.constructor = ng_netflow_constructor,227.rcvmsg = ng_netflow_rcvmsg,228.close = ng_netflow_close,229.shutdown = ng_netflow_rmnode,230.newhook = ng_netflow_newhook,231.rcvdata = ng_netflow_rcvdata,232.disconnect = ng_netflow_disconnect,233.cmdlist = ng_netflow_cmds,234};235NETGRAPH_INIT(netflow, &ng_netflow_typestruct);236237/* Called at node creation */238static int239ng_netflow_constructor(node_p node)240{241priv_p priv;242int i;243244/* Initialize private data */245priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);246247/* Initialize fib data */248priv->maxfibs = rt_numfibs;249priv->fib_data = malloc(sizeof(fib_export_p) * priv->maxfibs,250M_NETGRAPH, M_WAITOK | M_ZERO);251252/* Make node and its data point at each other */253NG_NODE_SET_PRIVATE(node, priv);254priv->node = node;255256/* Initialize timeouts to default values */257priv->nfinfo_inact_t = INACTIVE_TIMEOUT;258priv->nfinfo_act_t = ACTIVE_TIMEOUT;259260/* Set default config */261for (i = 0; i < NG_NETFLOW_MAXIFACES; i++)262priv->ifaces[i].info.conf = NG_NETFLOW_CONF_INGRESS;263264/* Initialize callout handle */265callout_init(&priv->exp_callout, 1);266267/* Allocate memory and set up flow cache */268ng_netflow_cache_init(priv);269270return (0);271}272273/*274* ng_netflow supports two hooks: data and export.275* Incoming traffic is expected on data, and expired276* netflow datagrams are sent to export.277*/278static int279ng_netflow_newhook(node_p node, hook_p hook, const char *name)280{281const priv_p priv = NG_NODE_PRIVATE(node);282283if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */284strlen(NG_NETFLOW_HOOK_DATA)) == 0) {285iface_p iface;286int ifnum = -1;287const char *cp;288char *eptr;289290cp = name + strlen(NG_NETFLOW_HOOK_DATA);291if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))292return (EINVAL);293294ifnum = (int)strtoul(cp, &eptr, 10);295if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)296return (EINVAL);297298/* See if hook is already connected */299if (priv->ifaces[ifnum].hook != NULL)300return (EISCONN);301302iface = &priv->ifaces[ifnum];303304/* Link private info and hook together */305NG_HOOK_SET_PRIVATE(hook, iface);306iface->hook = hook;307308/*309* In most cases traffic accounting is done on an310* Ethernet interface, so default data link type311* will be DLT_EN10MB.312*/313iface->info.ifinfo_dlt = DLT_EN10MB;314315} else if (strncmp(name, NG_NETFLOW_HOOK_OUT,316strlen(NG_NETFLOW_HOOK_OUT)) == 0) {317iface_p iface;318int ifnum = -1;319const char *cp;320char *eptr;321322cp = name + strlen(NG_NETFLOW_HOOK_OUT);323if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))324return (EINVAL);325326ifnum = (int)strtoul(cp, &eptr, 10);327if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)328return (EINVAL);329330/* See if hook is already connected */331if (priv->ifaces[ifnum].out != NULL)332return (EISCONN);333334iface = &priv->ifaces[ifnum];335336/* Link private info and hook together */337NG_HOOK_SET_PRIVATE(hook, iface);338iface->out = hook;339340} else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) {341if (priv->export != NULL)342return (EISCONN);343344/* Netflow version 5 supports 32-bit counters only */345if (CNTR_MAX == UINT64_MAX)346return (EINVAL);347348priv->export = hook;349350/* Exporter is ready. Let's schedule expiry. */351callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,352(void *)priv);353} else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT9) == 0) {354if (priv->export9 != NULL)355return (EISCONN);356357priv->export9 = hook;358359/* Exporter is ready. Let's schedule expiry. */360callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,361(void *)priv);362} else363return (EINVAL);364365return (0);366}367368/* Get a netgraph control message. */369static int370ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook)371{372const priv_p priv = NG_NODE_PRIVATE(node);373struct ng_mesg *resp = NULL;374int error = 0;375struct ng_mesg *msg;376377NGI_GET_MSG(item, msg);378379/* Deal with message according to cookie and command */380switch (msg->header.typecookie) {381case NGM_NETFLOW_COOKIE:382switch (msg->header.cmd) {383case NGM_NETFLOW_INFO:384{385struct ng_netflow_info *i;386387NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info),388M_NOWAIT);389i = (struct ng_netflow_info *)resp->data;390ng_netflow_copyinfo(priv, i);391392break;393}394case NGM_NETFLOW_IFINFO:395{396struct ng_netflow_ifinfo *i;397const uint16_t *index;398399if (msg->header.arglen != sizeof(uint16_t))400ERROUT(EINVAL);401402index = (uint16_t *)msg->data;403if (*index >= NG_NETFLOW_MAXIFACES)404ERROUT(EINVAL);405406/* connected iface? */407if (priv->ifaces[*index].hook == NULL)408ERROUT(EINVAL);409410NG_MKRESPONSE(resp, msg,411sizeof(struct ng_netflow_ifinfo), M_NOWAIT);412i = (struct ng_netflow_ifinfo *)resp->data;413memcpy((void *)i, (void *)&priv->ifaces[*index].info,414sizeof(priv->ifaces[*index].info));415416break;417}418case NGM_NETFLOW_SETDLT:419{420struct ng_netflow_setdlt *set;421struct ng_netflow_iface *iface;422423if (msg->header.arglen !=424sizeof(struct ng_netflow_setdlt))425ERROUT(EINVAL);426427set = (struct ng_netflow_setdlt *)msg->data;428if (set->iface >= NG_NETFLOW_MAXIFACES)429ERROUT(EINVAL);430iface = &priv->ifaces[set->iface];431432/* connected iface? */433if (iface->hook == NULL)434ERROUT(EINVAL);435436switch (set->dlt) {437case DLT_EN10MB:438iface->info.ifinfo_dlt = DLT_EN10MB;439break;440case DLT_RAW:441iface->info.ifinfo_dlt = DLT_RAW;442break;443default:444ERROUT(EINVAL);445}446break;447}448case NGM_NETFLOW_SETIFINDEX:449{450struct ng_netflow_setifindex *set;451struct ng_netflow_iface *iface;452453if (msg->header.arglen !=454sizeof(struct ng_netflow_setifindex))455ERROUT(EINVAL);456457set = (struct ng_netflow_setifindex *)msg->data;458if (set->iface >= NG_NETFLOW_MAXIFACES)459ERROUT(EINVAL);460iface = &priv->ifaces[set->iface];461462/* connected iface? */463if (iface->hook == NULL)464ERROUT(EINVAL);465466iface->info.ifinfo_index = set->index;467468break;469}470case NGM_NETFLOW_SETTIMEOUTS:471{472struct ng_netflow_settimeouts *set;473474if (msg->header.arglen !=475sizeof(struct ng_netflow_settimeouts))476ERROUT(EINVAL);477478set = (struct ng_netflow_settimeouts *)msg->data;479480priv->nfinfo_inact_t = set->inactive_timeout;481priv->nfinfo_act_t = set->active_timeout;482483break;484}485case NGM_NETFLOW_SETCONFIG:486{487struct ng_netflow_setconfig *set;488489if (msg->header.arglen !=490sizeof(struct ng_netflow_setconfig))491ERROUT(EINVAL);492493set = (struct ng_netflow_setconfig *)msg->data;494495if (set->iface >= NG_NETFLOW_MAXIFACES)496ERROUT(EINVAL);497498priv->ifaces[set->iface].info.conf = set->conf;499500break;501}502case NGM_NETFLOW_SETTEMPLATE:503{504struct ng_netflow_settemplate *set;505506if (msg->header.arglen !=507sizeof(struct ng_netflow_settemplate))508ERROUT(EINVAL);509510set = (struct ng_netflow_settemplate *)msg->data;511512priv->templ_packets = set->packets;513priv->templ_time = set->time;514515break;516}517case NGM_NETFLOW_SETMTU:518{519struct ng_netflow_setmtu *set;520521if (msg->header.arglen !=522sizeof(struct ng_netflow_setmtu))523ERROUT(EINVAL);524525set = (struct ng_netflow_setmtu *)msg->data;526if ((set->mtu < MIN_MTU) || (set->mtu > MAX_MTU))527ERROUT(EINVAL);528529priv->mtu = set->mtu;530531break;532}533case NGM_NETFLOW_SHOW:534if (msg->header.arglen !=535sizeof(struct ngnf_show_header))536ERROUT(EINVAL);537538NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT);539540if (!resp)541ERROUT(ENOMEM);542543error = ng_netflow_flow_show(priv,544(struct ngnf_show_header *)msg->data,545(struct ngnf_show_header *)resp->data);546547if (error)548NG_FREE_MSG(resp);549550break;551case NGM_NETFLOW_V9INFO:552{553struct ng_netflow_v9info *i;554555NG_MKRESPONSE(resp, msg,556sizeof(struct ng_netflow_v9info), M_NOWAIT);557i = (struct ng_netflow_v9info *)resp->data;558ng_netflow_copyv9info(priv, i);559560break;561}562default:563ERROUT(EINVAL); /* unknown command */564break;565}566break;567default:568ERROUT(EINVAL); /* incorrect cookie */569break;570}571572/*573* Take care of synchronous response, if any.574* Free memory and return.575*/576done:577NG_RESPOND_MSG(error, node, item, resp);578NG_FREE_MSG(msg);579580return (error);581}582583/* Receive data on hook. */584static int585ng_netflow_rcvdata (hook_p hook, item_p item)586{587const node_p node = NG_HOOK_NODE(hook);588const priv_p priv = NG_NODE_PRIVATE(node);589const iface_p iface = NG_HOOK_PRIVATE(hook);590hook_p out;591struct mbuf *m = NULL, *m_old = NULL;592struct ip *ip = NULL;593struct ip6_hdr *ip6 = NULL;594struct m_tag *mtag;595int pullup_len = 0;596uint8_t acct = 0, bypass = 0;597int error = 0, l3_off = 0;598#if defined(INET) || defined(INET6)599int off;600uint8_t flags = 0, upper_proto = 0;601unsigned int src_if_index;602caddr_t upper_ptr = NULL;603#endif604fib_export_p fe;605uint32_t fib;606607if ((hook == priv->export) || (hook == priv->export9)) {608/*609* Data arrived on export hook.610* This must not happen.611*/612log(LOG_ERR, "ng_netflow: incoming data on export hook!\n");613ERROUT(EINVAL);614}615616if (hook == iface->hook) {617if ((iface->info.conf & NG_NETFLOW_CONF_INGRESS) == 0)618bypass = 1;619out = iface->out;620} else if (hook == iface->out) {621if ((iface->info.conf & NG_NETFLOW_CONF_EGRESS) == 0)622bypass = 1;623out = iface->hook;624} else625ERROUT(EINVAL);626627if ((!bypass) && (iface->info.conf &628(NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE))) {629mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,630MTAG_NETFLOW_CALLED, NULL);631while (mtag != NULL) {632if ((iface->info.conf & NG_NETFLOW_CONF_ONCE) ||633((ng_ID_t *)(mtag + 1))[0] == NG_NODE_ID(node)) {634bypass = 1;635break;636}637mtag = m_tag_locate(NGI_M(item), MTAG_NETFLOW,638MTAG_NETFLOW_CALLED, mtag);639}640}641642if (bypass) {643if (out == NULL)644ERROUT(ENOTCONN);645646NG_FWD_ITEM_HOOK(error, item, out);647return (error);648}649650if (iface->info.conf &651(NG_NETFLOW_CONF_ONCE | NG_NETFLOW_CONF_THISONCE)) {652mtag = m_tag_alloc(MTAG_NETFLOW, MTAG_NETFLOW_CALLED,653sizeof(ng_ID_t), M_NOWAIT);654if (mtag) {655((ng_ID_t *)(mtag + 1))[0] = NG_NODE_ID(node);656m_tag_prepend(NGI_M(item), mtag);657}658}659660#if defined(INET) || defined(INET6)661/* Import configuration flags related to flow creation */662flags = iface->info.conf & NG_NETFLOW_FLOW_FLAGS;663#endif664665NGI_GET_M(item, m);666m_old = m;667668/* Increase counters. */669iface->info.ifinfo_packets++;670671/*672* Depending on interface data link type and packet contents673* we pullup enough data, so that ng_netflow_flow_add() does not674* need to know about mbuf at all. We keep current length of data675* needed to be contiguous in pullup_len. mtod() is done at the676* very end one more time, since m can had changed after pulluping.677*678* In case of unrecognized data we don't return error, but just679* pass data to downstream hook, if it is available.680*/681682#define M_CHECK(length) do { \683pullup_len += length; \684if (((m)->m_pkthdr.len < (pullup_len)) || \685((pullup_len) > MHLEN)) { \686error = EINVAL; \687goto bypass; \688} \689if ((m)->m_len < (pullup_len) && \690(((m) = m_pullup((m),(pullup_len))) == NULL)) { \691error = ENOBUFS; \692goto done; \693} \694} while (0)695696switch (iface->info.ifinfo_dlt) {697case DLT_EN10MB: /* Ethernet */698{699struct ether_header *eh;700uint16_t etype;701702M_CHECK(sizeof(struct ether_header));703eh = mtod(m, struct ether_header *);704705/* Make sure this is IP frame. */706etype = ntohs(eh->ether_type);707switch (etype) {708#ifdef INET709case ETHERTYPE_IP:710M_CHECK(sizeof(struct ip));711eh = mtod(m, struct ether_header *);712ip = (struct ip *)(eh + 1);713l3_off = sizeof(struct ether_header);714break;715#endif716#ifdef INET6717case ETHERTYPE_IPV6:718/*719* m_pullup() called by M_CHECK() pullups720* kern.ipc.max_protohdr (default 60 bytes)721* which is enough.722*/723M_CHECK(sizeof(struct ip6_hdr));724eh = mtod(m, struct ether_header *);725ip6 = (struct ip6_hdr *)(eh + 1);726l3_off = sizeof(struct ether_header);727break;728#endif729case ETHERTYPE_VLAN:730{731struct ether_vlan_header *evh;732733M_CHECK(sizeof(struct ether_vlan_header) -734sizeof(struct ether_header));735evh = mtod(m, struct ether_vlan_header *);736etype = ntohs(evh->evl_proto);737l3_off = sizeof(struct ether_vlan_header);738739if (etype == ETHERTYPE_IP) {740#ifdef INET741M_CHECK(sizeof(struct ip));742ip = (struct ip *)(evh + 1);743break;744#endif745#ifdef INET6746} else if (etype == ETHERTYPE_IPV6) {747M_CHECK(sizeof(struct ip6_hdr));748ip6 = (struct ip6_hdr *)(evh + 1);749break;750#endif751}752}753default:754goto bypass; /* pass this frame */755}756break;757}758case DLT_RAW: /* IP packets */759M_CHECK(sizeof(struct ip));760ip = mtod(m, struct ip *);761/* l3_off is already zero */762#ifdef INET6763/*764* If INET6 is not defined IPv6 packets765* will be discarded in ng_netflow_flow_add().766*/767if (ip->ip_v == IP6VERSION) {768ip = NULL;769M_CHECK(sizeof(struct ip6_hdr) - sizeof(struct ip));770ip6 = mtod(m, struct ip6_hdr *);771}772#endif773#ifndef INET774ip = NULL;775#endif776break;777default:778goto bypass;779break;780}781782#if defined(INET) || defined(INET6)783off = pullup_len;784#endif785786if ((ip != NULL) && ((ip->ip_off & htons(IP_OFFMASK)) == 0)) {787if ((ip->ip_v != IPVERSION) ||788((ip->ip_hl << 2) < sizeof(struct ip)))789goto bypass;790/*791* In case of IPv4 header with options, we haven't pulled792* up enough, yet.793*/794M_CHECK((ip->ip_hl << 2) - sizeof(struct ip));795796#if defined(INET) || defined(INET6)797/* Save upper layer offset and proto */798off = pullup_len;799upper_proto = ip->ip_p;800#endif801802/*803* XXX: in case of wrong upper layer header we will804* forward this packet but skip this record in netflow.805*/806switch (ip->ip_p) {807case IPPROTO_TCP:808M_CHECK(sizeof(struct tcphdr));809break;810case IPPROTO_UDP:811M_CHECK(sizeof(struct udphdr));812break;813case IPPROTO_SCTP:814M_CHECK(sizeof(struct sctphdr));815break;816}817} else if (ip != NULL) {818/*819* Nothing to save except upper layer proto,820* since this is a packet fragment.821*/822#if defined(INET) || defined(INET6)823flags |= NG_NETFLOW_IS_FRAG;824upper_proto = ip->ip_p;825#endif826if ((ip->ip_v != IPVERSION) ||827((ip->ip_hl << 2) < sizeof(struct ip)))828goto bypass;829#ifdef INET6830} else if (ip6 != NULL) {831int cur = ip6->ip6_nxt, hdr_off = 0;832struct ip6_ext *ip6e;833struct ip6_frag *ip6f;834835if (priv->export9 == NULL)836goto bypass;837838/* Save upper layer info. */839off = pullup_len;840upper_proto = cur;841842if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)843goto bypass;844845/*846* Loop through IPv6 extended headers to get upper847* layer header / frag.848*/849for (;;) {850switch (cur) {851/*852* Same as in IPv4, we can forward a 'bad'853* packet without accounting.854*/855case IPPROTO_TCP:856M_CHECK(sizeof(struct tcphdr));857goto loopend;858case IPPROTO_UDP:859M_CHECK(sizeof(struct udphdr));860goto loopend;861case IPPROTO_SCTP:862M_CHECK(sizeof(struct sctphdr));863goto loopend;864865/* Loop until 'real' upper layer headers */866case IPPROTO_HOPOPTS:867case IPPROTO_ROUTING:868case IPPROTO_DSTOPTS:869M_CHECK(sizeof(struct ip6_ext));870ip6e = (struct ip6_ext *)(mtod(m, caddr_t) +871off);872upper_proto = ip6e->ip6e_nxt;873hdr_off = (ip6e->ip6e_len + 1) << 3;874break;875876/* RFC4302, can be before DSTOPTS */877case IPPROTO_AH:878M_CHECK(sizeof(struct ip6_ext));879ip6e = (struct ip6_ext *)(mtod(m, caddr_t) +880off);881upper_proto = ip6e->ip6e_nxt;882hdr_off = (ip6e->ip6e_len + 2) << 2;883break;884885case IPPROTO_FRAGMENT:886M_CHECK(sizeof(struct ip6_frag));887ip6f = (struct ip6_frag *)(mtod(m, caddr_t) +888off);889upper_proto = ip6f->ip6f_nxt;890hdr_off = sizeof(struct ip6_frag);891off += hdr_off;892flags |= NG_NETFLOW_IS_FRAG;893goto loopend;894895#if 0896case IPPROTO_NONE:897goto loopend;898#endif899/*900* Any unknown header (new extension or IPv6/IPv4901* header for tunnels) ends loop.902*/903default:904goto loopend;905}906907off += hdr_off;908cur = upper_proto;909}910#endif911}912#undef M_CHECK913914#ifdef INET6915loopend:916#endif917/* Just in case of real reallocation in M_CHECK() / m_pullup() */918if (m != m_old) {919priv->nfinfo_realloc_mbuf++;920/* Restore ip/ipv6 pointer */921if (ip != NULL)922ip = (struct ip *)(mtod(m, caddr_t) + l3_off);923else if (ip6 != NULL)924ip6 = (struct ip6_hdr *)(mtod(m, caddr_t) + l3_off);925}926927#if defined(INET) || defined(INET6)928upper_ptr = (caddr_t)(mtod(m, caddr_t) + off);929930/* Determine packet input interface. Prefer configured. */931src_if_index = 0;932if (hook == iface->out || iface->info.ifinfo_index == 0) {933if (m->m_pkthdr.rcvif != NULL)934src_if_index = m->m_pkthdr.rcvif->if_index;935} else936src_if_index = iface->info.ifinfo_index;937#endif938939/* Check packet FIB */940fib = M_GETFIB(m);941if (fib >= priv->maxfibs) {942CTR2(KTR_NET, "ng_netflow_rcvdata(): packet fib %d is out of "943"range of available fibs: 0 .. %d",944fib, priv->maxfibs);945goto bypass;946}947948if ((fe = priv_to_fib(priv, fib)) == NULL) {949/* Setup new FIB */950if (ng_netflow_fib_init(priv, fib) != 0) {951/* malloc() failed */952goto bypass;953}954955fe = priv_to_fib(priv, fib);956}957958#ifdef INET959if (ip != NULL)960error = ng_netflow_flow_add(priv, fe, ip, upper_ptr,961upper_proto, flags, src_if_index);962#endif963#if defined(INET6) && defined(INET)964else965#endif966#ifdef INET6967if (ip6 != NULL)968error = ng_netflow_flow6_add(priv, fe, ip6, upper_ptr,969upper_proto, flags, src_if_index);970#endif971else972goto bypass;973974acct = 1;975bypass:976if (out != NULL) {977if (acct == 0) {978/* Accounting failure */979if (ip != NULL) {980counter_u64_add(priv->nfinfo_spackets, 1);981counter_u64_add(priv->nfinfo_sbytes,982m->m_pkthdr.len);983} else if (ip6 != NULL) {984counter_u64_add(priv->nfinfo_spackets6, 1);985counter_u64_add(priv->nfinfo_sbytes6,986m->m_pkthdr.len);987}988}989990/* XXX: error gets overwritten here */991NG_FWD_NEW_DATA(error, item, out, m);992return (error);993}994done:995if (item)996NG_FREE_ITEM(item);997if (m)998NG_FREE_M(m);9991000return (error);1001}10021003/* We will be shut down in a moment */1004static int1005ng_netflow_close(node_p node)1006{1007const priv_p priv = NG_NODE_PRIVATE(node);10081009callout_drain(&priv->exp_callout);1010ng_netflow_cache_flush(priv);10111012return (0);1013}10141015/* Do local shutdown processing. */1016static int1017ng_netflow_rmnode(node_p node)1018{1019const priv_p priv = NG_NODE_PRIVATE(node);10201021NG_NODE_SET_PRIVATE(node, NULL);1022NG_NODE_UNREF(priv->node);10231024free(priv->fib_data, M_NETGRAPH);1025free(priv, M_NETGRAPH);10261027return (0);1028}10291030/* Hook disconnection. */1031static int1032ng_netflow_disconnect(hook_p hook)1033{1034node_p node = NG_HOOK_NODE(hook);1035priv_p priv = NG_NODE_PRIVATE(node);1036iface_p iface = NG_HOOK_PRIVATE(hook);10371038if (iface != NULL) {1039if (iface->hook == hook)1040iface->hook = NULL;1041if (iface->out == hook)1042iface->out = NULL;1043}10441045/* if export hook disconnected stop running expire(). */1046if (hook == priv->export) {1047if (priv->export9 == NULL)1048callout_drain(&priv->exp_callout);1049priv->export = NULL;1050}10511052if (hook == priv->export9) {1053if (priv->export == NULL)1054callout_drain(&priv->exp_callout);1055priv->export9 = NULL;1056}10571058/* Removal of the last link destroys the node. */1059if (NG_NODE_NUMHOOKS(node) == 0)1060ng_rmnode_self(node);10611062return (0);1063}106410651066