#include <netinet/ip_fw.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
+#include <linux/tcp.h>
+#include <net/route.h>
+#include <net/sock.h>
+static inline int inet_iif(const struct sk_buff *skb)
+{
+ return ((struct rtable *)skb->dst)->rt_iif;
+}
+
+#else
#include <net/inet_hashtables.h> /* inet_lookup */
+#endif
#include <net/route.h> /* 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 *);
static unsigned int mod_idx;
static struct mod_args mods[10]; /* hard limit to 10 modules */
-/*
- * Data structure to cache our ucred related
- * information. This structure only gets used if
- * the user specified UID/GID based constraints in
- * a firewall rule.
- */
-struct ip_fw_ugid {
- gid_t fw_groups[NGROUPS];
- int fw_ngroups;
- uid_t fw_uid;
- int fw_prid;
-};
-
/*
* my_mod_register should be called automatically as the init
* functions in the submodules. Unfortunately this compiler/linker
mods[i].mod->evhand(NULL, MOD_UNLOAD, mods[i].mod->priv);
}
}
-/*--- end of module bindinghelper functions ---*/
+/*--- end of module binding helper functions ---*/
/*---
* Control hooks:
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);
* - 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
+ * 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
+ * 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
#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
#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);
}
if (m != NULL) { /* Accept. reinject and free the mbuf */
- REINJECT(info, NF_STOP);
+ REINJECT(info, NF_ACCEPT);
m_freem(m);
} else if (ret == 0) {
/* dummynet has kept the packet, will reinject later. */
*
* We do this only on selected protocols: TCP, ...
*
- * Note- for locally generated, outgoing packets we don't need to
- * do a lookup because the sk_buff already points to the socket where
+ * 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 ip_fw_ugid *ugp)
+ 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)
+ if (proto != IPPROTO_TCP) /* XXX extend for UDP */
return -1;
- if ((dir ? (void *)skb->dst : (void *)skb->dev) == NULL) {
+ if ((dir ? (void *)skb_dst(skb) : (void *)skb->dev) == NULL) {
panic(" -- this should not happen\n");
return -1;
}
- /*
- * inet_lookup above 2.6.24 has an additional 'net' parameter
- * so we use a macro to conditionally supply it.
- * Also we need to switch dst and src depending on the direction.
- */
+ 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 /* 2.6.25 and above */
+#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
-
- sk = (dir) ?
- inet_lookup(_OPT_NET_ARG &tcp_hashinfo,
- daddr, dport, saddr, sport, // matches outgoing for server sockets
- net_iif(skb)) :
- inet_lookup(_OPT_NET_ARG &tcp_hashinfo,
- saddr, sport, daddr, dport, // matches incoming for server sockets
+#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
- /* no match, nothing to be done */
- if (sk == NULL)
- return -1;
+ 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
/*
- * On a match, sk is returned with a refcount.
- * In tcp states less that TCP_TIME_WAIT sk references a struct sock
- * which is what we want,
- * otherwise it references a struct inet_timewait_sock which does
- * not point to credentials.
+ * 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 the two kernels.
+ * location changes between kernels.
*/
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28)
/* use the current's real uid/gid */
#define _CURR_UID f_cred->fsuid
#define _CURR_GID f_cred->fsgid
#endif
- if (sk->sk_state < TCP_TIME_WAIT && sk->sk_socket && sk->sk_socket->file) {
- ugp->fw_uid = sk->sk_socket->file->_CURR_UID;
- ret = 1;
+
+#define GOOD_STATES ( \
+ (1<<TCP_LISTEN) | (1<<TCP_SYN_RECV) | (1<<TCP_SYN_SENT) | \
+ (1<<TCP_ESTABLISHED) | (1<<TCP_FIN_WAIT1) | (1<<TCP_FIN_WAIT2) )
+ // surely exclude TCP_CLOSE, TCP_TIME_WAIT, TCP_LAST_ACK
+ // uncertain TCP_CLOSE_WAIT and TCP_CLOSING
+
+ if ((1<<st) & GOOD_STATES) {
+ read_lock_bh(&sk->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;
}
- sock_put(sk);
+ if (!skb->sk) /* return the reference that came from the lookup */
+ sock_put(sk);
+#undef GOOD_STATES
#undef _CURR_UID
#undef _CURR_GID
-
- //printf("%s dir %d skb->dst %p skb->dev %p ret %d\n", __FUNCTION__, dir, skb->dst, skb->dev, ret);
return ret;
+
+#endif /* LINUX > 2.4 */
}
/*
{
.hook = call_ipfw,
.pf = PF_INET,
- .hooknum = NF_IP_PRE_ROUTING,
+ .hooknum = IPFW_HOOK_IN,
.priority = NF_IP_PRI_FILTER,
SET_MOD_OWNER
},
extern moduledata_t *moddesc_ipfw;
extern moduledata_t *moddesc_dummynet;
+extern void rn_init(void);
/*
* Module glue - init and exit function.
*/
{
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);