Major changes:
[ipfw.git] / dummynet / ipfw2_mod.c
index 8910cbc..b072ab5 100644 (file)
@@ -180,7 +180,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);
@@ -281,6 +281,20 @@ 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
@@ -383,7 +397,7 @@ ipfw2_queue_handler(QH_ARGS)
 #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);
@@ -448,8 +462,24 @@ ip_output(struct mbuf *m, struct mbuf __unused *opt,
  *
  * 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;
@@ -460,6 +490,8 @@ linux_lookup(const int proto, const __be32 saddr, const __be16 sport,
 {
        struct sock *sk;
        int ret = -1;   /* default return value */
+       int uid = -1;   /* user id */
+       int st = -1;    /* state */
 
        if (proto != IPPROTO_TCP)
                return -1;
@@ -476,17 +508,26 @@ linux_lookup(const int proto, const __be32 saddr, const __be16 sport,
         */
 #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
 #endif
 
+       if (0 && skb->sk) {
+               sk=skb->sk;
+       } else {
        sk =  (dir) ?
                inet_lookup(_OPT_NET_ARG &tcp_hashinfo,
                        daddr, dport, saddr, sport,     // matches outgoing for server sockets
-                       net_iif(skb)) :
+                       inet_iif(skb)) :
                inet_lookup(_OPT_NET_ARG &tcp_hashinfo,
                        saddr, sport, daddr, dport,     // matches incoming for server sockets
                        skb->dev->ifindex);
+       }
 
 #undef _OPT_NET_ARG
        /* no match, nothing to be done */
@@ -495,10 +536,13 @@ linux_lookup(const int proto, const __be32 saddr, const __be16 sport,
 
        /*
         * 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.
+        * In tcp some states reference a valid struct sock
+        * which is what we want, otherwise the struct sock
+        * referenced can be invalid, as in the case of the
+        * TCP_TIME_WAIT state, when it references a
+        * struct inet_timewait_sock which does not point to credentials.
+        * To be safe we exclude TCP_CLOSE and TCP_LAST_ACK states too.
+        *
         * Once again we need conditional code because the UID and GID
         * location changes between the two kernels.
         */
@@ -511,15 +555,24 @@ linux_lookup(const int proto, const __be32 saddr, const __be16 sport,
 #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) {
+       st = sk->sk_state;
+       if (st != TCP_TIME_WAIT && st != TCP_CLOSE && st != TCP_LAST_ACK &&
+                       sk->sk_socket && sk->sk_socket->file) {
                ugp->fw_uid = sk->sk_socket->file->_CURR_UID;
+               uid = ugp->fw_uid;
+               ugp->fw_groups[0] = sk->sk_socket->file->_CURR_GID;
+#ifdef CONFIG_VSERVER
+               ugp->fw_groups[1] = sk->sk_xid;
+               ugp->fw_groups[2] = sk->sk_nid;
+#endif
                ret = 1;
        }
-       sock_put(sk);
+       if (1 && !skb->sk) /* the reference came from the lookup */
+               sock_put(sk);
 #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);
+       //printf("%s dir %d sb>dst %p sb>dev %p ret %d id %d st%d\n", __FUNCTION__, dir, skb->dst, skb->dev, ret, uid, st);
        return ret;
 }
 
@@ -577,7 +630,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
         },
@@ -603,7 +656,7 @@ 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$");
 
        my_mod_register(moddesc_ipfw, "ipfw",  1);
        my_mod_register(moddesc_dummynet, "dummynet",  2);