/* * Copyright (C) 2009 Luigi Rizzo, Marta Carbone, Universita` di Pisa * * 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. */ /* * $Id: ipfw2_mod.c 10302 2012-01-19 21:49:23Z marta $ * * The main interface to build ipfw+dummynet as a linux module. * (and possibly as a windows module as well, though that part * is not complete yet). * * The control interface uses the sockopt mechanism * on a socket(AF_INET, SOCK_RAW, IPPROTO_RAW). * * The data interface uses the netfilter interface, at the moment * hooked to the PRE_ROUTING and POST_ROUTING hooks. * Unfortunately the netfilter interface is a moving target, * so we need a set of macros to adapt to the various cases. * * In the netfilter hook we just mark packet as 'QUEUE' and then * let the queue handler to do the whole work (filtering and * possibly emulation). * As we receive packets, we wrap them with an mbuf descriptor * so the existing ipfw+dummynet code runs unmodified. */ #include #include /* sizeof struct mbuf */ #include /* NGROUPS */ #ifdef __linux__ #include #include #include #include /* NF_IP_PRI_FILTER */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) #include /* nf_queue */ #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) #define __read_mostly #endif #endif /* !__linux__ */ #include /* in_addr */ #include /* ip_fw_ctl_t, ip_fw_chk_t */ #include /* ip_fw_ctl_t, ip_fw_chk_t */ #include /* ip_dn_ctl_t, ip_dn_io_t */ #include /* PFIL_IN, PFIL_OUT */ #ifdef __linux__ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) /* XXX was < 2.6.0: inet_hashtables.h is introduced in 2.6.14 */ // #warning --- inet_hashtables not present on 2.4 #include #include #include static inline int inet_iif(const struct sk_buff *skb) { return ((struct rtable *)skb->dst)->rt_iif; } #else #include /* inet_lookup */ #endif #endif /* __linux__ */ #include /* inet_iif */ /* * Here we allocate some global variables used in the firewall. */ //ip_dn_ctl_t *ip_dn_ctl_ptr; int (*ip_dn_ctl_ptr)(struct sockopt *); ip_fw_ctl_t *ip_fw_ctl_ptr; int (*ip_dn_io_ptr)(struct mbuf **m, int dir, struct ip_fw_args *fwa); ip_fw_chk_t *ip_fw_chk_ptr; void (*bridge_dn_p)(struct mbuf *, struct ifnet *); /* Divert hooks. */ void (*ip_divert_ptr)(struct mbuf *m, int incoming); /* ng_ipfw hooks. */ ng_ipfw_input_t *ng_ipfw_input_p = NULL; /*--- * Glue code to implement the registration of children with the parent. * Each child should call my_mod_register() when linking, so that * module_init() and module_exit() can call init_children() and * fini_children() to provide the necessary initialization. * We use the same mechanism for MODULE_ and SYSINIT_. * The former only get a pointer to the moduledata, * the latter have two function pointers (init/uninit) */ #include struct mod_args { const char *name; int order; struct moduledata *mod; void (*init)(void), (*uninit)(void); }; static unsigned int mod_idx; static struct mod_args mods[10]; /* hard limit to 10 modules */ int my_mod_register(const char *name, int order, struct moduledata *mod, void *init, void *uninit); /* * my_mod_register should be called automatically as the init * functions in the submodules. Unfortunately this compiler/linker * trick is not supported yet so we call it manually. */ int my_mod_register(const char *name, int order, struct moduledata *mod, void *init, void *uninit) { struct mod_args m; m.name = name; m.order = order; m.mod = mod; m.init = init; m.uninit = uninit; printf("%s %s called\n", __FUNCTION__, name); if (mod_idx < sizeof(mods) / sizeof(mods[0])) mods[mod_idx++] = m; return 0; } static void init_children(void) { unsigned int i; /* Call the functions registered at init time. */ printf("%s mod_idx value %d\n", __FUNCTION__, mod_idx); for (i = 0; i < mod_idx; i++) { struct mod_args *m = &mods[i]; printf("+++ start module %d %s %s at %p order 0x%x\n", i, m->name, m->mod ? m->mod->name : "SYSINIT", m->mod, m->order); if (m->mod && m->mod->evhand) m->mod->evhand(NULL, MOD_LOAD, m->mod->priv); else if (m->init) m->init(); } } static void fini_children(void) { int i; /* Call the functions registered at init time. */ for (i = mod_idx - 1; i >= 0; i--) { struct mod_args *m = &mods[i]; printf("+++ end module %d %s %s at %p order 0x%x\n", i, m->name, m->mod ? m->mod->name : "SYSINIT", m->mod, m->order); if (m->mod && m->mod->evhand) m->mod->evhand(NULL, MOD_UNLOAD, m->mod->priv); else if (m->uninit) m->uninit(); } } /*--- end of module binding helper functions ---*/ /*--- * Control hooks: * ipfw_ctl_h() is a wrapper for linux to FreeBSD sockopt call convention. * then call the ipfw handler in order to manage requests. * In turn this is called by the linux set/get handlers. */ static int ipfw_ctl_h(struct sockopt *s, int cmd, int dir, int len, void __user *user) { struct thread t; int ret = EINVAL; memset(s, 0, sizeof(s)); s->sopt_name = cmd; s->sopt_dir = dir; s->sopt_valsize = len; s->sopt_val = user; /* sopt_td is not used but it is referenced */ memset(&t, 0, sizeof(t)); s->sopt_td = &t; //printf("%s called with cmd %d len %d sopt %p user %p\n", __FUNCTION__, cmd, len, s, user); if (ip_fw_ctl_ptr && cmd != IP_DUMMYNET3 && (cmd == IP_FW3 || cmd < IP_DUMMYNET_CONFIGURE)) ret = ip_fw_ctl_ptr(s); else if (ip_dn_ctl_ptr && (cmd == IP_DUMMYNET3 || cmd >= IP_DUMMYNET_CONFIGURE)) ret = ip_dn_ctl_ptr(s); return -ret; /* errors are < 0 on linux */ } /* * Convert an mbuf into an skbuff * At the moment this only works for ip packets fully contained * in a single mbuf. We assume that on entry ip_len and ip_off are * in host format, and the ip checksum is not computed. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* check boundary */ int dst_output(struct skbuff *s) { return 0; } struct sk_buff * mbuf2skbuff(struct mbuf* m) { return NULL; } #else struct sk_buff * mbuf2skbuff(struct mbuf* m) { struct sk_buff *skb; size_t len = m->m_pkthdr.len; /* used to lookup the routing table */ struct rtable *r; struct flowi fl; int ret = 0; /* success for ip_route_output_key() */ struct ip *ip = mtod(m, struct ip *); /* XXX ip_output has ip_len and ip_off in network format, * linux expects host format */ ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); ip->ip_sum = 0; ip->ip_sum = in_cksum(m, ip->ip_hl<<2); /* fill flowi struct, we need just the dst addr, see XXX */ bzero(&fl, sizeof(fl)); flow_daddr.daddr = ip->ip_dst.s_addr; /* * ip_route_output_key() should increment * r->u.dst.__use and call a dst_hold(dst) * XXX verify how we release the resources. */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38) /* check boundary */ r = ip_route_output_key(&init_net, &fl.u.ip4); #elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26) /* check boundary */ ret = ip_route_output_key(&init_net, &r, &fl); #else ret = ip_route_output_key(&r, &fl); #endif if (ret != 0 || r == NULL ) { printf("NO ROUTE FOUND\n"); return NULL; } /* allocate the skbuff and the data */ skb = alloc_skb(len + sizeof(struct ethhdr), GFP_ATOMIC); if (skb == NULL) { printf("%s: can not allocate SKB buffers.\n", __FUNCTION__); return NULL; } skb->protocol = htons(ETH_P_IP); // XXX 8 or 16 bit ? /* sk_dst_set XXX take the lock (?) */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) skb_dst_set(skb, &r->u.dst); #else skb_dst_set(skb, &r->dst); #endif skb->dev = skb_dst(skb)->dev; /* reserve space for ethernet header */ skb_reserve(skb, sizeof(struct ethhdr)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) skb_reset_network_header(skb); // skb->network_header = skb->data - skb->head #else skb->nh.raw = skb->data; #endif /* set skbuff tail pointers and copy content */ skb_put(skb, len); memcpy(skb->data, m->m_data, len); return skb; } #endif /* keepalives not supported on linux 2.4 */ /* * This function is called to reinject packets to the * kernel stack within the linux netfilter system * or to send a new created mbuf. * In the first case we have a valid sk_buff pointer * encapsulated within the fake mbuf, so we can call * the reinject function trough netisr_dispatch. * In the last case we need to build a sk_buff from scratch, * before sending out the packet. */ int ip_output(struct mbuf *m, struct mbuf __unused *opt, struct route __unused *ro, int __unused flags, struct ip_moptions __unused *imo, struct inpcb __unused *inp) { if ( m->m_skb != NULL ) { /* reinjected packet, just call dispatch */ netisr_dispatch(0, m); } else { /* self-generated packet, wrap as appropriate and send */ #ifdef __linux__ struct sk_buff *skb = mbuf2skbuff(m); if (skb != NULL) dst_output(skb); #else /* Windows */ #endif FREE_PKT(m); } return 0; } /* * setsockopt hook has no return value other than the error code. */ int do_ipfw_set_ctl(struct sock __unused *sk, int cmd, void __user *user, unsigned int len) { struct sockopt s; /* pass arguments */ return ipfw_ctl_h(&s, cmd, SOPT_SET, len, user); } /* * getsockopt can can return a block of data in response. */ int do_ipfw_get_ctl(struct sock __unused *sk, int cmd, void __user *user, int *len) { struct sockopt s; /* pass arguments */ int ret = ipfw_ctl_h(&s, cmd, SOPT_GET, *len, user); *len = s.sopt_valsize; /* return lenght back to the caller */ return ret; } #ifdef __linux__ /* * declare our [get|set]sockopt hooks */ static struct nf_sockopt_ops ipfw_sockopts = { .pf = PF_INET, .set_optmin = _IPFW_SOCKOPT_BASE, .set_optmax = _IPFW_SOCKOPT_END, .set = do_ipfw_set_ctl, .get_optmin = _IPFW_SOCKOPT_BASE, .get_optmax = _IPFW_SOCKOPT_END, .get = do_ipfw_get_ctl, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) .owner = THIS_MODULE, #endif }; /*---- * We need a number of macros to adapt to the various APIs in * different linux versions. Among them: * * - the hook names change between macros (NF_IP*) and enum NF_INET_* * * - the second argument to the netfilter hook is * struct sk_buff ** in kernels <= 2.6.22 * struct sk_buff * in kernels > 2.6.22 * * - NF_STOP is not defined before 2.6 so we remap it to NF_ACCEPT * * - the packet descriptor passed to the queue handler is * struct nf_info in kernels <= 2.6.24 * struct nf_queue_entry in kernels <= 2.6.24 * * - the arguments to the queue handler also change; */ /* * declare hook to grab packets from the netfilter interface. * The NF_* names change in different versions of linux, in some * cases they are #defines, in others they are enum, so we * need to adapt. */ #ifndef NF_IP_PRE_ROUTING #define NF_IP_PRE_ROUTING NF_INET_PRE_ROUTING #endif #ifndef NF_IP_POST_ROUTING #define NF_IP_POST_ROUTING NF_INET_POST_ROUTING #endif /* * ipfw hooks into the POST_ROUTING and the PRE_ROUTING chains. * PlanetLab sets skb_tag to the slice id in the LOCAL_INPUT and * POST_ROUTING chains, so if we want to use that information we * need to hook the LOCAL_INPUT chain instead of the PRE_ROUTING. * However at the moment the skb_tag info is not reliable so * we stay with the standard hooks. */ #if 0 // defined(IPFW_PLANETLAB) #define IPFW_HOOK_IN NF_IP_LOCAL_IN #else #define IPFW_HOOK_IN NF_IP_PRE_ROUTING #endif /* * The main netfilter hook. * To make life simple, we queue everything and then do all the * decision in the queue handler. * * XXX note that in 2.4 and up to 2.6.22 the skbuf is passed as sk_buff** * so we have an #ifdef to set the proper argument type. */ static unsigned int call_ipfw(unsigned int __unused hooknum, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) // in 2.6.22 we have ** struct sk_buff __unused **skb, #else struct sk_buff __unused *skb, #endif const struct net_device __unused *in, const struct net_device __unused *out, int __unused (*okfn)(struct sk_buff *)) { return NF_QUEUE; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) /* XXX was 2.6.0 */ #define NF_STOP NF_ACCEPT #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) /* * nf_queue_entry is a recent addition, in previous versions * of the code the struct is called nf_info. */ #define nf_queue_entry nf_info /* for simplicity */ /* also, 2.4 and perhaps something else have different arguments */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) /* XXX unsure */ /* on 2.4 we use nf_info */ #define QH_ARGS struct sk_buff *skb, struct nf_info *info, void *data #else /* 2.6.14. 2.6.24 */ #define QH_ARGS struct sk_buff *skb, struct nf_info *info, unsigned int qnum, void *data #endif #define DEFINE_SKB /* nothing, already an argument */ #define REINJECT(_inf, _verd) nf_reinject(skb, _inf, _verd) #else /* 2.6.25 and above */ #define QH_ARGS struct nf_queue_entry *info, unsigned int queuenum #define DEFINE_SKB struct sk_buff *skb = info->skb; #define REINJECT(_inf, _verd) nf_reinject(_inf, _verd) #endif /* * used by dummynet when dropping packets * XXX use dummynet_send() */ void reinject_drop(struct mbuf* m) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) /* unsure on the exact boundary */ struct sk_buff *skb = (struct sk_buff *)m; #endif REINJECT(m->queue_entry, NF_DROP); } /* * The real call to the firewall. nf_queue_entry points to the skbuf, * and eventually we need to return both through nf_reinject(). */ static int ipfw2_queue_handler(QH_ARGS) { DEFINE_SKB /* no semicolon here, goes in the macro */ int ret = 0; /* return value */ struct mbuf *m; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) if (skb->nh.iph == NULL) { printf("null dp, len %d reinject now\n", skb->len); REINJECT(info, NF_ACCEPT); return 0; } #endif m = malloc(sizeof(*m), 0, 0); if (m == NULL) { printf("malloc fail, len %d reinject now\n", skb->len); REINJECT(info, NF_ACCEPT); return 0; } m->m_skb = skb; m->m_len = skb->len; /* len from ip header to end */ m->m_pkthdr.len = skb->len; /* total packet len */ m->m_pkthdr.rcvif = info->indev; m->queue_entry = info; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) /* XXX was 2.6.0 */ m->m_data = skb->nh.iph; #else m->m_data = skb_network_header(skb); #endif /* XXX add the interface */ if (info->hook == IPFW_HOOK_IN) { ret = ipfw_check_hook(NULL, &m, info->indev, PFIL_IN, NULL); } else { ret = ipfw_check_hook(NULL, &m, info->outdev, PFIL_OUT, NULL); } if (m != NULL) { /* Accept. reinject and free the mbuf */ REINJECT(info, NF_ACCEPT); m_freem(m); } else if (ret == 0) { /* dummynet has kept the packet, will reinject later. */ } else { /* * Packet dropped by ipfw or dummynet. Nothing to do as * FREE_PKT already did a reinject as NF_DROP */ } return 0; } struct route; struct ip_moptions; struct inpcb; /* XXX should include prototypes for netisr_dispatch and ip_output */ /* * The reinjection routine after a packet comes out from dummynet. * We must update the skb timestamp so ping reports the right time. * This routine is also used (with num == -1) as FREE_PKT. XXX */ void netisr_dispatch(int num, struct mbuf *m) { struct nf_queue_entry *info = m->queue_entry; struct sk_buff *skb = m->m_skb; /* always used */ /* * This function can be called by the FREE_PKT() * used when ipfw generate their own mbuf packets * or by the mbuf2skbuff() function. */ m_freem(m); /* XXX check * info is null in the case of a real mbuf * (one created by the ipfw code without a * valid sk_buff pointer */ if (info == NULL) return; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) // XXX above 2.6.x ? __net_timestamp(skb); /* update timestamp */ #endif /* XXX to obey one-pass, possibly call the queue handler here */ REINJECT(info, ((num == -1)?NF_DROP:NF_STOP)); /* accept but no more firewall */ } /* * socket lookup function for linux. * This code is used to associate uid, gid, jail/xid to packets, * and store the info in a cache *ugp where they can be accessed quickly. * The function returns 1 if the info is found, -1 otherwise. * * We do this only on selected protocols: TCP, ... * * The chain is the following * sk_buff* sock* socket* file* * skb -> sk ->sk_socket->file ->f_owner ->pid * skb -> sk ->sk_socket->file ->f_uid (direct) * skb -> sk ->sk_socket->file ->f_cred->fsuid (2.6.29+) * * Related headers: * linux/skbuff.h struct skbuff * net/sock.h struct sock * linux/net.h struct socket * linux/fs.h struct file * * With vserver we may have sk->sk_xid and sk->sk_nid that * which we store in fw_groups[1] (matches O_JAIL) and fw_groups[2] * (no matches yet) * * Note- for locally generated, outgoing packets we should not need * need a lookup because the sk_buff already points to the socket where * the info is. */ extern struct inet_hashinfo tcp_hashinfo; int linux_lookup(const int proto, const __be32 saddr, const __be16 sport, const __be32 daddr, const __be16 dport, struct sk_buff *skb, int dir, struct bsd_ucred *u) { #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13) /* XXX was 2.6.0 */ return -1; #else struct sock *sk; int ret = -1; /* default return value */ int st = -1; /* state */ if (proto != IPPROTO_TCP) /* XXX extend for UDP */ return -1; if ((dir ? (void *)skb_dst(skb) : (void *)skb->dev) == NULL) { panic(" -- this should not happen\n"); return -1; } if (skb->sk) { sk = skb->sk; } else { /* * Try a lookup. On a match, sk has a refcount that we must * release on exit (we know it because skb->sk = NULL). * * inet_lookup above 2.6.24 has an additional 'net' parameter * so we use a macro to conditionally supply it. * swap dst and src depending on the direction. */ #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24) #define _OPT_NET_ARG #else #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) /* there is no dev_net() on 2.6.25 */ #define _OPT_NET_ARG (skb->dev->nd_net), #else /* 2.6.26 and above */ #define _OPT_NET_ARG dev_net(skb->dev), #endif #endif sk = (dir) ? /* dir != 0 on output */ inet_lookup(_OPT_NET_ARG &tcp_hashinfo, daddr, dport, saddr, sport, // match outgoing inet_iif(skb)) : inet_lookup(_OPT_NET_ARG &tcp_hashinfo, saddr, sport, daddr, dport, // match incoming skb->dev->ifindex); #undef _OPT_NET_ARG if (sk == NULL) /* no match, nothing to be done */ return -1; } ret = 1; /* retrying won't make things better */ st = sk->sk_state; #ifdef CONFIG_VSERVER u->xid = sk->sk_xid; u->nid = sk->sk_nid; #else u->xid = u->nid = 0; #endif /* * Exclude tcp states where sk points to a inet_timewait_sock which * has no sk_socket field (surely TCP_TIME_WAIT, perhaps more). * To be safe, use a whitelist and not a blacklist. * Before dereferencing sk_socket grab a lock on sk_callback_lock. * * Once again we need conditional code because the UID and GID * location changes between kernels. */ #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) /* use the current's real uid/gid */ #define _CURR_UID f_uid #define _CURR_GID f_gid #else /* 2.6.29 and above */ /* use the current's file access real uid/gid */ #define _CURR_UID f_cred->fsuid #define _CURR_GID f_cred->fsgid #endif #define GOOD_STATES ( \ (1<sk_callback_lock); if (sk->sk_socket && sk->sk_socket->file) { u->uid = sk->sk_socket->file->_CURR_UID; u->gid = sk->sk_socket->file->_CURR_GID; } read_unlock_bh(&sk->sk_callback_lock); } else { u->uid = u->gid = 0; } if (!skb->sk) /* return the reference that came from the lookup */ sock_put(sk); #undef GOOD_STATES #undef _CURR_UID #undef _CURR_GID return ret; #endif /* LINUX > 2.4 */ } /* * Now prepare to hook the various functions. * Linux 2.4 has a different API so we need some adaptation * for register and unregister hooks * * the unregister function changed arguments between 2.6.22 and 2.6.24 */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) struct nf_queue_handler ipfw2_queue_handler_desc = { .outfn = ipfw2_queue_handler, .name = "ipfw2 dummynet queue", }; #define REG_QH_ARG(fn) &(fn ## _desc) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) /* XXX was 2.6.0 */ static int nf_register_hooks(struct nf_hook_ops *ops, int n) { int i, ret = 0; for (i = 0; i < n; i++) { ret = nf_register_hook(ops + i); if (ret < 0) break; } return ret; } static void nf_unregister_hooks(struct nf_hook_ops *ops, int n) { int i; for (i = 0; i < n; i++) { nf_unregister_hook(ops + i); } } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) /* XXX was 2.6.0 */ #define REG_QH_ARG(fn) fn, NULL /* argument for nf_[un]register_queue_handler */ #endif #define UNREG_QH_ARG(fn) //fn /* argument for nf_[un]register_queue_handler */ #define SET_MOD_OWNER #else /* linux > 2.6.17 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) #define UNREG_QH_ARG(fn) //fn /* argument for nf_[un]register_queue_handler */ #else #define UNREG_QH_ARG(fn) , &(fn ## _desc) #endif /* 2.6.0 < LINUX > 2.6.24 */ #define SET_MOD_OWNER .owner = THIS_MODULE, #endif /* !LINUX < 2.6.0 */ static struct nf_hook_ops ipfw_ops[] __read_mostly = { { .hook = call_ipfw, .pf = PF_INET, .hooknum = IPFW_HOOK_IN, .priority = NF_IP_PRI_FILTER, SET_MOD_OWNER }, { .hook = call_ipfw, .pf = PF_INET, .hooknum = NF_IP_POST_ROUTING, .priority = NF_IP_PRI_FILTER, SET_MOD_OWNER }, }; #endif /* __linux__ */ /* descriptors for the children, until i find a way for the * linker to produce them */ extern moduledata_t *moddesc_ipfw; extern moduledata_t *moddesc_dummynet; extern moduledata_t *moddesc_dn_fifo; extern moduledata_t *moddesc_dn_wf2qp; extern moduledata_t *moddesc_dn_rr; extern moduledata_t *moddesc_dn_qfq; extern moduledata_t *moddesc_dn_prio; extern void *sysinit_ipfw_init; extern void *sysuninit_ipfw_destroy; extern void *sysinit_vnet_ipfw_init; extern void *sysuninit_vnet_ipfw_uninit; /* * Module glue - init and exit function. */ int __init ipfw_module_init(void) { int ret = 0; #ifdef _WIN32 unsigned long resolution; #endif rn_init(64); my_mod_register("ipfw", 1, moddesc_ipfw, NULL, NULL); my_mod_register("sy_ipfw", 2, NULL, sysinit_ipfw_init, sysuninit_ipfw_destroy); my_mod_register("sy_Vnet_ipfw", 3, NULL, sysinit_vnet_ipfw_init, sysuninit_vnet_ipfw_uninit); my_mod_register("dummynet", 4, moddesc_dummynet, NULL, NULL); my_mod_register("dn_fifo", 5, moddesc_dn_fifo, NULL, NULL); my_mod_register("dn_wf2qp", 6, moddesc_dn_wf2qp, NULL, NULL); my_mod_register("dn_rr", 7, moddesc_dn_rr, NULL, NULL); my_mod_register("dn_qfq", 8, moddesc_dn_qfq, NULL, NULL); my_mod_register("dn_prio", 9, moddesc_dn_prio, NULL, NULL); init_children(); #ifdef _WIN32 resolution = ExSetTimerResolution(1, TRUE); printf("*** ExSetTimerResolution: resolution set to %d n-sec ***\n",resolution); #endif #ifdef EMULATE_SYSCTL keinit_GST(); #endif #ifdef __linux__ /* sockopt register, in order to talk with user space */ ret = nf_register_sockopt(&ipfw_sockopts); if (ret < 0) { printf("error %d in nf_register_sockopt\n", ret); goto clean_modules; } /* queue handler registration, in order to get network * packet under a private queue */ ret = nf_register_queue_handler(PF_INET, REG_QH_ARG(ipfw2_queue_handler) ); if (ret < 0) /* queue busy */ goto unregister_sockopt; ret = nf_register_hooks(ipfw_ops, ARRAY_SIZE(ipfw_ops)); if (ret < 0) goto unregister_sockopt; printf("%s loaded\n", __FUNCTION__); return 0; /* handle errors on load */ unregister_sockopt: nf_unregister_queue_handler(PF_INET UNREG_QH_ARG(ipfw2_queue_handler) ); nf_unregister_sockopt(&ipfw_sockopts); clean_modules: fini_children(); printf("%s error\n", __FUNCTION__); #endif /* __linux__ */ return ret; } /* module shutdown */ void __exit ipfw_module_exit(void) { #ifdef EMULATE_SYSCTL keexit_GST(); #endif #ifdef _WIN32 ExSetTimerResolution(0,FALSE); #else /* linux hook */ nf_unregister_hooks(ipfw_ops, ARRAY_SIZE(ipfw_ops)); /* maybe drain the queue before unregistering ? */ nf_unregister_queue_handler(PF_INET UNREG_QH_ARG(ipfw2_queue_handler) ); nf_unregister_sockopt(&ipfw_sockopts); #endif /* __linux__ */ fini_children(); printf("%s unloaded\n", __FUNCTION__); } #ifdef __linux__ module_init(ipfw_module_init) module_exit(ipfw_module_exit) MODULE_LICENSE("Dual BSD/GPL"); /* the code here is all BSD. */ #endif