1141351Sglebius/*- 2141351Sglebius * Copyright 2005, Gleb Smirnoff <glebius@FreeBSD.org> 3141351Sglebius * All rights reserved. 4141351Sglebius * 5141351Sglebius * Redistribution and use in source and binary forms, with or without 6141351Sglebius * modification, are permitted provided that the following conditions 7141351Sglebius * are met: 8141351Sglebius * 1. Redistributions of source code must retain the above copyright 9141351Sglebius * notice, this list of conditions and the following disclaimer. 10141351Sglebius * 2. Redistributions in binary form must reproduce the above copyright 11141351Sglebius * notice, this list of conditions and the following disclaimer in the 12141351Sglebius * documentation and/or other materials provided with the distribution. 13141351Sglebius * 14141351Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15141351Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16141351Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17141351Sglebius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18141351Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19141351Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20141351Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21141351Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22141351Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23141351Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24141351Sglebius * SUCH DAMAGE. 25141351Sglebius * 26141351Sglebius * $FreeBSD$ 27141351Sglebius */ 28141351Sglebius 29225586Sae#include "opt_inet.h" 30225586Sae#include "opt_inet6.h" 31225586Sae 32141351Sglebius#include <sys/param.h> 33141351Sglebius#include <sys/systm.h> 34141351Sglebius#include <sys/kernel.h> 35185895Szec#include <sys/lock.h> 36141351Sglebius#include <sys/mbuf.h> 37141351Sglebius#include <sys/malloc.h> 38141351Sglebius#include <sys/ctype.h> 39141351Sglebius#include <sys/errno.h> 40185895Szec#include <sys/rwlock.h> 41141351Sglebius#include <sys/socket.h> 42141351Sglebius#include <sys/syslog.h> 43141351Sglebius 44141351Sglebius#include <net/if.h> 45257176Sglebius#include <net/if_var.h> 46141351Sglebius 47141351Sglebius#include <netinet/in.h> 48141351Sglebius#include <netinet/in_systm.h> 49141351Sglebius#include <netinet/in_var.h> 50201748Sluigi#include <netinet/ip_var.h> 51141351Sglebius#include <netinet/ip_fw.h> 52141351Sglebius#include <netinet/ip.h> 53225586Sae#include <netinet/ip6.h> 54225586Sae#include <netinet6/ip6_var.h> 55141351Sglebius 56240494Sglebius#include <netpfil/ipfw/ip_fw_private.h> 57240494Sglebius 58141351Sglebius#include <netgraph/ng_message.h> 59141351Sglebius#include <netgraph/ng_parse.h> 60141351Sglebius#include <netgraph/ng_ipfw.h> 61141351Sglebius#include <netgraph/netgraph.h> 62141351Sglebius 63141351Sglebiusstatic int ng_ipfw_mod_event(module_t mod, int event, void *data); 64141351Sglebiusstatic ng_constructor_t ng_ipfw_constructor; 65141351Sglebiusstatic ng_shutdown_t ng_ipfw_shutdown; 66141351Sglebiusstatic ng_newhook_t ng_ipfw_newhook; 67141351Sglebiusstatic ng_connect_t ng_ipfw_connect; 68141351Sglebiusstatic ng_findhook_t ng_ipfw_findhook; 69141351Sglebiusstatic ng_rcvdata_t ng_ipfw_rcvdata; 70141351Sglebiusstatic ng_disconnect_t ng_ipfw_disconnect; 71141351Sglebius 72141351Sglebiusstatic hook_p ng_ipfw_findhook1(node_p, u_int16_t ); 73141351Sglebiusstatic int ng_ipfw_input(struct mbuf **, int, struct ip_fw_args *, 74141351Sglebius int); 75141351Sglebius 76141351Sglebius/* We have only one node */ 77141351Sglebiusstatic node_p fw_node; 78141351Sglebius 79141351Sglebius/* Netgraph node type descriptor */ 80141351Sglebiusstatic struct ng_type ng_ipfw_typestruct = { 81141351Sglebius .version = NG_ABI_VERSION, 82141351Sglebius .name = NG_IPFW_NODE_TYPE, 83141351Sglebius .mod_event = ng_ipfw_mod_event, 84141351Sglebius .constructor = ng_ipfw_constructor, 85141351Sglebius .shutdown = ng_ipfw_shutdown, 86141351Sglebius .newhook = ng_ipfw_newhook, 87141351Sglebius .connect = ng_ipfw_connect, 88141351Sglebius .findhook = ng_ipfw_findhook, 89141351Sglebius .rcvdata = ng_ipfw_rcvdata, 90141351Sglebius .disconnect = ng_ipfw_disconnect, 91141351Sglebius}; 92141351SglebiusNETGRAPH_INIT(ipfw, &ng_ipfw_typestruct); 93272840SmelifaroMODULE_DEPEND(ng_ipfw, ipfw, 3, 3, 3); 94141351Sglebius 95141351Sglebius/* Information we store for each hook */ 96141351Sglebiusstruct ng_ipfw_hook_priv { 97141351Sglebius hook_p hook; 98141351Sglebius u_int16_t rulenum; 99141351Sglebius}; 100141351Sglebiustypedef struct ng_ipfw_hook_priv *hpriv_p; 101141351Sglebius 102141351Sglebiusstatic int 103141351Sglebiusng_ipfw_mod_event(module_t mod, int event, void *data) 104141351Sglebius{ 105141351Sglebius int error = 0; 106141351Sglebius 107141351Sglebius switch (event) { 108141351Sglebius case MOD_LOAD: 109141351Sglebius 110141351Sglebius if (ng_ipfw_input_p != NULL) { 111141351Sglebius error = EEXIST; 112141351Sglebius break; 113141351Sglebius } 114141351Sglebius 115141351Sglebius /* Setup node without any private data */ 116141351Sglebius if ((error = ng_make_node_common(&ng_ipfw_typestruct, &fw_node)) 117141351Sglebius != 0) { 118141351Sglebius log(LOG_ERR, "%s: can't create ng_ipfw node", __func__); 119141351Sglebius break; 120297793Spfg } 121141351Sglebius 122141351Sglebius /* Try to name node */ 123141351Sglebius if (ng_name_node(fw_node, "ipfw") != 0) 124141351Sglebius log(LOG_WARNING, "%s: failed to name node \"ipfw\"", 125141351Sglebius __func__); 126141351Sglebius 127141351Sglebius /* Register hook */ 128141351Sglebius ng_ipfw_input_p = ng_ipfw_input; 129141351Sglebius break; 130141351Sglebius 131141351Sglebius case MOD_UNLOAD: 132141351Sglebius /* 133141351Sglebius * This won't happen if a node exists. 134141351Sglebius * ng_ipfw_input_p is already cleared. 135141351Sglebius */ 136141351Sglebius break; 137141351Sglebius 138141351Sglebius default: 139141351Sglebius error = EOPNOTSUPP; 140141351Sglebius break; 141141351Sglebius } 142141351Sglebius 143141351Sglebius return (error); 144141351Sglebius} 145141351Sglebius 146141351Sglebiusstatic int 147141351Sglebiusng_ipfw_constructor(node_p node) 148141351Sglebius{ 149141351Sglebius return (EINVAL); /* Only one node */ 150141351Sglebius} 151141351Sglebius 152141351Sglebiusstatic int 153141351Sglebiusng_ipfw_newhook(node_p node, hook_p hook, const char *name) 154141351Sglebius{ 155141351Sglebius hpriv_p hpriv; 156141351Sglebius u_int16_t rulenum; 157141351Sglebius const char *cp; 158141451Sglebius char *endptr; 159141351Sglebius 160146745Sglebius /* Protect from leading zero */ 161146745Sglebius if (name[0] == '0' && name[1] != '\0') 162146745Sglebius return (EINVAL); 163146745Sglebius 164141351Sglebius /* Check that name contains only digits */ 165141451Sglebius for (cp = name; *cp != '\0'; cp++) 166146745Sglebius if (!isdigit(*cp)) 167141351Sglebius return (EINVAL); 168141351Sglebius 169141351Sglebius /* Convert it to integer */ 170141451Sglebius rulenum = (u_int16_t)strtol(name, &endptr, 10); 171141451Sglebius if (*endptr != '\0') 172141351Sglebius return (EINVAL); 173141351Sglebius 174141351Sglebius /* Allocate memory for this hook's private data */ 175184205Sdes hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO); 176141351Sglebius if (hpriv== NULL) 177141351Sglebius return (ENOMEM); 178141351Sglebius 179141351Sglebius hpriv->hook = hook; 180141351Sglebius hpriv->rulenum = rulenum; 181141351Sglebius 182141351Sglebius NG_HOOK_SET_PRIVATE(hook, hpriv); 183141351Sglebius 184141351Sglebius return(0); 185141351Sglebius} 186141351Sglebius 187141351Sglebius/* 188141351Sglebius * Set hooks into queueing mode, to avoid recursion between 189141351Sglebius * netgraph layer and ip_{input,output}. 190141351Sglebius */ 191141351Sglebiusstatic int 192141351Sglebiusng_ipfw_connect(hook_p hook) 193141351Sglebius{ 194141351Sglebius NG_HOOK_FORCE_QUEUE(hook); 195141351Sglebius return (0); 196141351Sglebius} 197141351Sglebius 198141351Sglebius/* Look up hook by name */ 199230214Sglebiusstatic hook_p 200141351Sglebiusng_ipfw_findhook(node_p node, const char *name) 201141351Sglebius{ 202141351Sglebius u_int16_t n; /* numeric representation of hook */ 203141451Sglebius char *endptr; 204141351Sglebius 205141451Sglebius n = (u_int16_t)strtol(name, &endptr, 10); 206141451Sglebius if (*endptr != '\0') 207141351Sglebius return NULL; 208141351Sglebius return ng_ipfw_findhook1(node, n); 209141351Sglebius} 210141351Sglebius 211141351Sglebius/* Look up hook by rule number */ 212141351Sglebiusstatic hook_p 213141351Sglebiusng_ipfw_findhook1(node_p node, u_int16_t rulenum) 214141351Sglebius{ 215141351Sglebius hook_p hook; 216141351Sglebius hpriv_p hpriv; 217141351Sglebius 218141351Sglebius LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 219141351Sglebius hpriv = NG_HOOK_PRIVATE(hook); 220141351Sglebius if (NG_HOOK_IS_VALID(hook) && (hpriv->rulenum == rulenum)) 221141351Sglebius return (hook); 222141351Sglebius } 223141351Sglebius 224141351Sglebius return (NULL); 225141351Sglebius} 226141351Sglebius 227141351Sglebius 228141351Sglebiusstatic int 229141351Sglebiusng_ipfw_rcvdata(hook_p hook, item_p item) 230141351Sglebius{ 231209633Sglebius struct m_tag *tag; 232209633Sglebius struct ipfw_rule_ref *r; 233141351Sglebius struct mbuf *m; 234225586Sae struct ip *ip; 235141351Sglebius 236141351Sglebius NGI_GET_M(item, m); 237141351Sglebius NG_FREE_ITEM(item); 238141351Sglebius 239209633Sglebius tag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL); 240201527Sluigi if (tag == NULL) { 241141351Sglebius NG_FREE_M(m); 242141351Sglebius return (EINVAL); /* XXX: find smth better */ 243297793Spfg } 244141351Sglebius 245225586Sae if (m->m_len < sizeof(struct ip) && 246225586Sae (m = m_pullup(m, sizeof(struct ip))) == NULL) 247226186Smelifaro return (ENOBUFS); 248225586Sae 249225586Sae ip = mtod(m, struct ip *); 250225586Sae 251209633Sglebius r = (struct ipfw_rule_ref *)(tag + 1); 252209633Sglebius if (r->info & IPFW_INFO_IN) { 253225586Sae switch (ip->ip_v) { 254225586Sae#ifdef INET 255225586Sae case IPVERSION: 256225586Sae ip_input(m); 257226186Smelifaro return (0); 258225586Sae#endif 259225586Sae#ifdef INET6 260225586Sae case IPV6_VERSION >> 4: 261225586Sae ip6_input(m); 262226186Smelifaro return (0); 263225586Sae#endif 264225586Sae } 265201527Sluigi } else { 266225586Sae switch (ip->ip_v) { 267225586Sae#ifdef INET 268225586Sae case IPVERSION: 269225586Sae return (ip_output(m, NULL, NULL, IP_FORWARDING, 270225586Sae NULL, NULL)); 271225586Sae#endif 272225586Sae#ifdef INET6 273225586Sae case IPV6_VERSION >> 4: 274225586Sae return (ip6_output(m, NULL, NULL, 0, NULL, 275225586Sae NULL, NULL)); 276225586Sae#endif 277225586Sae } 278225586Sae } 279226186Smelifaro 280226186Smelifaro /* unknown IP protocol version */ 281226186Smelifaro NG_FREE_M(m); 282226186Smelifaro return (EPROTONOSUPPORT); 283141351Sglebius} 284141351Sglebius 285141351Sglebiusstatic int 286141351Sglebiusng_ipfw_input(struct mbuf **m0, int dir, struct ip_fw_args *fwa, int tee) 287141351Sglebius{ 288141351Sglebius struct mbuf *m; 289141699Sglebius struct ip *ip; 290141351Sglebius hook_p hook; 291141351Sglebius int error = 0; 292141351Sglebius 293141351Sglebius /* 294141351Sglebius * Node must be loaded and corresponding hook must be present. 295141351Sglebius */ 296141351Sglebius if (fw_node == NULL || 297209722Sglebius (hook = ng_ipfw_findhook1(fw_node, fwa->rule.info)) == NULL) 298141351Sglebius return (ESRCH); /* no hook associated with this rule */ 299141351Sglebius 300141351Sglebius /* 301141351Sglebius * We have two modes: in normal mode we add a tag to packet, which is 302141351Sglebius * important to return packet back to IP stack. In tee mode we make 303141351Sglebius * a copy of a packet and forward it into netgraph without a tag. 304141351Sglebius */ 305141351Sglebius if (tee == 0) { 306201527Sluigi struct m_tag *tag; 307201527Sluigi struct ipfw_rule_ref *r; 308141351Sglebius m = *m0; 309141351Sglebius *m0 = NULL; /* it belongs now to netgraph */ 310141351Sglebius 311201527Sluigi tag = m_tag_alloc(MTAG_IPFW_RULE, 0, sizeof(*r), 312201527Sluigi M_NOWAIT|M_ZERO); 313201527Sluigi if (tag == NULL) { 314141351Sglebius m_freem(m); 315141351Sglebius return (ENOMEM); 316141351Sglebius } 317201527Sluigi r = (struct ipfw_rule_ref *)(tag + 1); 318201527Sluigi *r = fwa->rule; 319210537Sglebius r->info &= IPFW_ONEPASS; /* keep this info */ 320210537Sglebius r->info |= dir ? IPFW_INFO_IN : IPFW_INFO_OUT; 321201527Sluigi m_tag_prepend(m, tag); 322141351Sglebius 323141351Sglebius } else 324243882Sglebius if ((m = m_dup(*m0, M_NOWAIT)) == NULL) 325141351Sglebius return (ENOMEM); /* which is ignored */ 326141351Sglebius 327141705Sglebius if (m->m_len < sizeof(struct ip) && 328141706Sglebius (m = m_pullup(m, sizeof(struct ip))) == NULL) 329141706Sglebius return (EINVAL); 330141705Sglebius 331141699Sglebius ip = mtod(m, struct ip *); 332141699Sglebius 333141351Sglebius NG_SEND_DATA_ONLY(error, hook, m); 334141351Sglebius 335141351Sglebius return (error); 336141351Sglebius} 337141351Sglebius 338141351Sglebiusstatic int 339141351Sglebiusng_ipfw_shutdown(node_p node) 340141351Sglebius{ 341141351Sglebius 342141351Sglebius /* 343141351Sglebius * After our single node has been removed, 344141351Sglebius * the only thing that can be done is 345141351Sglebius * 'kldunload ng_ipfw.ko' 346141351Sglebius */ 347141351Sglebius ng_ipfw_input_p = NULL; 348141351Sglebius NG_NODE_UNREF(node); 349141351Sglebius return (0); 350141351Sglebius} 351141351Sglebius 352141351Sglebiusstatic int 353141351Sglebiusng_ipfw_disconnect(hook_p hook) 354141351Sglebius{ 355141351Sglebius const hpriv_p hpriv = NG_HOOK_PRIVATE(hook); 356141351Sglebius 357184205Sdes free(hpriv, M_NETGRAPH); 358141351Sglebius NG_HOOK_SET_PRIVATE(hook, NULL); 359141351Sglebius 360141351Sglebius return (0); 361141351Sglebius} 362