*/
/*
- * $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
#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>
#else
#include <net/inet_hashtables.h> /* inet_lookup */
#endif
+#endif /* __linux__ */
+
#include <net/route.h> /* inet_iif */
/*
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
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]))
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)
{
return ret;
}
+#ifdef __linux__
+
/*
* declare our [get|set]sockopt hooks
*/
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_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);
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
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,
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 */
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;
/*
* 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) {
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();