summaryrefslogtreecommitdiffstats
path: root/sys/netgraph/netflow/ng_netflow.c
diff options
context:
space:
mode:
authorglebius <glebius@FreeBSD.org>2004-09-16 20:24:23 +0000
committerglebius <glebius@FreeBSD.org>2004-09-16 20:24:23 +0000
commit8546c37afed4c64f225d8ec619d1ed59cdb2caec (patch)
tree2ec8b73fd58b00a1b3df300b870676e6cecb8622 /sys/netgraph/netflow/ng_netflow.c
parent4b598bb10d0d33b9af9e0476855de5ae7b1c54d5 (diff)
downloadFreeBSD-src-8546c37afed4c64f225d8ec619d1ed59cdb2caec.zip
FreeBSD-src-8546c37afed4c64f225d8ec619d1ed59cdb2caec.tar.gz
A netgraph node implementing Netflow version 5.
Supported by: Bestcom ISP, Rinet ISP Approved by: julian (mentor)
Diffstat (limited to 'sys/netgraph/netflow/ng_netflow.c')
-rw-r--r--sys/netgraph/netflow/ng_netflow.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/sys/netgraph/netflow/ng_netflow.c b/sys/netgraph/netflow/ng_netflow.c
new file mode 100644
index 0000000..4cfbe08
--- /dev/null
+++ b/sys/netgraph/netflow/ng_netflow.c
@@ -0,0 +1,527 @@
+/*-
+ * Copyright (c) 2004 Gleb Smirnoff <glebius@cell.sick.ru>
+ * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Gleb Smirnoff and
+ * contributors.
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $SourceForge: ng_netflow.c,v 1.30 2004/09/05 11:37:43 glebius Exp $
+ */
+
+static const char rcs_id[] =
+ "@(#) $FreeBSD$";
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ctype.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <net/if_var.h>
+#include <net/bpf.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_parse.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/netflow/netflow.h>
+#include <netgraph/netflow/ng_netflow.h>
+
+/* Netgraph methods */
+static ng_constructor_t ng_netflow_constructor;
+static ng_rcvmsg_t ng_netflow_rcvmsg;
+static ng_close_t ng_netflow_close;
+static ng_shutdown_t ng_netflow_rmnode;
+static ng_newhook_t ng_netflow_newhook;
+static ng_rcvdata_t ng_netflow_rcvdata;
+static ng_disconnect_t ng_netflow_disconnect;
+
+/* Parse type for struct ng_netflow_info */
+static const struct ng_parse_struct_field ng_netflow_info_type_fields[]
+ = NG_NETFLOW_INFO_TYPE;
+static const struct ng_parse_type ng_netflow_info_type = {
+ &ng_parse_struct_type,
+ &ng_netflow_info_type_fields
+};
+
+/* Parse type for struct ng_netflow_ifinfo */
+static const struct ng_parse_struct_field ng_netflow_ifinfo_type_fields[]
+ = NG_NETFLOW_IFINFO_TYPE;
+static const struct ng_parse_type ng_netflow_ifinfo_type = {
+ &ng_parse_struct_type,
+ &ng_netflow_ifinfo_type_fields
+};
+
+/* Parse type for struct ng_netflow_setdlt */
+static const struct ng_parse_struct_field ng_netflow_setdlt_type_fields[]
+ = NG_NETFLOW_SETDLT_TYPE;
+static const struct ng_parse_type ng_netflow_setdlt_type = {
+ &ng_parse_struct_type,
+ &ng_netflow_setdlt_type_fields
+};
+
+/* Parse type for ng_netflow_setifindex */
+static const struct ng_parse_struct_field ng_netflow_setifindex_type_fields[]
+ = NG_NETFLOW_SETIFINDEX_TYPE;
+static const struct ng_parse_type ng_netflow_setifindex_type = {
+ &ng_parse_struct_type,
+ &ng_netflow_setifindex_type_fields
+};
+
+/* Parse type for ng_netflow_settimeouts */
+static const struct ng_parse_struct_field ng_netflow_settimeouts_type_fields[]
+ = NG_NETFLOW_SETTIMEOUTS_TYPE;
+static const struct ng_parse_type ng_netflow_settimeouts_type = {
+ &ng_parse_struct_type,
+ &ng_netflow_settimeouts_type_fields
+};
+
+/* List of commands and how to convert arguments to/from ASCII */
+static const struct ng_cmdlist ng_netflow_cmds[] = {
+ {
+ NGM_NETFLOW_COOKIE,
+ NGM_NETFLOW_INFO,
+ "info",
+ NULL,
+ &ng_netflow_info_type
+ },
+ {
+ NGM_NETFLOW_COOKIE,
+ NGM_NETFLOW_IFINFO,
+ "ifinfo",
+ &ng_parse_uint8_type,
+ &ng_netflow_ifinfo_type
+ },
+ {
+ NGM_NETFLOW_COOKIE,
+ NGM_NETFLOW_SETDLT,
+ "setdlt",
+ &ng_netflow_setdlt_type,
+ NULL
+ },
+ {
+ NGM_NETFLOW_COOKIE,
+ NGM_NETFLOW_SETIFINDEX,
+ "setifindex",
+ &ng_netflow_setifindex_type,
+ NULL
+ },
+ {
+ NGM_NETFLOW_COOKIE,
+ NGM_NETFLOW_SETTIMEOUTS,
+ "settimeouts",
+ &ng_netflow_settimeouts_type,
+ NULL
+ },
+ { 0 }
+};
+
+
+/* Netgraph node type descriptor */
+static struct ng_type ng_netflow_typestruct = {
+ .version = NG_ABI_VERSION,
+ .name = NG_NETFLOW_NODE_TYPE,
+ .constructor = ng_netflow_constructor,
+ .rcvmsg = ng_netflow_rcvmsg,
+ .close = ng_netflow_close,
+ .shutdown = ng_netflow_rmnode,
+ .newhook = ng_netflow_newhook,
+ .rcvdata = ng_netflow_rcvdata,
+ .disconnect = ng_netflow_disconnect,
+ .cmdlist = ng_netflow_cmds,
+};
+NETGRAPH_INIT(netflow, &ng_netflow_typestruct);
+
+/* Called at node creation */
+static int
+ng_netflow_constructor (node_p node)
+{
+ priv_p priv;
+ int error = 0;
+
+ /* Initialize private data */
+ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT);
+ if (priv == NULL)
+ return (ENOMEM);
+ bzero(priv, sizeof(*priv));
+
+ /* Make node and its data point at each other */
+ NG_NODE_SET_PRIVATE(node, priv);
+ priv->node = node;
+
+ /* Initialize timeouts to default values */
+ priv->info.nfinfo_inact_t = INACTIVE_TIMEOUT;
+ priv->info.nfinfo_act_t = ACTIVE_TIMEOUT;
+
+ /* Initialize callout handle */
+ callout_init(&priv->exp_callout, 1);
+
+ /* Allocate memory and set up flow cache */
+ if ((error = ng_netflow_cache_init(priv)))
+ return (error);
+
+ priv->dgram.header.version = htons(NETFLOW_V5);
+
+ return (0);
+}
+
+/*
+ * ng_netflow supports two hooks: data and export.
+ * Incoming traffic is expected on data, and expired
+ * netflow datagrams are sent to export.
+ */
+static int
+ng_netflow_newhook(node_p node, hook_p hook, const char *name)
+{
+ const priv_p priv = NG_NODE_PRIVATE(node);
+
+ if (strncmp(name, NG_NETFLOW_HOOK_DATA, /* an iface hook? */
+ strlen(NG_NETFLOW_HOOK_DATA)) == 0) {
+ iface_p iface;
+ int ifnum = -1;
+ const char *cp;
+ char *eptr;
+
+ cp = name + strlen(NG_NETFLOW_HOOK_DATA);
+ if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0'))
+ return (EINVAL);
+
+ ifnum = (int)strtoul(cp, &eptr, 10);
+ if (*eptr != '\0' || ifnum < 0 || ifnum >= NG_NETFLOW_MAXIFACES)
+ return (EINVAL);
+
+ /* See if hook is already connected */
+ if (priv->ifaces[ifnum].hook != NULL)
+ return (EISCONN);
+
+ iface = &priv->ifaces[ifnum];
+
+ /* Link private info and hook together */
+ NG_HOOK_SET_PRIVATE(hook, iface);
+ iface->hook = hook;
+
+ /*
+ * In most cases traffic accounting is done on an
+ * Ethernet interface, so default data link type
+ * will be DLT_EN10MB.
+ */
+ iface->info.ifinfo_dlt = DLT_EN10MB;
+
+ } else if (strcmp(name, NG_NETFLOW_HOOK_EXPORT) == 0) {
+
+ if (priv->export != NULL)
+ return (EISCONN);
+
+ priv->export = hook;
+
+ /* Exporter is ready. Let's schedule expiry. */
+ callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
+ (void *)priv);
+ } else
+ return (EINVAL);
+
+ return (0);
+}
+
+/* Get a netgraph control message. */
+static int
+ng_netflow_rcvmsg (node_p node, item_p item, hook_p lasthook)
+{
+ const priv_p priv = NG_NODE_PRIVATE(node);
+ struct ng_mesg *resp = NULL;
+ int error = 0;
+ struct ng_mesg *msg;
+
+ NGI_GET_MSG(item, msg);
+
+ /* Deal with message according to cookie and command */
+ switch (msg->header.typecookie) {
+ case NGM_NETFLOW_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_NETFLOW_INFO:
+ {
+ struct ng_netflow_info *i;
+
+ NG_MKRESPONSE(resp, msg, sizeof(struct ng_netflow_info),
+ M_NOWAIT);
+ i = (struct ng_netflow_info *)resp->data;
+ ng_netflow_copyinfo(priv, i);
+
+ break;
+ }
+ case NGM_NETFLOW_IFINFO:
+ {
+ struct ng_netflow_ifinfo *i;
+ const uint8_t *index;
+
+ if (msg->header.arglen != sizeof(uint8_t))
+ ERROUT(EINVAL);
+
+ index = (uint8_t *)msg->data;
+
+ /* connected iface? */
+ if (priv->ifaces[*index].hook == NULL)
+ ERROUT(EINVAL);
+
+ NG_MKRESPONSE(resp, msg,
+ sizeof(struct ng_netflow_ifinfo), M_NOWAIT);
+ i = (struct ng_netflow_ifinfo *)resp->data;
+ memcpy((void *)i, (void *)&priv->ifaces[*index].info,
+ sizeof(priv->ifaces[*index].info));
+
+ break;
+ }
+ case NGM_NETFLOW_SETDLT:
+ {
+ struct ng_netflow_setdlt *set;
+ struct ng_netflow_iface *iface;
+
+ if (msg->header.arglen != sizeof(struct ng_netflow_setdlt))
+ ERROUT(EINVAL);
+
+ set = (struct ng_netflow_setdlt *)msg->data;
+ iface = &priv->ifaces[set->iface];
+
+ /* connected iface? */
+ if (iface->hook == NULL)
+ ERROUT(EINVAL);
+
+ switch (set->dlt) {
+ case DLT_EN10MB:
+ iface->info.ifinfo_dlt = DLT_EN10MB;
+ break;
+ case DLT_RAW:
+ iface->info.ifinfo_dlt = DLT_RAW;
+ break;
+ default:
+ ERROUT(EINVAL);
+ }
+ break;
+ }
+ case NGM_NETFLOW_SETIFINDEX:
+ {
+ struct ng_netflow_setifindex *set;
+ struct ng_netflow_iface *iface;
+
+ if (msg->header.arglen != sizeof(struct ng_netflow_setifindex))
+ ERROUT(EINVAL);
+
+ set = (struct ng_netflow_setifindex *)msg->data;
+ iface = &priv->ifaces[set->iface];
+
+ /* connected iface? */
+ if (iface->hook == NULL)
+ ERROUT(EINVAL);
+
+ iface->info.ifinfo_index = set->index;
+
+ break;
+ }
+ case NGM_NETFLOW_SETTIMEOUTS:
+ {
+ struct ng_netflow_settimeouts *set;
+
+ if (msg->header.arglen != sizeof(struct ng_netflow_settimeouts))
+ ERROUT(EINVAL);
+
+ set = (struct ng_netflow_settimeouts *)msg->data;
+
+ priv->info.nfinfo_inact_t = set->inactive_timeout;
+ priv->info.nfinfo_act_t = set->active_timeout;
+
+ break;
+ }
+ case NGM_NETFLOW_SHOW:
+ {
+ uint32_t *last;
+
+ if (msg->header.arglen != sizeof(uint32_t))
+ ERROUT(EINVAL);
+
+ last = (uint32_t *)msg->data;
+
+ NG_MKRESPONSE(resp, msg, NGRESP_SIZE, M_NOWAIT);
+
+ if (!resp)
+ ERROUT(ENOMEM);
+
+ error = ng_netflow_flow_show(priv, *last, resp);
+
+ break;
+ }
+ default:
+ ERROUT(EINVAL); /* unknown command */
+ break;
+ }
+ break;
+ default:
+ ERROUT(EINVAL); /* incorrect cookie */
+ break;
+ }
+
+ /*
+ * Take care of synchronous response, if any.
+ * Free memory and return.
+ */
+done:
+ NG_RESPOND_MSG(error, node, item, resp);
+ NG_FREE_MSG(msg);
+
+ return (error);
+}
+
+/* Receive data on hook. */
+static int
+ng_netflow_rcvdata (hook_p hook, item_p item)
+{
+ const node_p node = NG_HOOK_NODE(hook);
+ const priv_p priv = NG_NODE_PRIVATE(node);
+ const iface_p iface = NG_HOOK_PRIVATE(hook);
+ struct mbuf *m;
+ int error = 0;
+
+ NGI_GET_M(item, m);
+ if (hook == priv->export) {
+ /*
+ * Data arrived on export hook.
+ * This must not happen.
+ */
+ printf("ng_netflow: incoming data on export hook!\n");
+ ERROUT(EINVAL);
+ };
+
+ /* increase counters */
+ iface->info.ifinfo_packets++;
+
+ switch (iface->info.ifinfo_dlt) {
+ case DLT_EN10MB: /* Ethernet */
+ {
+ struct ether_header *eh;
+ uint16_t etype;
+
+ if (CHECK_MLEN(m, (sizeof(struct ether_header))))
+ ERROUT(EINVAL);
+
+ if (CHECK_PULLUP(m, (sizeof(struct ether_header))))
+ ERROUT(ENOBUFS);
+
+ eh = mtod(m, struct ether_header *);
+
+ /* make sure this is IP frame */
+ etype = ntohs(eh->ether_type);
+ switch (etype) {
+ case ETHERTYPE_IP:
+ m_adj(m, sizeof(struct ether_header));
+ break;
+ default:
+ ERROUT(EINVAL); /* ignore this frame */
+ }
+
+ break;
+ }
+ case DLT_RAW:
+ break;
+ default:
+ ERROUT(EINVAL);
+ break;
+ }
+
+ if (CHECK_MLEN(m, sizeof(struct ip)))
+ ERROUT(EINVAL);
+
+ if (CHECK_PULLUP(m, sizeof(struct ip)))
+ ERROUT(ENOBUFS);
+
+ error = ng_netflow_flow_add(priv, &m, iface);
+
+done:
+ if (m) {
+ if (item)
+ NG_FREE_ITEM(item);
+ NG_FREE_M(m);
+ }
+
+ return (error);
+}
+
+/* We will be shut down in a moment */
+static int
+ng_netflow_close(node_p node)
+{
+ const priv_p priv = NG_NODE_PRIVATE(node);
+
+ callout_drain(&priv->exp_callout);
+ ng_netflow_cache_flush(priv);
+
+ return (0);
+}
+
+/* Do local shutdown processing. */
+static int
+ng_netflow_rmnode(node_p node)
+{
+ const priv_p priv = NG_NODE_PRIVATE(node);
+
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_UNREF(priv->node);
+
+ FREE(priv, M_NETGRAPH);
+
+ return (0);
+}
+
+/* Hook disconnection. */
+static int
+ng_netflow_disconnect(hook_p hook)
+{
+ node_p node = NG_HOOK_NODE(hook);
+ priv_p priv = NG_NODE_PRIVATE(node);
+ iface_p iface = NG_HOOK_PRIVATE(hook);
+
+ if (iface != NULL)
+ iface->hook = NULL;
+
+ /* if export hook disconnected stop running expire(). */
+ if (hook == priv->export) {
+ callout_drain(&priv->exp_callout);
+ priv->export = NULL;
+ }
+
+ /* Removal of the last link destroys the node. */
+ if (NG_NODE_NUMHOOKS(node) == 0)
+ ng_rmnode_self(node);
+
+ return (0);
+}
OpenPOWER on IntegriCloud