#include <sys/cdefs.h>
#include <sys/mbuf.h> /* sizeof struct mbuf */
+#include <sys/param.h> /* NGROUPS */
#ifdef __linux__
#include <linux/module.h>
#include <netinet/in.h> /* in_addr */
#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 */
+#include <net/pfil.h> /* PFIL_IN, PFIL_OUT */
+#include <net/inet_hashtables.h> /* inet_lookup */
+#include <net/route.h> /* inet_iif */
/*
* Here we allocate some global variables used in the firewall.
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
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 bindinghelper 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.
#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
* 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,
#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
#else
m->m_data = skb_network_header(skb);
#endif
+
/* XXX add the interface */
if (info->hook == NF_IP_PRE_ROUTING) {
ret = ipfw_check_in(NULL, &m, info->indev, PFIL_IN, NULL);
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 {
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 */
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, ...
+ *
+ * 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 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 */
+
+ 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 /* 2.6.25 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
+ 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 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.
+ * 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
+ 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;
+ }
+ 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);
+ return ret;
+}
/*
* Now prepare to hook the various functions.
#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