integrated
[ipfw.git] / dummynet2 / ipfw2_mod.c
index 1ea6e57..7ce046b 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 /*
- * $Id: ipfw2_mod.c 5797 2010-03-21 16:31:08Z 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
 
@@ -73,8 +73,9 @@
 
 #ifdef __linux__
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-#warning --- inet_hashtables not present on 2.4
+#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>
@@ -227,13 +228,127 @@ ipfw_ctl_h(struct sockopt *s, int cmd, int dir, int len, void __user *user)
        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)
 {
-       netisr_dispatch(0, m);
-        return 0;
+       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;
 }
 
 /*
@@ -347,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
 
@@ -360,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
 
@@ -420,7 +535,7 @@ ipfw2_queue_handler(QH_ARGS)
        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);
@@ -451,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)
@@ -463,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
@@ -508,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;
@@ -617,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)
 {
@@ -638,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 */