summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/ipfw/ipfw2.c22
-rw-r--r--sys/netgraph/ng_ipfw.c321
-rw-r--r--sys/netgraph/ng_ipfw.h50
-rw-r--r--sys/netinet/ip_fw.h7
-rw-r--r--sys/netinet/ip_fw2.c23
-rw-r--r--sys/netinet/ip_fw_pfil.c47
6 files changed, 470 insertions, 0 deletions
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 12f1432..c495c3a 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -198,6 +198,8 @@ enum tokens {
TOK_QUEUE,
TOK_DIVERT,
TOK_TEE,
+ TOK_NETGRAPH,
+ TOK_NGTEE,
TOK_FORWARD,
TOK_SKIPTO,
TOK_DENY,
@@ -300,6 +302,8 @@ struct _s_x rule_actions[] = {
{ "queue", TOK_QUEUE },
{ "divert", TOK_DIVERT },
{ "tee", TOK_TEE },
+ { "netgraph", TOK_NETGRAPH },
+ { "ngtee", TOK_NGTEE },
{ "fwd", TOK_FORWARD },
{ "forward", TOK_FORWARD },
{ "skipto", TOK_SKIPTO },
@@ -1193,6 +1197,14 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
printf("tee %u", cmd->arg1);
break;
+ case O_NETGRAPH:
+ printf("netgraph %u", cmd->arg1);
+ break;
+
+ case O_NGTEE:
+ printf("ngtee %u", cmd->arg1);
+ break;
+
case O_FORWARD_IP:
{
ipfw_insn_sa *s = (ipfw_insn_sa *)cmd;
@@ -3123,6 +3135,16 @@ add(int ac, char *av[])
ac--; av++;
break;
+ case TOK_NETGRAPH:
+ case TOK_NGTEE:
+ action->opcode = (i == TOK_NETGRAPH ) ? O_NETGRAPH : O_NGTEE;
+ NEED1("missing netgraph cookie");
+ action->arg1 = strtoul(*av, NULL, 0);
+ if (action->arg1 == 0)
+ errx(EX_DATAERR, "illegal netgraph cookie");
+ ac--; av++;
+ break;
+
case TOK_FORWARD: {
ipfw_insn_sa *p = (ipfw_insn_sa *)action;
char *s, *end;
diff --git a/sys/netgraph/ng_ipfw.c b/sys/netgraph/ng_ipfw.c
new file mode 100644
index 0000000..96df884
--- /dev/null
+++ b/sys/netgraph/ng_ipfw.c
@@ -0,0 +1,321 @@
+/*-
+ * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_fw.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_parse.h>
+#include <netgraph/ng_ipfw.h>
+#include <netgraph/netgraph.h>
+
+static int ng_ipfw_mod_event(module_t mod, int event, void *data);
+static ng_constructor_t ng_ipfw_constructor;
+static ng_shutdown_t ng_ipfw_shutdown;
+static ng_newhook_t ng_ipfw_newhook;
+static ng_connect_t ng_ipfw_connect;
+static ng_findhook_t ng_ipfw_findhook;
+static ng_rcvdata_t ng_ipfw_rcvdata;
+static ng_disconnect_t ng_ipfw_disconnect;
+
+static hook_p ng_ipfw_findhook1(node_p, u_int16_t );
+static int ng_ipfw_input(struct mbuf **, int, struct ip_fw_args *,
+ int);
+
+/* We have only one node */
+static node_p fw_node;
+
+/* Netgraph node type descriptor */
+static struct ng_type ng_ipfw_typestruct = {
+ .version = NG_ABI_VERSION,
+ .name = NG_IPFW_NODE_TYPE,
+ .mod_event = ng_ipfw_mod_event,
+ .constructor = ng_ipfw_constructor,
+ .shutdown = ng_ipfw_shutdown,
+ .newhook = ng_ipfw_newhook,
+ .connect = ng_ipfw_connect,
+ .findhook = ng_ipfw_findhook,
+ .rcvdata = ng_ipfw_rcvdata,
+ .disconnect = ng_ipfw_disconnect,
+};
+NETGRAPH_INIT(ipfw, &ng_ipfw_typestruct);
+MODULE_DEPEND(ng_ipfw, ipfw, 2, 2, 2);
+
+/* Information we store for each hook */
+struct ng_ipfw_hook_priv {
+ hook_p hook;
+ u_int16_t rulenum;
+};
+typedef struct ng_ipfw_hook_priv *hpriv_p;
+
+static int
+ng_ipfw_mod_event(module_t mod, int event, void *data)
+{
+ int error = 0;
+
+ switch (event) {
+ case MOD_LOAD:
+
+ if (ng_ipfw_input_p != NULL) {
+ error = EEXIST;
+ break;
+ }
+
+ /* Setup node without any private data */
+ if ((error = ng_make_node_common(&ng_ipfw_typestruct, &fw_node))
+ != 0) {
+ log(LOG_ERR, "%s: can't create ng_ipfw node", __func__);
+ break;
+ };
+
+ /* Try to name node */
+ if (ng_name_node(fw_node, "ipfw") != 0)
+ log(LOG_WARNING, "%s: failed to name node \"ipfw\"",
+ __func__);
+
+ /* Register hook */
+ ng_ipfw_input_p = ng_ipfw_input;
+ break;
+
+ case MOD_UNLOAD:
+ /*
+ * This won't happen if a node exists.
+ * ng_ipfw_input_p is already cleared.
+ */
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+static int
+ng_ipfw_constructor(node_p node)
+{
+ return (EINVAL); /* Only one node */
+}
+
+static int
+ng_ipfw_newhook(node_p node, hook_p hook, const char *name)
+{
+ hpriv_p hpriv;
+ u_int16_t rulenum;
+ const char *cp;
+ char c;
+ int len;
+
+ /* Check that name contains only digits */
+ for (len = strlen(name), cp = name, c = *cp; len > 0; c =*++cp, len--)
+ if (!isdigit(c))
+ return (EINVAL);
+
+ /* Convert it to integer */
+ if ((rulenum = (u_int16_t)strtol(name, NULL, 10)) == 0)
+ return (EINVAL);
+
+ /* Allocate memory for this hook's private data */
+ MALLOC(hpriv, hpriv_p, sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
+ if (hpriv== NULL)
+ return (ENOMEM);
+
+ hpriv->hook = hook;
+ hpriv->rulenum = rulenum;
+
+ NG_HOOK_SET_PRIVATE(hook, hpriv);
+
+ return(0);
+}
+
+/*
+ * Set hooks into queueing mode, to avoid recursion between
+ * netgraph layer and ip_{input,output}.
+ */
+static int
+ng_ipfw_connect(hook_p hook)
+{
+ NG_HOOK_FORCE_QUEUE(hook);
+ return (0);
+}
+
+/* Look up hook by name */
+hook_p
+ng_ipfw_findhook(node_p node, const char *name)
+{
+ u_int16_t n; /* numeric representation of hook */
+
+ if ((n = (u_int16_t)strtol(name, NULL, 10)) == 0)
+ return NULL;
+ return ng_ipfw_findhook1(node, n);
+}
+
+/* Look up hook by rule number */
+static hook_p
+ng_ipfw_findhook1(node_p node, u_int16_t rulenum)
+{
+ hook_p hook;
+ hpriv_p hpriv;
+
+ LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
+ hpriv = NG_HOOK_PRIVATE(hook);
+ if (NG_HOOK_IS_VALID(hook) && (hpriv->rulenum == rulenum))
+ return (hook);
+ }
+
+ return (NULL);
+}
+
+
+static int
+ng_ipfw_rcvdata(hook_p hook, item_p item)
+{
+ struct ng_ipfw_tag *ngit;
+ struct mbuf *m;
+
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ if ((ngit = (struct ng_ipfw_tag *)m_tag_locate(m, NGM_IPFW_COOKIE, 0,
+ NULL)) == NULL) {
+ NG_FREE_M(m);
+ return (EINVAL); /* XXX: find smth better */
+ };
+
+ switch (ngit->dir) {
+ case NG_IPFW_OUT:
+ return ip_output(m, NULL, NULL, ngit->flags, NULL, NULL);
+
+ case NG_IPFW_IN:
+ {
+ struct ip *ip;
+
+ ip = mtod(m, struct ip *);
+ ip->ip_len = htons(ip->ip_len);
+ ip->ip_off = htons(ip->ip_off);
+ ip_input(m);
+
+ return (0);
+ }
+ default:
+ panic("ng_ipfw_rcvdata: bad dir %u", ngit->dir);
+ }
+
+ /* not reached */
+ return (0);
+}
+
+static int
+ng_ipfw_input(struct mbuf **m0, int dir, struct ip_fw_args *fwa, int tee)
+{
+ struct mbuf *m;
+ struct ng_ipfw_tag *ngit;
+ hook_p hook;
+ int error = 0;
+
+ /*
+ * Node must be loaded and corresponding hook must be present.
+ */
+ if (fw_node == NULL ||
+ (hook = ng_ipfw_findhook1(fw_node, fwa->cookie)) == NULL) {
+ if (tee == 0)
+ m_freem(*m0);
+ return (ESRCH); /* no hook associated with this rule */
+ }
+
+ /*
+ * We have two modes: in normal mode we add a tag to packet, which is
+ * important to return packet back to IP stack. In tee mode we make
+ * a copy of a packet and forward it into netgraph without a tag.
+ */
+ if (tee == 0) {
+ m = *m0;
+ *m0 = NULL; /* it belongs now to netgraph */
+
+ if ((ngit = (struct ng_ipfw_tag *)m_tag_alloc(NGM_IPFW_COOKIE,
+ 0, TAGSIZ, M_NOWAIT|M_ZERO)) == NULL) {
+ m_freem(m);
+ return (ENOMEM);
+ }
+ ngit->rule = fwa->rule;
+ ngit->dir = dir;
+ ngit->ifp = fwa->oif;
+ if (dir == NG_IPFW_OUT)
+ ngit->flags = fwa->flags;
+ m_tag_prepend(m, &ngit->mt);
+
+ } else
+ if ((m = m_dup(*m0, M_DONTWAIT)) == NULL)
+ return (ENOMEM); /* which is ignored */
+
+ NG_SEND_DATA_ONLY(error, hook, m);
+
+ return (error);
+}
+
+static int
+ng_ipfw_shutdown(node_p node)
+{
+
+ /*
+ * After our single node has been removed,
+ * the only thing that can be done is
+ * 'kldunload ng_ipfw.ko'
+ */
+ ng_ipfw_input_p = NULL;
+ NG_NODE_UNREF(node);
+ return (0);
+}
+
+static int
+ng_ipfw_disconnect(hook_p hook)
+{
+ const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
+
+ FREE(hpriv, M_NETGRAPH);
+ NG_HOOK_SET_PRIVATE(hook, NULL);
+
+ return (0);
+}
diff --git a/sys/netgraph/ng_ipfw.h b/sys/netgraph/ng_ipfw.h
new file mode 100644
index 0000000..e2ff698
--- /dev/null
+++ b/sys/netgraph/ng_ipfw.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#define NG_IPFW_NODE_TYPE "ipfw"
+#define NGM_IPFW_COOKIE 1105988990
+
+#ifdef _KERNEL
+
+typedef int ng_ipfw_input_t(struct mbuf **, int, struct ip_fw_args *, int);
+extern ng_ipfw_input_t *ng_ipfw_input_p;
+#define NG_IPFW_LOADED (ng_ipfw_input_p != NULL)
+
+struct ng_ipfw_tag {
+ struct m_tag mt; /* tag header */
+ struct ip_fw *rule; /* matching rule */
+ struct ifnet *ifp; /* interface, for ip_output */
+ int dir;
+#define NG_IPFW_OUT 0
+#define NG_IPFW_IN 1
+ int flags; /* flags, for ip_output (IPv6 ?) */
+};
+
+#define TAGSIZ (sizeof(struct ng_ipfw_tag) - sizeof(struct m_tag))
+
+#endif /* _KERNEL */
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index de92951..0da6f43 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -138,6 +138,12 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_DIVERTED, /* arg1=bitmap (1:loop, 2:out) */
O_TCPDATALEN, /* arg1 = tcp data len */
+ /*
+ * actions for ng_ipfw
+ */
+ O_NETGRAPH, /* send to ng_ipfw */
+ O_NGTEE, /* copy to ng_ipfw */
+
O_LAST_OPCODE /* not an opcode! */
};
@@ -425,6 +431,7 @@ enum {
IP_FW_TEE,
IP_FW_DUMMYNET,
IP_FW_NETGRAPH,
+ IP_FW_NGTEE,
};
/* flags for divert mtag */
diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c
index 172de80..57aa13c 100644
--- a/sys/netinet/ip_fw2.c
+++ b/sys/netinet/ip_fw2.c
@@ -77,6 +77,9 @@
#include <netinet/tcpip.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
+
+#include <netgraph/ng_ipfw.h>
+
#include <altq/if_altq.h>
#ifdef IPSEC
@@ -649,6 +652,14 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ether_header *eh,
sa->sa.sin_port);
}
break;
+ case O_NETGRAPH:
+ snprintf(SNPARGS(action2, 0), "Netgraph %d",
+ cmd->arg1);
+ break;
+ case O_NGTEE:
+ snprintf(SNPARGS(action2, 0), "Ngtee %d",
+ cmd->arg1);
+ break;
default:
action = "UNKNOWN";
break;
@@ -2528,6 +2539,14 @@ check_body:
retval = IP_FW_PASS;
goto done;
+ case O_NETGRAPH:
+ case O_NGTEE:
+ args->rule = f; /* report matching rule */
+ args->cookie = cmd->arg1;
+ retval = (cmd->opcode == O_NETGRAPH) ?
+ IP_FW_NETGRAPH : IP_FW_NGTEE;
+ goto done;
+
default:
panic("-- unknown opcode %d\n", cmd->opcode);
} /* end of switch() on opcodes */
@@ -3108,6 +3127,10 @@ check_ipfw_struct(struct ip_fw *rule, int size)
case O_TEE:
if (ip_divert_ptr == NULL)
return EINVAL;
+ case O_NETGRAPH:
+ case O_NGTEE:
+ if (!NG_IPFW_LOADED)
+ return EINVAL;
case O_FORWARD_MAC: /* XXX not implemented yet */
case O_CHECK_STATE:
case O_COUNT:
diff --git a/sys/netinet/ip_fw_pfil.c b/sys/netinet/ip_fw_pfil.c
index 22308bb..0103d8c 100644
--- a/sys/netinet/ip_fw_pfil.c
+++ b/sys/netinet/ip_fw_pfil.c
@@ -59,6 +59,8 @@
#include <netinet/ip_divert.h>
#include <netinet/ip_dummynet.h>
+#include <netgraph/ng_ipfw.h>
+
#include <machine/in_cksum.h>
static int ipfw_pfil_hooked = 0;
@@ -69,6 +71,9 @@ ip_dn_ruledel_t *ip_dn_ruledel_ptr = NULL;
/* Divert hooks. */
ip_divert_packet_t *ip_divert_ptr = NULL;
+/* ng_ipfw hooks. */
+ng_ipfw_input_t *ng_ipfw_input_p = NULL;
+
/* Forward declarations. */
static int ipfw_divert(struct mbuf **, int, int);
#define DIV_DIR_IN 1
@@ -79,6 +84,7 @@ ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
struct inpcb *inp)
{
struct ip_fw_args args;
+ struct ng_ipfw_tag *ng_tag;
struct m_tag *dn_tag;
int ipfw = 0;
int divert;
@@ -104,6 +110,15 @@ ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
m_tag_delete(*m0, dn_tag);
}
+ ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
+ NULL);
+ if (ng_tag != NULL) {
+ KASSERT(ng_tag->dir == NG_IPFW_IN,
+ ("ng_ipfw tag with wrong direction"));
+ args.rule = ng_tag->rule;
+ m_tag_delete(*m0, (struct m_tag *)ng_tag);
+ }
+
again:
args.m = *m0;
args.inp = inp;
@@ -156,6 +171,17 @@ again:
} else
goto again; /* continue with packet */
+ case IP_FW_NGTEE:
+ if (!NG_IPFW_LOADED)
+ goto drop;
+ (void)ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 1);
+ goto again; /* continue with packet */
+
+ case IP_FW_NETGRAPH:
+ if (!NG_IPFW_LOADED)
+ goto drop;
+ return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0);
+
default:
KASSERT(0, ("%s: unknown retval", __func__));
}
@@ -174,6 +200,7 @@ ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
struct inpcb *inp)
{
struct ip_fw_args args;
+ struct ng_ipfw_tag *ng_tag;
struct m_tag *dn_tag;
int ipfw = 0;
int divert;
@@ -199,6 +226,15 @@ ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
m_tag_delete(*m0, dn_tag);
}
+ ng_tag = (struct ng_ipfw_tag *)m_tag_locate(*m0, NGM_IPFW_COOKIE, 0,
+ NULL);
+ if (ng_tag != NULL) {
+ KASSERT(ng_tag->dir == NG_IPFW_OUT,
+ ("ng_ipfw tag with wrong direction"));
+ args.rule = ng_tag->rule;
+ m_tag_delete(*m0, (struct m_tag *)ng_tag);
+ }
+
again:
args.m = *m0;
args.oif = ifp;
@@ -258,6 +294,17 @@ again:
} else
goto again; /* continue with packet */
+ case IP_FW_NGTEE:
+ if (!NG_IPFW_LOADED)
+ goto drop;
+ (void)ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 1);
+ goto again; /* continue with packet */
+
+ case IP_FW_NETGRAPH:
+ if (!NG_IPFW_LOADED)
+ goto drop;
+ return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0);
+
default:
KASSERT(0, ("%s: unknown retval", __func__));
}
OpenPOWER on IntegriCloud