This repo is obsolete, please see git://git.code.sf.net/p/dummynet/code@master
[ipfw.git] / dummynet2 / ipfw2_mod.c
index f59a37c..7ce046b 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 /*
- * $Id: ipfw2_mod.c 4671 2010-01-04 17:50:51Z luigi $
+ * $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
@@ -59,7 +59,7 @@
 #include <net/netfilter/nf_queue.h>    /* nf_queue */
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
 #define __read_mostly
 #endif
 
 #include <netinet/ipfw/ip_fw_private.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 */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-#warning --- inet_hashtables not present on 2.4
+
+#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 <linux/tcp.h>
 #include <net/route.h>
 #include <net/sock.h>
@@ -83,6 +87,8 @@ static inline int inet_iif(const struct sk_buff *skb)
 #else
 #include <net/inet_hashtables.h>       /* inet_lookup */
 #endif
+#endif /* __linux__ */
+
 #include <net/route.h>                 /* inet_iif */
 
 /*
@@ -98,6 +104,12 @@ 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
@@ -130,8 +142,13 @@ int
 my_mod_register(const char *name, int order,
        struct moduledata *mod, void *init, void *uninit)
 {
-       struct mod_args m = { .name = name, .order = order,
-               .mod = mod, .init = init, .uninit = 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]))
@@ -199,49 +216,156 @@ ipfw_ctl_h(struct sockopt *s, int cmd, int dir, int len, void __user *user)
        memset(&t, 0, sizeof(t));
        s->sopt_td = &t;
        
-       // printf("%s called with cmd %d len %d\n", __FUNCTION__, cmd, len);
+       //printf("%s called with cmd %d len %d sopt %p user %p\n", __FUNCTION__, cmd, len, s, user);
 
-       if (cmd < IP_DUMMYNET_CONFIGURE && ip_fw_ctl_ptr)
+       if (ip_fw_ctl_ptr && cmd != IP_DUMMYNET3 && (cmd == IP_FW3 ||
+           cmd < IP_DUMMYNET_CONFIGURE))
                ret = ip_fw_ctl_ptr(s);
-       else if (cmd >= IP_DUMMYNET_CONFIGURE && ip_dn_ctl_ptr)
+       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 */
 }
 
-#ifdef _WIN32
+/*
+ * 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;
+}
 
-void
-netisr_dispatch(int __unused num, struct mbuf *m)
+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)
 {
-       netisr_dispatch(0, m);
+       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;
 }
 
-#else /* this is the linux glue */
 /*
  * setsockopt hook has no return value other than the error code.
  */
-static int
+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
+int
 do_ipfw_get_ctl(struct sock __unused *sk,
        int cmd, void __user *user, int *len)
 {
@@ -252,6 +376,8 @@ do_ipfw_get_ctl(struct sock __unused *sk,
        return ret;
 }
 
+#ifdef __linux__
+
 /*
  * declare our [get|set]sockopt hooks
  */
@@ -336,7 +462,7 @@ call_ipfw(unsigned int __unused hooknum,
        return NF_QUEUE;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)        /* XXX was 2.6.0 */
 #define        NF_STOP         NF_ACCEPT
 #endif
 
@@ -349,10 +475,10 @@ call_ipfw(unsigned int __unused hooknum,
 #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,0) /* unsure on the exact boundary */
+#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.1.. 2.6.24 */
+#else  /* 2.6.14. 2.6.24 */
 #define QH_ARGS                struct sk_buff *skb, struct nf_info *info, unsigned int qnum, void *data
 #endif
 
@@ -405,11 +531,11 @@ ipfw2_queue_handler(QH_ARGS)
        }
 
        m->m_skb = skb;
-       m->m_len = skb->len;            /* len in this skbuf */
+       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,0)
+#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);
@@ -440,11 +566,11 @@ 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)
@@ -452,9 +578,21 @@ 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);
 
-       KASSERT((info != NULL), ("%s info null!\n", __FUNCTION__));
+       /* 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
@@ -463,15 +601,6 @@ netisr_dispatch(int num, struct mbuf *m)
        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;
-}
-
 /*
  * socket lookup function for linux.
  * This code is used to associate uid, gid, jail/xid to packets,
@@ -506,7 +635,7 @@ 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,0)
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13)       /* XXX was 2.6.0 */
        return -1;
 #else
        struct sock *sk;
@@ -615,7 +744,15 @@ linux_lookup(const int proto, const __be32 saddr, const __be16 sport,
  *
  * the unregister function changed arguments between 2.6.22 and 2.6.24
  */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#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)
 {
@@ -636,17 +773,13 @@ nf_unregister_hooks(struct nf_hook_ops *ops, int n)
                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.0 */
-
-struct nf_queue_handler ipfw2_queue_handler_desc = {
-        .outfn = ipfw2_queue_handler,
-        .name = "ipfw2 dummynet queue",
-};
-#define REG_QH_ARG(fn) &(fn ## _desc)
+#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 */
@@ -674,13 +807,18 @@ static struct nf_hook_ops ipfw_ops[] __read_mostly = {
                SET_MOD_OWNER
         },
 };
-#endif /* !__linux__ */
+#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;
@@ -689,27 +827,37 @@ extern void *sysuninit_vnet_ipfw_uninit;
 /*
  * Module glue - init and exit function.
  */
-static int __init
+int __init
 ipfw_module_init(void)
 {
        int ret = 0;
-
-       printf("%s in-hook %d svn id %s\n", __FUNCTION__, IPFW_HOOK_IN, "$Id: ipfw2_mod.c 4671 2010-01-04 17:50:51Z luigi $");
+#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
-       return ret;
+       resolution = ExSetTimerResolution(1, TRUE);
+       printf("*** ExSetTimerResolution: resolution set to %d n-sec ***\n",resolution);
+#endif
+#ifdef EMULATE_SYSCTL
+       keinit_GST();
+#endif 
 
-#else  /* linux hook */
+#ifdef __linux__
        /* sockopt register, in order to talk with user space */
        ret = nf_register_sockopt(&ipfw_sockopts);
         if (ret < 0) {
@@ -740,21 +888,26 @@ clean_modules:
        fini_children();
        printf("%s error\n", __FUNCTION__);
 
+#endif /* __linux__ */
        return ret;
-#endif /* linux */
 }
 
 /* module shutdown */
-static void __exit
+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 */
+#endif /* __linux__ */
 
        fini_children();