ng_ipfw.c revision 200582
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: head/sys/netgraph/ng_ipfw.c 200582 2009-12-15 18:33:12Z luigi $ 27141351Sglebius */ 28141351Sglebius 29141351Sglebius#include <sys/param.h> 30141351Sglebius#include <sys/systm.h> 31141351Sglebius#include <sys/kernel.h> 32185895Szec#include <sys/lock.h> 33141351Sglebius#include <sys/mbuf.h> 34141351Sglebius#include <sys/malloc.h> 35141351Sglebius#include <sys/ctype.h> 36141351Sglebius#include <sys/errno.h> 37185895Szec#include <sys/rwlock.h> 38141351Sglebius#include <sys/socket.h> 39141351Sglebius#include <sys/syslog.h> 40141351Sglebius 41141351Sglebius#include <net/if.h> 42141351Sglebius 43141351Sglebius#include <netinet/in.h> 44141351Sglebius#include <netinet/in_systm.h> 45141351Sglebius#include <netinet/in_var.h> 46141351Sglebius#include <netinet/ip_fw.h> 47200582Sluigi#include <netinet/ipfw/ip_fw_private.h> 48141351Sglebius#include <netinet/ip.h> 49141351Sglebius#include <netinet/ip_var.h> 50141351Sglebius 51141351Sglebius#include <netgraph/ng_message.h> 52141351Sglebius#include <netgraph/ng_parse.h> 53141351Sglebius#include <netgraph/ng_ipfw.h> 54141351Sglebius#include <netgraph/netgraph.h> 55141351Sglebius 56141351Sglebiusstatic int ng_ipfw_mod_event(module_t mod, int event, void *data); 57141351Sglebiusstatic ng_constructor_t ng_ipfw_constructor; 58141351Sglebiusstatic ng_shutdown_t ng_ipfw_shutdown; 59141351Sglebiusstatic ng_newhook_t ng_ipfw_newhook; 60141351Sglebiusstatic ng_connect_t ng_ipfw_connect; 61141351Sglebiusstatic ng_findhook_t ng_ipfw_findhook; 62141351Sglebiusstatic ng_rcvdata_t ng_ipfw_rcvdata; 63141351Sglebiusstatic ng_disconnect_t ng_ipfw_disconnect; 64141351Sglebius 65141351Sglebiusstatic hook_p ng_ipfw_findhook1(node_p, u_int16_t ); 66141351Sglebiusstatic int ng_ipfw_input(struct mbuf **, int, struct ip_fw_args *, 67141351Sglebius int); 68141351Sglebius 69141351Sglebius/* We have only one node */ 70141351Sglebiusstatic node_p fw_node; 71141351Sglebius 72141351Sglebius/* Netgraph node type descriptor */ 73141351Sglebiusstatic struct ng_type ng_ipfw_typestruct = { 74141351Sglebius .version = NG_ABI_VERSION, 75141351Sglebius .name = NG_IPFW_NODE_TYPE, 76141351Sglebius .mod_event = ng_ipfw_mod_event, 77141351Sglebius .constructor = ng_ipfw_constructor, 78141351Sglebius .shutdown = ng_ipfw_shutdown, 79141351Sglebius .newhook = ng_ipfw_newhook, 80141351Sglebius .connect = ng_ipfw_connect, 81141351Sglebius .findhook = ng_ipfw_findhook, 82141351Sglebius .rcvdata = ng_ipfw_rcvdata, 83141351Sglebius .disconnect = ng_ipfw_disconnect, 84141351Sglebius}; 85141351SglebiusNETGRAPH_INIT(ipfw, &ng_ipfw_typestruct); 86141351SglebiusMODULE_DEPEND(ng_ipfw, ipfw, 2, 2, 2); 87141351Sglebius 88141351Sglebius/* Information we store for each hook */ 89141351Sglebiusstruct ng_ipfw_hook_priv { 90141351Sglebius hook_p hook; 91141351Sglebius u_int16_t rulenum; 92141351Sglebius}; 93141351Sglebiustypedef struct ng_ipfw_hook_priv *hpriv_p; 94141351Sglebius 95141351Sglebiusstatic int 96141351Sglebiusng_ipfw_mod_event(module_t mod, int event, void *data) 97141351Sglebius{ 98141351Sglebius int error = 0; 99141351Sglebius 100141351Sglebius switch (event) { 101141351Sglebius case MOD_LOAD: 102141351Sglebius 103141351Sglebius if (ng_ipfw_input_p != NULL) { 104141351Sglebius error = EEXIST; 105141351Sglebius break; 106141351Sglebius } 107141351Sglebius 108141351Sglebius /* Setup node without any private data */ 109141351Sglebius if ((error = ng_make_node_common(&ng_ipfw_typestruct, &fw_node)) 110141351Sglebius != 0) { 111141351Sglebius log(LOG_ERR, "%s: can't create ng_ipfw node", __func__); 112141351Sglebius break; 113141351Sglebius }; 114141351Sglebius 115141351Sglebius /* Try to name node */ 116141351Sglebius if (ng_name_node(fw_node, "ipfw") != 0) 117141351Sglebius log(LOG_WARNING, "%s: failed to name node \"ipfw\"", 118141351Sglebius __func__); 119141351Sglebius 120141351Sglebius /* Register hook */ 121141351Sglebius ng_ipfw_input_p = ng_ipfw_input; 122141351Sglebius break; 123141351Sglebius 124141351Sglebius case MOD_UNLOAD: 125141351Sglebius /* 126141351Sglebius * This won't happen if a node exists. 127141351Sglebius * ng_ipfw_input_p is already cleared. 128141351Sglebius */ 129141351Sglebius break; 130141351Sglebius 131141351Sglebius default: 132141351Sglebius error = EOPNOTSUPP; 133141351Sglebius break; 134141351Sglebius } 135141351Sglebius 136141351Sglebius return (error); 137141351Sglebius} 138141351Sglebius 139141351Sglebiusstatic int 140141351Sglebiusng_ipfw_constructor(node_p node) 141141351Sglebius{ 142141351Sglebius return (EINVAL); /* Only one node */ 143141351Sglebius} 144141351Sglebius 145141351Sglebiusstatic int 146141351Sglebiusng_ipfw_newhook(node_p node, hook_p hook, const char *name) 147141351Sglebius{ 148141351Sglebius hpriv_p hpriv; 149141351Sglebius u_int16_t rulenum; 150141351Sglebius const char *cp; 151141451Sglebius char *endptr; 152141351Sglebius 153146745Sglebius /* Protect from leading zero */ 154146745Sglebius if (name[0] == '0' && name[1] != '\0') 155146745Sglebius return (EINVAL); 156146745Sglebius 157141351Sglebius /* Check that name contains only digits */ 158141451Sglebius for (cp = name; *cp != '\0'; cp++) 159146745Sglebius if (!isdigit(*cp)) 160141351Sglebius return (EINVAL); 161141351Sglebius 162141351Sglebius /* Convert it to integer */ 163141451Sglebius rulenum = (u_int16_t)strtol(name, &endptr, 10); 164141451Sglebius if (*endptr != '\0') 165141351Sglebius return (EINVAL); 166141351Sglebius 167141351Sglebius /* Allocate memory for this hook's private data */ 168184205Sdes hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO); 169141351Sglebius if (hpriv== NULL) 170141351Sglebius return (ENOMEM); 171141351Sglebius 172141351Sglebius hpriv->hook = hook; 173141351Sglebius hpriv->rulenum = rulenum; 174141351Sglebius 175141351Sglebius NG_HOOK_SET_PRIVATE(hook, hpriv); 176141351Sglebius 177141351Sglebius return(0); 178141351Sglebius} 179141351Sglebius 180141351Sglebius/* 181141351Sglebius * Set hooks into queueing mode, to avoid recursion between 182141351Sglebius * netgraph layer and ip_{input,output}. 183141351Sglebius */ 184141351Sglebiusstatic int 185141351Sglebiusng_ipfw_connect(hook_p hook) 186141351Sglebius{ 187141351Sglebius NG_HOOK_FORCE_QUEUE(hook); 188141351Sglebius return (0); 189141351Sglebius} 190141351Sglebius 191141351Sglebius/* Look up hook by name */ 192141351Sglebiushook_p 193141351Sglebiusng_ipfw_findhook(node_p node, const char *name) 194141351Sglebius{ 195141351Sglebius u_int16_t n; /* numeric representation of hook */ 196141451Sglebius char *endptr; 197141351Sglebius 198141451Sglebius n = (u_int16_t)strtol(name, &endptr, 10); 199141451Sglebius if (*endptr != '\0') 200141351Sglebius return NULL; 201141351Sglebius return ng_ipfw_findhook1(node, n); 202141351Sglebius} 203141351Sglebius 204141351Sglebius/* Look up hook by rule number */ 205141351Sglebiusstatic hook_p 206141351Sglebiusng_ipfw_findhook1(node_p node, u_int16_t rulenum) 207141351Sglebius{ 208141351Sglebius hook_p hook; 209141351Sglebius hpriv_p hpriv; 210141351Sglebius 211141351Sglebius LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 212141351Sglebius hpriv = NG_HOOK_PRIVATE(hook); 213141351Sglebius if (NG_HOOK_IS_VALID(hook) && (hpriv->rulenum == rulenum)) 214141351Sglebius return (hook); 215141351Sglebius } 216141351Sglebius 217141351Sglebius return (NULL); 218141351Sglebius} 219141351Sglebius 220141351Sglebius 221141351Sglebiusstatic int 222141351Sglebiusng_ipfw_rcvdata(hook_p hook, item_p item) 223141351Sglebius{ 224141351Sglebius struct ng_ipfw_tag *ngit; 225141351Sglebius struct mbuf *m; 226141351Sglebius 227141351Sglebius NGI_GET_M(item, m); 228141351Sglebius NG_FREE_ITEM(item); 229141351Sglebius 230141351Sglebius if ((ngit = (struct ng_ipfw_tag *)m_tag_locate(m, NGM_IPFW_COOKIE, 0, 231141351Sglebius NULL)) == NULL) { 232141351Sglebius NG_FREE_M(m); 233141351Sglebius return (EINVAL); /* XXX: find smth better */ 234141351Sglebius }; 235141351Sglebius 236141351Sglebius switch (ngit->dir) { 237141351Sglebius case NG_IPFW_OUT: 238141699Sglebius { 239141702Sglebius struct ip *ip; 240141699Sglebius 241141704Sglebius if (m->m_len < sizeof(struct ip) && 242141706Sglebius (m = m_pullup(m, sizeof(struct ip))) == NULL) 243141706Sglebius return (EINVAL); 244141702Sglebius 245141702Sglebius ip = mtod(m, struct ip *); 246141702Sglebius 247141699Sglebius ip->ip_len = ntohs(ip->ip_len); 248141699Sglebius ip->ip_off = ntohs(ip->ip_off); 249141699Sglebius 250155681Sru return ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); 251141699Sglebius } 252141351Sglebius case NG_IPFW_IN: 253141351Sglebius ip_input(m); 254141351Sglebius return (0); 255141351Sglebius default: 256141351Sglebius panic("ng_ipfw_rcvdata: bad dir %u", ngit->dir); 257141351Sglebius } 258141351Sglebius 259141351Sglebius /* not reached */ 260141351Sglebius return (0); 261141351Sglebius} 262141351Sglebius 263141351Sglebiusstatic int 264141351Sglebiusng_ipfw_input(struct mbuf **m0, int dir, struct ip_fw_args *fwa, int tee) 265141351Sglebius{ 266141351Sglebius struct mbuf *m; 267141351Sglebius struct ng_ipfw_tag *ngit; 268141699Sglebius struct ip *ip; 269141351Sglebius hook_p hook; 270141351Sglebius int error = 0; 271141351Sglebius 272141351Sglebius /* 273141351Sglebius * Node must be loaded and corresponding hook must be present. 274141351Sglebius */ 275141351Sglebius if (fw_node == NULL || 276141351Sglebius (hook = ng_ipfw_findhook1(fw_node, fwa->cookie)) == NULL) { 277141351Sglebius if (tee == 0) 278141351Sglebius m_freem(*m0); 279141351Sglebius return (ESRCH); /* no hook associated with this rule */ 280141351Sglebius } 281141351Sglebius 282141351Sglebius /* 283141351Sglebius * We have two modes: in normal mode we add a tag to packet, which is 284141351Sglebius * important to return packet back to IP stack. In tee mode we make 285141351Sglebius * a copy of a packet and forward it into netgraph without a tag. 286141351Sglebius */ 287141351Sglebius if (tee == 0) { 288141351Sglebius m = *m0; 289141351Sglebius *m0 = NULL; /* it belongs now to netgraph */ 290141351Sglebius 291141351Sglebius if ((ngit = (struct ng_ipfw_tag *)m_tag_alloc(NGM_IPFW_COOKIE, 292141351Sglebius 0, TAGSIZ, M_NOWAIT|M_ZERO)) == NULL) { 293141351Sglebius m_freem(m); 294141351Sglebius return (ENOMEM); 295141351Sglebius } 296141351Sglebius ngit->rule = fwa->rule; 297193859Soleg ngit->rule_id = fwa->rule_id; 298193859Soleg ngit->chain_id = fwa->chain_id; 299141351Sglebius ngit->dir = dir; 300141351Sglebius ngit->ifp = fwa->oif; 301141351Sglebius m_tag_prepend(m, &ngit->mt); 302141351Sglebius 303141351Sglebius } else 304141705Sglebius if ((m = m_dup(*m0, M_DONTWAIT)) == NULL) 305141351Sglebius return (ENOMEM); /* which is ignored */ 306141351Sglebius 307141705Sglebius if (m->m_len < sizeof(struct ip) && 308141706Sglebius (m = m_pullup(m, sizeof(struct ip))) == NULL) 309141706Sglebius return (EINVAL); 310141705Sglebius 311141699Sglebius ip = mtod(m, struct ip *); 312141699Sglebius ip->ip_len = htons(ip->ip_len); 313141699Sglebius ip->ip_off = htons(ip->ip_off); 314141699Sglebius 315141351Sglebius NG_SEND_DATA_ONLY(error, hook, m); 316141351Sglebius 317141351Sglebius return (error); 318141351Sglebius} 319141351Sglebius 320141351Sglebiusstatic int 321141351Sglebiusng_ipfw_shutdown(node_p node) 322141351Sglebius{ 323141351Sglebius 324141351Sglebius /* 325141351Sglebius * After our single node has been removed, 326141351Sglebius * the only thing that can be done is 327141351Sglebius * 'kldunload ng_ipfw.ko' 328141351Sglebius */ 329141351Sglebius ng_ipfw_input_p = NULL; 330141351Sglebius NG_NODE_UNREF(node); 331141351Sglebius return (0); 332141351Sglebius} 333141351Sglebius 334141351Sglebiusstatic int 335141351Sglebiusng_ipfw_disconnect(hook_p hook) 336141351Sglebius{ 337141351Sglebius const hpriv_p hpriv = NG_HOOK_PRIVATE(hook); 338141351Sglebius 339184205Sdes free(hpriv, M_NETGRAPH); 340141351Sglebius NG_HOOK_SET_PRIVATE(hook, NULL); 341141351Sglebius 342141351Sglebius return (0); 343141351Sglebius} 344