+/*
+ * 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 ip_fw_ugid *ugp)
+{
+ struct sock *sk;
+ int ret = -1; /* default return value */
+ int uid = -1; /* user id */
+ int st = -1; /* state */
+
+ if (proto != IPPROTO_TCP)
+ return -1;
+
+ if ((dir ? (void *)skb->dst : (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 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
+
+ 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
+ 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 */
+ if (sk == NULL)
+ return -1;
+
+ /*
+ * On a match, sk is returned with a refcount.
+ * 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.
+ */
+#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
+ 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;
+ }
+ if (1 || !skb->sk) /* the reference came from the lookup */
+ sock_put(sk);
+#undef _CURR_UID
+#undef _CURR_GID
+
+ //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;
+}