diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 12f14328f895..c495c3a99c65 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 000000000000..96df884ed743 --- /dev/null +++ b/sys/netgraph/ng_ipfw.c @@ -0,0 +1,321 @@ +/*- + * Copyright 2005, Gleb Smirnoff + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 000000000000..e2ff69821402 --- /dev/null +++ b/sys/netgraph/ng_ipfw.h @@ -0,0 +1,50 @@ +/*- + * Copyright 2005, Gleb Smirnoff + * 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 de9295119675..0da6f43c5a11 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 172de80f56a5..57aa13c897f2 100644 --- a/sys/netinet/ip_fw2.c +++ b/sys/netinet/ip_fw2.c @@ -77,6 +77,9 @@ #include #include #include + +#include + #include #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 @@ ipfw_chk(struct ip_fw_args *args) 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 22308bb66b7b..0103d8cddfae 100644 --- a/sys/netinet/ip_fw_pfil.c +++ b/sys/netinet/ip_fw_pfil.c @@ -59,6 +59,8 @@ #include #include +#include + #include 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 @@ ipfw_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, } 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 @@ ipfw_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, } 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__)); }