*/
/*
- * $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
#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
#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>
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;
}
/*
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
#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
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);
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);
- 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
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;
*
* 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)
{
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 */