[PATCH][EBTABLES] add "ebt_among" match

Bart De Schuymer <[email protected]>
Newsgroups gmane.linux.network.bridge.ebtables.devel
Message-ID <[email protected]>
Hi Dave,

This patch adds the ebtables among target. This allows fast matching of
a frame's MAC src/dst address vs a list of addresses. It also allows
matching on MAC/IP address pairs, making it possible to put loads of
anti-spoofing entries inside one ebtables rule. This is a big speed
improvement.

The patch is from Grzegorz Borowiak <grzes_at_gnu.univ.gda.pl>

cheers,
Bart


--- linux-2.6.0-test6/net/bridge/netfilter/Kconfig.old	2003-10-12 16:04:03.000000000 +0200
+++ linux-2.6.0-test6/net/bridge/netfilter/Kconfig	2003-10-12 16:09:05.000000000 +0200
@@ -55,6 +55,16 @@ config BRIDGE_EBT_802_3
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config BRIDGE_EBT_AMONG
+	tristate "ebt: among filter support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the among match, which allows matching the MAC source
+	  and/or destination address on a list of addresses. Optionally,
+	  MAC/IP address pairs can be matched, f.e. for anti-spoofing rules.
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 config BRIDGE_EBT_ARP
 	tristate "ebt: ARP filter support"
 	depends on BRIDGE_NF_EBTABLES
--- linux-2.6.0-test6/net/bridge/netfilter/Makefile.old	2003-10-12 16:03:24.000000000 +0200
+++ linux-2.6.0-test6/net/bridge/netfilter/Makefile	2003-10-12 16:03:54.000000000 +0200
@@ -11,6 +11,7 @@ obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtabl
 
 #matches
 obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
+obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o
 obj-$(CONFIG_BRIDGE_EBT_ARP) += ebt_arp.o
 obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip.o
 obj-$(CONFIG_BRIDGE_EBT_MARK) += ebt_mark_m.o
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.0-test6/net/bridge/netfilter/ebt_among.c	2003-10-12 16:03:02.000000000 +0200
@@ -0,0 +1,215 @@
+/*
+ *  ebt_among
+ *
+ *	Authors:
+ *	Grzegorz Borowiak <[email protected]>
+ *
+ *  August, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_among.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/module.h>
+
+static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
+				     const char *mac, uint32_t ip)
+{
+	/* You may be puzzled as to how this code works.
+	 * Some tricks were used, refer to 
+	 * 	include/linux/netfilter_bridge/ebt_among.h
+	 * as there you can find a solution of this mystery.
+	 */
+	const struct ebt_mac_wormhash_tuple *p;
+	int start, limit, i;
+	uint32_t cmp[2] = { 0, 0 };
+	int key = (const unsigned char) mac[5];
+
+	memcpy(((char *) cmp) + 2, mac, 6);
+	start = wh->table[key];
+	limit = wh->table[key + 1];
+	if (ip) {
+		for (i = start; i < limit; i++) {
+			p = &wh->pool[i];
+			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
+				if (p->ip == 0 || p->ip == ip) {
+					return 1;
+				}
+			}
+		}
+	} else {
+		for (i = start; i < limit; i++) {
+			p = &wh->pool[i];
+			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
+				if (p->ip == 0) {
+					return 1;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
+					    *wh)
+{
+	int i;
+
+	for (i = 0; i < 256; i++) {
+		if (wh->table[i] > wh->table[i + 1])
+			return -0x100 - i;
+		if (wh->table[i] < 0)
+			return -0x200 - i;
+		if (wh->table[i] > wh->poolsize)
+			return -0x300 - i;
+	}
+	if (wh->table[256] > wh->poolsize)
+		return -0xc00;
+	return 0;
+}
+
+static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr)
+{
+	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP)) {
+		struct iphdr iph;
+
+		if (skb_copy_bits(skb, 0, &iph, sizeof(iph)))
+			return -1;
+		*addr = iph.daddr;
+	} else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+		struct arphdr arph;
+
+		if (skb_copy_bits(skb, 0, &arph, sizeof(arph)) ||
+		    arph.ar_pln != sizeof(uint32_t) || arph.ar_hln != ETH_ALEN)
+			return -1;
+		if (skb_copy_bits(skb, sizeof(struct arphdr) +
+		    2 * ETH_ALEN + sizeof(uint32_t), addr, sizeof(uint32_t)))
+			return -1;
+	}
+	return 0;
+}
+
+static int get_ip_src(const struct sk_buff *skb, uint32_t *addr)
+{
+	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP)) {
+		struct iphdr iph;
+
+		if (skb_copy_bits(skb, 0, &iph, sizeof(iph)))
+			return -1;
+		*addr = iph.saddr;
+	} else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+		struct arphdr arph;
+
+		if (skb_copy_bits(skb, 0, &arph, sizeof(arph)) ||
+		    arph.ar_pln != sizeof(uint32_t) || arph.ar_hln != ETH_ALEN)
+			return -1;
+		if (skb_copy_bits(skb, sizeof(struct arphdr) +
+		    ETH_ALEN, addr, sizeof(uint32_t)))
+			return -1;
+	}
+	return 0;
+}
+
+static int ebt_filter_among(const struct sk_buff *skb,
+			    const struct net_device *in,
+			    const struct net_device *out, const void *data,
+			    unsigned int datalen)
+{
+	struct ebt_among_info *info = (struct ebt_among_info *) data;
+	const char *dmac, *smac;
+	const struct ebt_mac_wormhash *wh_dst, *wh_src;
+	uint32_t dip = 0, sip = 0;
+
+	wh_dst = ebt_among_wh_dst(info);
+	wh_src = ebt_among_wh_src(info);
+
+	if (wh_src) {
+		smac = skb->mac.ethernet->h_source;
+		if (get_ip_src(skb, &sip))
+			return EBT_NOMATCH;
+		if (!(info->bitmask & EBT_AMONG_SRC_NEG)) {
+			/* we match only if it contains */
+			if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
+				return EBT_NOMATCH;
+		} else {
+			/* we match only if it DOES NOT contain */
+			if (ebt_mac_wormhash_contains(wh_src, smac, sip))
+				return EBT_NOMATCH;
+		}
+	}
+
+	if (wh_dst) {
+		dmac = skb->mac.ethernet->h_dest;
+		if (get_ip_dst(skb, &dip))
+			return EBT_NOMATCH;
+		if (!(info->bitmask & EBT_AMONG_DST_NEG)) {
+			/* we match only if it contains */
+			if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
+				return EBT_NOMATCH;
+		} else {
+			/* we match only if it DOES NOT contain */
+			if (ebt_mac_wormhash_contains(wh_dst, dmac, dip))
+				return EBT_NOMATCH;
+		}
+	}
+
+	return EBT_MATCH;
+}
+
+static int ebt_among_check(const char *tablename, unsigned int hookmask,
+			   const struct ebt_entry *e, void *data,
+			   unsigned int datalen)
+{
+	struct ebt_among_info *info = (struct ebt_among_info *) data;
+	int expected_length = sizeof(struct ebt_among_info);
+	const struct ebt_mac_wormhash *wh_dst, *wh_src;
+	int err;
+
+	wh_dst = ebt_among_wh_dst(info);
+	wh_src = ebt_among_wh_src(info);
+	expected_length += ebt_mac_wormhash_size(wh_dst);
+	expected_length += ebt_mac_wormhash_size(wh_src);
+
+	if (datalen != EBT_ALIGN(expected_length)) {
+		printk(KERN_WARNING
+		       "ebtables: among: wrong size: %d"
+		       "against expected %d, rounded to %d\n",
+		       datalen, expected_length,
+		       EBT_ALIGN(expected_length));
+		return -EINVAL;
+	}
+	if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) {
+		printk(KERN_WARNING
+		       "ebtables: among: dst integrity fail: %x\n", -err);
+		return -EINVAL;
+	}
+	if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) {
+		printk(KERN_WARNING
+		       "ebtables: among: src integrity fail: %x\n", -err);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct ebt_match filter_among = {
+	.name		= EBT_AMONG_MATCH, 
+	.match		= ebt_filter_among, 
+	.check		= ebt_among_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_among);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_among);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.0-test6/include/linux/netfilter_bridge/ebt_among.h	2003-10-12 16:02:51.000000000 +0200
@@ -0,0 +1,65 @@
+#ifndef __LINUX_BRIDGE_EBT_AMONG_H
+#define __LINUX_BRIDGE_EBT_AMONG_H
+
+#define EBT_AMONG_DST 0x01
+#define EBT_AMONG_SRC 0x02
+
+/* Grzegorz Borowiak <[email protected]> 2003
+ * 
+ * Write-once-read-many hash table, used for checking if a given
+ * MAC address belongs to a set or not and possibly for checking
+ * if it is related with a given IPv4 address.
+ *
+ * The hash value of an address is its last byte.
+ * 
+ * In real-world ethernet addresses, values of the last byte are
+ * evenly distributed and there is no need to consider other bytes.
+ * It would only slow the routines down.
+ *
+ * For MAC address comparison speedup reasons, we introduce a trick.
+ * MAC address is mapped onto an array of two 32-bit integers.
+ * This pair of integers is compared with MAC addresses in the
+ * hash table, which are stored also in form of pairs of integers
+ * (in `cmp' array). This is quick as it requires only two elementary
+ * number comparisons in worst case. Further, we take advantage of
+ * fact that entropy of 3 last bytes of address is larger than entropy
+ * of 3 first bytes. So first we compare 4 last bytes of addresses and
+ * if they are the same we compare 2 first.
+ *
+ * Yes, it is a memory overhead, but in 2003 AD, who cares?
+ */
+
+struct ebt_mac_wormhash_tuple
+{
+	uint32_t cmp[2];
+	uint32_t ip;
+};
+
+struct ebt_mac_wormhash
+{
+	int table[257];
+	int poolsize;
+	struct ebt_mac_wormhash_tuple pool[0];
+};
+
+#define ebt_mac_wormhash_size(x) ((x) ? sizeof(struct ebt_mac_wormhash) \
+		+ (x)->poolsize * sizeof(struct ebt_mac_wormhash_tuple) : 0)
+
+struct ebt_among_info
+{
+	int wh_dst_ofs;
+	int wh_src_ofs;
+	int bitmask;
+};
+
+#define EBT_AMONG_DST_NEG 0x1
+#define EBT_AMONG_SRC_NEG 0x2
+
+#define ebt_among_wh_dst(x) ((x)->wh_dst_ofs ? \
+	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_dst_ofs) : NULL)
+#define ebt_among_wh_src(x) ((x)->wh_src_ofs ? \
+	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_src_ofs) : NULL)
+
+#define EBT_AMONG_MATCH "among"
+
+#endif



-------------------------------------------------------
This SF.net email is sponsored by: SF.net Giveback Program.
SourceForge.net hosts over 70,000 Open Source Projects.
See the people who have HELPED US provide better services:
Click here: http://sourceforge.net/supporters.php
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.