X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=dummynet%2Fipfw2_mod.c;h=667d48725ea9f3892faeed2ed50aacef454c52b7;hb=31b969263c34f46f398eec33c0b0e95947842cda;hp=ff5a92ff458ef89c876569c2ac2d85486476ed70;hpb=1c3dc9f45532c25adc21f297422f0f5a7420b8ca;p=ipfw.git diff --git a/dummynet/ipfw2_mod.c b/dummynet/ipfw2_mod.c index ff5a92f..667d487 100644 --- a/dummynet/ipfw2_mod.c +++ b/dummynet/ipfw2_mod.c @@ -47,6 +47,9 @@ #include #include /* sizeof struct mbuf */ +#include /* NGROUPS */ + +#include "missing.h" #ifdef __linux__ #include @@ -67,20 +70,36 @@ #include /* in_addr */ #include /* ip_fw_ctl_t, ip_fw_chk_t */ #include /* ip_dn_ctl_t, ip_dn_io_t */ -#include /* PFIL_IN, PFIL_OUT */ +#include /* PFIL_IN, PFIL_OUT */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#warning --- inet_hashtables not present on 2.4 +#include +#include +#include +static inline int inet_iif(const struct sk_buff *skb) +{ + return ((struct rtable *)skb->dst)->rt_iif; +} + +#else +#include /* inet_lookup */ +#endif +#include /* inet_iif */ /* * Here we allocate some global variables used in the firewall. */ -ip_dn_ctl_t *ip_dn_ctl_ptr; +//ip_dn_ctl_t *ip_dn_ctl_ptr; +int (*ip_dn_ctl_ptr)(struct sockopt *); + ip_fw_ctl_t *ip_fw_ctl_ptr; -ip_dn_io_t *ip_dn_io_ptr; +int (*ip_dn_io_ptr)(struct mbuf **m, int dir, struct ip_fw_args *fwa); 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 @@ -140,9 +159,9 @@ fini_children(void) mods[i].mod->evhand(NULL, MOD_UNLOAD, mods[i].mod->priv); } } -/* end of module bindinghelper functions */ +/*--- end of module binding helper 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. @@ -164,7 +183,7 @@ 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\n", __FUNCTION__, cmd, len); if (cmd < IP_DUMMYNET_CONFIGURE && ip_fw_ctl_ptr) ret = ip_fw_ctl_ptr(s); @@ -233,6 +252,25 @@ static struct nf_sockopt_ops ipfw_sockopts = { #endif }; +/*---- + * We need a number of macros to adapt to the various APIs in + * different linux versions. Among them: + * + * - the hook names change between macros (NF_IP*) and enum NF_INET_* + * + * - the second argument to the netfilter hook is + * struct sk_buff ** in kernels <= 2.6.22 + * struct sk_buff * in kernels > 2.6.22 + * + * - NF_STOP is not defined before 2.6 so we remap it to NF_ACCEPT + * + * - the packet descriptor passed to the queue handler is + * struct nf_info in kernels <= 2.6.24 + * struct nf_queue_entry in kernels <= 2.6.24 + * + * - the arguments to the queue handler also change; + */ + /* * declare hook to grab packets from the netfilter interface. * The NF_* names change in different versions of linux, in some @@ -246,12 +284,27 @@ static struct nf_sockopt_ops ipfw_sockopts = { #define NF_IP_POST_ROUTING NF_INET_POST_ROUTING #endif +/* + * ipfw hooks into the POST_ROUTING and the PRE_ROUTING chains. + * PlanetLab sets skb_tag to the slice id in the LOCAL_INPUT and + * POST_ROUTING chains, so if we want to use that information we + * need to hook the LOCAL_INPUT chain instead of the PRE_ROUTING. + * However at the moment the skb_tag info is not reliable so + * we stay with the standard hooks. + */ +#if 0 // defined(IPFW_PLANETLAB) +#define IPFW_HOOK_IN NF_IP_LOCAL_IN +#else +#define IPFW_HOOK_IN NF_IP_PRE_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** + * XXX note that in 2.4 and up to 2.6.22 the skbuf is passed as sk_buff** + * so we have an #ifdef to set the proper argument type. */ static unsigned int call_ipfw(unsigned int __unused hooknum, @@ -273,7 +326,13 @@ call_ipfw(unsigned int __unused hooknum, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) +/* + * nf_queue_entry is a recent addition, in previous versions + * of the code the struct is called nf_info. + */ #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 */ /* on 2.4 we use nf_info */ #define QH_ARGS struct sk_buff *skb, struct nf_info *info, void *data @@ -339,8 +398,9 @@ ipfw2_queue_handler(QH_ARGS) #else m->m_data = skb_network_header(skb); #endif + /* XXX add the interface */ - if (info->hook == NF_IP_PRE_ROUTING) { + if (info->hook == IPFW_HOOK_IN) { ret = ipfw_check_in(NULL, &m, info->indev, PFIL_IN, NULL); } else { ret = ipfw_check_out(NULL, &m, info->outdev, PFIL_OUT, NULL); @@ -348,7 +408,7 @@ ipfw2_queue_handler(QH_ARGS) if (m != NULL) { /* Accept. reinject and free the mbuf */ REINJECT(info, NF_STOP); - free(m, M_IPFW); + m_freem(m); } else if (ret == 0) { /* dummynet has kept the packet, will reinject later. */ } else { @@ -377,7 +437,8 @@ 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); + m_freem(m); + 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 */ @@ -396,6 +457,141 @@ ip_output(struct mbuf *m, struct mbuf __unused *opt, return 0; } +/* + * socket lookup function for linux. + * This code is used to associate uid, gid, jail/xid to packets, + * and store the info in a cache *ugp where they can be accessed quickly. + * The function returns 1 if the info is found, -1 otherwise. + * + * We do this only on selected protocols: TCP, ... + * + * The chain is the following + * sk_buff* sock* socket* file* + * skb -> sk ->sk_socket->file ->f_owner ->pid + * skb -> sk ->sk_socket->file ->f_uid (direct) + * skb -> sk ->sk_socket->file ->f_cred->fsuid (2.6.29+) + * + * Related headers: + * linux/skbuff.h struct skbuff + * net/sock.h struct sock + * linux/net.h struct socket + * linux/fs.h struct file + * + * With vserver we may have sk->sk_xid and sk->sk_nid that + * which we store in fw_groups[1] (matches O_JAIL) and fw_groups[2] + * (no matches yet) + * + * Note- for locally generated, outgoing packets we should not need + * need a lookup because the sk_buff already points to the socket where + * the info is. + */ +extern struct inet_hashinfo tcp_hashinfo; +int +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) + return -1; +#else + struct sock *sk; + int ret = -1; /* default return value */ + int st = -1; /* state */ + + + if (proto != IPPROTO_TCP) /* XXX extend for UDP */ + return -1; + + if ((dir ? (void *)skb->dst : (void *)skb->dev) == NULL) { + panic(" -- this should not happen\n"); + return -1; + } + + if (skb->sk) { + sk = skb->sk; + } else { + /* + * Try a lookup. On a match, sk has a refcount that we must + * release on exit (we know it because skb->sk = NULL). + * + * inet_lookup above 2.6.24 has an additional 'net' parameter + * so we use a macro to conditionally supply it. + * swap dst and src depending on the direction. + */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24) +#define _OPT_NET_ARG +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) +/* there is no dev_net() on 2.6.25 */ +#define _OPT_NET_ARG (skb->dev->nd_net), +#else /* 2.6.26 and above */ +#define _OPT_NET_ARG dev_net(skb->dev), +#endif +#endif + sk = (dir) ? /* dir != 0 on output */ + inet_lookup(_OPT_NET_ARG &tcp_hashinfo, + daddr, dport, saddr, sport, // match outgoing + inet_iif(skb)) : + inet_lookup(_OPT_NET_ARG &tcp_hashinfo, + saddr, sport, daddr, dport, // match incoming + skb->dev->ifindex); +#undef _OPT_NET_ARG + + if (sk == NULL) /* no match, nothing to be done */ + return -1; + } + ret = 1; /* retrying won't make things better */ + st = sk->sk_state; +#ifdef CONFIG_VSERVER + u->xid = sk->sk_xid; + u->nid = sk->sk_nid; +#else + u->xid = u->nid = 0; +#endif + /* + * Exclude tcp states where sk points to a inet_timewait_sock which + * has no sk_socket field (surely TCP_TIME_WAIT, perhaps more). + * To be safe, use a whitelist and not a blacklist. + * Before dereferencing sk_socket grab a lock on sk_callback_lock. + * + * Once again we need conditional code because the UID and GID + * location changes between kernels. + */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) +/* use the current's real uid/gid */ +#define _CURR_UID f_uid +#define _CURR_GID f_gid +#else /* 2.6.29 and above */ +/* use the current's file access real uid/gid */ +#define _CURR_UID f_cred->fsuid +#define _CURR_GID f_cred->fsgid +#endif + +#define GOOD_STATES ( \ + (1<sk_callback_lock); + if (sk->sk_socket && sk->sk_socket->file) { + u->uid = sk->sk_socket->file->_CURR_UID; + u->gid = sk->sk_socket->file->_CURR_GID; + } + read_unlock_bh(&sk->sk_callback_lock); + } else { + u->uid = u->gid = 0; + } + if (!skb->sk) /* return the reference that came from the lookup */ + sock_put(sk); +#undef GOOD_STATES +#undef _CURR_UID +#undef _CURR_GID + return ret; + +#endif /* LINUX > 2.4 */ +} /* * Now prepare to hook the various functions. @@ -451,7 +647,7 @@ static struct nf_hook_ops ipfw_ops[] __read_mostly = { { .hook = call_ipfw, .pf = PF_INET, - .hooknum = NF_IP_PRE_ROUTING, + .hooknum = IPFW_HOOK_IN, .priority = NF_IP_PRI_FILTER, SET_MOD_OWNER }, @@ -469,6 +665,7 @@ static struct nf_hook_ops ipfw_ops[] __read_mostly = { extern moduledata_t *moddesc_ipfw; extern moduledata_t *moddesc_dummynet; +extern void rn_init(void); /* * Module glue - init and exit function. */ @@ -477,7 +674,9 @@ ipfw_module_init(void) { int ret = 0; - printf("%s called\n", __FUNCTION__); + printf("%s in-hook %d svn id %s\n", __FUNCTION__, IPFW_HOOK_IN, "$Id$"); + + rn_init(); my_mod_register(moddesc_ipfw, "ipfw", 1); my_mod_register(moddesc_dummynet, "dummynet", 2); @@ -541,5 +740,5 @@ ipfw_module_exit(void) #ifdef __linux__ module_init(ipfw_module_init) module_exit(ipfw_module_exit) -MODULE_LICENSE("GPL"); /* mandatory */ +MODULE_LICENSE("Dual BSD/GPL"); /* the code here is all BSD. */ #endif