--- /dev/null
+/*
+ * 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$
+ *
+ * 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 <sys/cdefs.h>
+#include <sys/mbuf.h> /* sizeof struct mbuf */
+
+#ifdef __linux__
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h> /* NF_IP_PRI_FILTER */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
+#include <net/netfilter/nf_queue.h> /* nf_queue */
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
+#define __read_mostly
+#endif
+
+#endif /* !__linux__ */
+
+#include <netinet/in.h> /* in_addr */
+#include <netinet/ip_fw.h> /* ip_fw_ctl_t, ip_fw_chk_t */
+#include <netinet/ip_dummynet.h> /* ip_dn_ctl_t, ip_dn_io_t */
+#include <net/pfil.h> /* PFIL_IN, PFIL_OUT */
+
+/*
+ * Here we allocate some global variables used in the firewall.
+ */
+ip_dn_ctl_t *ip_dn_ctl_ptr;
+ip_fw_ctl_t *ip_fw_ctl_ptr;
+
+ip_dn_io_t *ip_dn_io_ptr;
+ip_fw_chk_t *ip_fw_chk_ptr;
+
+void (*bridge_dn_p)(struct mbuf *, struct ifnet *);
+
+/*
+ * 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.
+ */
+#include <sys/module.h>
+struct mod_args {
+ struct moduledata *mod;
+ const char *name;
+ int order;
+};
+
+static unsigned int mod_idx;
+static struct mod_args mods[10]; /* hard limit to 10 modules */
+
+/*
+ * 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(struct moduledata *mod, const char *name, int order)
+{
+ struct mod_args m = { mod, name, order };
+
+ 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++) {
+ printf("+++ start module %d %s %s at %p order 0x%x\n",
+ i, mods[i].name, mods[i].mod->name,
+ mods[i].mod, mods[i].order);
+ mods[i].mod->evhand(NULL, MOD_LOAD, mods[i].mod->priv);
+ }
+}
+
+static void
+fini_children(void)
+{
+ int i;
+
+ /* Call the functions registered at init time. */
+ for (i = mod_idx - 1; i >= 0; i--) {
+ printf("+++ end module %d %s %s at %p order 0x%x\n",
+ i, mods[i].name, mods[i].mod->name,
+ mods[i].mod, mods[i].order);
+ mods[i].mod->evhand(NULL, MOD_UNLOAD, mods[i].mod->priv);
+ }
+}
+/* end of module bindinghelper 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\n", __FUNCTION__, cmd, len);
+
+ if (cmd < IP_DUMMYNET_CONFIGURE && ip_fw_ctl_ptr)
+ ret = ip_fw_ctl_ptr(s);
+ else if (cmd >= IP_DUMMYNET_CONFIGURE && ip_dn_ctl_ptr)
+ ret = ip_dn_ctl_ptr(s);
+
+ return -ret; /* errors are < 0 on linux */
+}
+
+#ifdef _WIN32
+
+void
+netisr_dispatch(int __unused num, struct mbuf *m)
+{
+}
+
+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)
+{
+ netisr_dispatch(0, m);
+ return 0;
+}
+
+#else /* this is the linux glue */
+/*
+ * setsockopt hook has no return value other than the error code.
+ */
+static 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.
+ */
+static 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;
+}
+
+/*
+ * 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
+};
+
+/*
+ * 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
+
+/*
+ * 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 the skbuf is passed as sk_buff**
+ */
+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,0)
+#define NF_STOP NF_ACCEPT
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+
+#define nf_queue_entry nf_info /* for simplicity */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* unsure on the exact boundary */
+/* on 2.4 we use nf_info */
+#define QH_ARGS struct sk_buff *skb, struct nf_info *info, void *data
+#else /* 2.6.1.. 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 in this skbuf */
+ 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,0)
+ m->m_data = skb->nh.iph;
+#else
+ m->m_data = skb_network_header(skb);
+#endif
+ /* XXX add the interface */
+ if (info->hook == NF_IP_PRE_ROUTING) {
+ ret = ipfw_check_in(NULL, &m, info->indev, PFIL_IN, NULL);
+ } else {
+ ret = ipfw_check_out(NULL, &m, info->outdev, PFIL_OUT, NULL);
+ }
+
+ if (m != NULL) { /* Accept. reinject and free the mbuf */
+ REINJECT(info, NF_STOP);
+ free(m, M_IPFW);
+ } else if (ret == 0) {
+ /* dummynet has kept the packet, will reinject later. */
+ } else {
+ /*
+ * Packet dropped by ipfw or dummynet, reinject as NF_DROP
+ * mbuf already released by ipfw itself
+ */
+ REINJECT(info, 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.
+ */
+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 */
+
+ free(m, M_IPFW);
+ KASSERT((info != NULL), ("%s info null!\n", __FUNCTION__));
+#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 */
+}
+
+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)
+{
+ netisr_dispatch(0, m);
+ return 0;
+}
+
+
+/*
+ * 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,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);
+ }
+}
+#define REG_QH_ARG(fn) fn, NULL /* argument for nf_[un]register_queue_handler */
+#define UNREG_QH_ARG(fn) //fn /* argument for nf_[un]register_queue_handler */
+#define SET_MOD_OWNER
+
+#else /* linux >= 2.6.0 */
+
+struct nf_queue_handler ipfw2_queue_handler_desc = {
+ .outfn = ipfw2_queue_handler,
+ .name = "ipfw2 dummynet queue",
+};
+#define REG_QH_ARG(fn) &(fn ## _desc)
+
+#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 = NF_IP_PRE_ROUTING,
+ .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 */
+extern moduledata_t *moddesc_ipfw;
+extern moduledata_t *moddesc_dummynet;
+
+/*
+ * Module glue - init and exit function.
+ */
+static int __init
+ipfw_module_init(void)
+{
+ int ret = 0;
+
+ printf("%s called\n", __FUNCTION__);
+
+ my_mod_register(moddesc_ipfw, "ipfw", 1);
+ my_mod_register(moddesc_dummynet, "dummynet", 2);
+ init_children();
+
+#ifdef _WIN32
+ return ret;
+
+#else /* linux hook */
+ /* 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__);
+
+ return ret;
+#endif /* linux */
+}
+
+/* module shutdown */
+static void __exit
+ipfw_module_exit(void)
+{
+#ifdef _WIN32
+#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("GPL"); /* mandatory */
+#endif