Re: Vlans

Lutz Jaenicke <[email protected]> Thu, 2 Nov 2006 11:19:13 +0100
Newsgroups gmane.linux.network.bridge.ebtables.user
Organization Innominate Security Technologies AG
Message-ID <[email protected]>
On Sat, Oct 28, 2006 at 05:21:31PM +0200, Bart De Schuymer wrote:
> Op wo, 18-10-2006 te 20:01 +0200, schreef Lutz Jaenicke:
> > The ebtables components (those configured with the ebtables command)
> > can be used to filter for VLAN tags. The ebtables specific protocol
> > matchers for arp, ip, etc are not transparent wrt VLAN: they will
> > not match VLAN encapsulated traffic. Note: I do have an extension
> > patch to provide this support (configurable) for some really old
> > version of ebtables (for kernel 2.4 with br-nf patch) that is due
> > to be ported to 2.6.18+ in the next days anyway.
> > I can send it to the list for discussion/inclusion as soon as the
> > porting is done if there is interest...
> 
> That would be interesting, thanks. I was thinking about a userspace flag
> like --vlan-encapsulated to let the user specify she wants to filter the
> ip/arp/etc stuff inside the vlan package. The kernel logic best resides
> inside the specific matches/targets, shouldn't be too messy.

I had implemented it using a /proc/sys control flag as attached
below. I am not to happy with this solution, so if you have a better
proposal...

I am currently overloaded with other projects so please understand
that my response time may be somewhat large from time to time.
It does not mean that I will not continue to work on this issue :-)

Best regards,
	Lutz
-- 
Dr.-Ing. Lutz Jänicke
CTO
Innominate Security Technologies AG  /protecting industrial networks/
tel: +49.30.6392-3308
fax: +49.30.6392-3307
Albert-Einstein-Str. 14
D-12489 Berlin, Germany
www.innominate.com
---------------------------------------------------------------

Visit us at the SPS/IPC/Drives in Nuernberg

28 - 30 November 2006, Hall 9, Stand 9-125

---------------------------------------------------------------

-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642

_______________________________________________
Ebtables-user mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ebtables-user
ebtables_vlan_transparent.patch (text/plain, 14.8 KB)
Index: br_netfilter.c
===================================================================
RCS file: /home/ljaenicke/source/mguard/linux-2.6/linux-2.6.15/net/bridge/br_netfilter.c,v
retrieving revision 1.3
diff -u -r1.3 br_netfilter.c
--- br_netfilter.c	10 Oct 2006 15:35:08 -0000	1.3
+++ br_netfilter.c	19 Oct 2006 18:14:09 -0000
@@ -57,6 +57,9 @@
 static int brnf_call_ip6tables = 1;
 static int brnf_call_arptables = 1;
 static int brnf_filter_vlan_tagged = 1;
+#ifdef CONFIG_BRIDGE_NETFILTER
+int ebtables_treat_vlan_transparent = 0;
+#endif
 #else
 #define brnf_filter_vlan_tagged 1
 #endif
@@ -1015,6 +1018,8 @@
 	return ret;
 }
 
+#define NET_EBTABLES_TREAT_VLAN_TRANSPARENT 2091
+
 static ctl_table brnf_table[] = {
 	{
 		.ctl_name	= NET_BRIDGE_NF_CALL_ARPTABLES,
@@ -1048,6 +1053,14 @@
 		.mode		= 0644,
 		.proc_handler	= &brnf_sysctl_call_tables,
 	},
+	{
+		.ctl_name	= NET_EBTABLES_TREAT_VLAN_TRANSPARENT,
+		.procname	= "ebtables-treat-vlan-transparent",
+		.data		= &ebtables_treat_vlan_transparent,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &brnf_sysctl_call_tables,
+	},
 	{ .ctl_name = 0 }
 };
 
Index: br_private.h
===================================================================
RCS file: /home/ljaenicke/source/mguard/linux-2.6/linux-2.6.15/net/bridge/br_private.h,v
retrieving revision 1.3
diff -u -r1.3 br_private.h
--- br_private.h	10 Oct 2006 15:35:08 -0000	1.3
+++ br_private.h	19 Oct 2006 14:43:37 -0000
@@ -195,6 +195,7 @@
 #ifdef CONFIG_BRIDGE_NETFILTER
 extern int br_netfilter_init(void);
 extern void br_netfilter_fini(void);
+extern int ebtables_treat_vlan_transparent;
 #else
 #define br_netfilter_init()	(0)
 #define br_netfilter_fini()	do { } while(0)
Index: netfilter/ebt_802_3.c
===================================================================
RCS file: /home/ljaenicke/source/mguard/linux-2.6/linux-2.6.15/net/bridge/netfilter/ebt_802_3.c,v
retrieving revision 1.2
diff -u -r1.2 ebt_802_3.c
--- netfilter/ebt_802_3.c	16 Jun 2006 14:20:29 -0000	1.2
+++ netfilter/ebt_802_3.c	19 Oct 2006 18:14:46 -0000
@@ -11,13 +11,18 @@
 #include <linux/netfilter_bridge/ebtables.h>
 #include <linux/netfilter_bridge/ebt_802_3.h>
 #include <linux/module.h>
+#include <linux/if_vlan.h>
 
 static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
    const struct net_device *out, const void *data, unsigned int datalen)
 {
 	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
-	struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb);
-	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
+	struct ebt_802_3_hdr _hdr, *hdr;
+	uint16_t type;
+	int offset = (skb->protocol == __constant_htons(ETH_P_8021Q)) ? VLAN_HLEN : 0;
+
+	hdr = skb_header_pointer(skb, offset, sizeof(_hdr), &_hdr);
+	type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
 
 	if (info->bitmask & EBT_802_3_SAP) {
 		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
Index: netfilter/ebt_among.c
===================================================================
RCS file: /home/ljaenicke/source/mguard/linux-2.6/linux-2.6.15/net/bridge/netfilter/ebt_among.c,v
retrieving revision 1.2
diff -u -r1.2 ebt_among.c
--- netfilter/ebt_among.c	16 Jun 2006 14:20:29 -0000	1.2
+++ netfilter/ebt_among.c	19 Oct 2006 18:15:17 -0000
@@ -12,6 +12,7 @@
 #include <linux/netfilter_bridge/ebt_among.h>
 #include <linux/ip.h>
 #include <linux/if_arp.h>
+#include <linux/if_vlan.h>
 #include <linux/module.h>
 
 static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
@@ -72,23 +73,30 @@
 
 static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr)
 {
-	if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
+	uint16_t h_proto = eth_hdr(skb)->h_proto;
+	int offset = 0;
+	if (h_proto == __constant_htons(ETH_P_8021Q)) {
+		h_proto = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;;
+		offset = VLAN_HLEN;
+	}
+
+	if (h_proto == __constant_htons(ETH_P_IP)) {
 		struct iphdr _iph, *ih;
 
-		ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
+		ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
 		if (ih == NULL)
 			return -1;
 		*addr = ih->daddr;
-	} else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
+	} else if (h_proto == __constant_htons(ETH_P_ARP)) {
 		struct arphdr _arph, *ah;
 		uint32_t buf, *bp;
 
-		ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
+		ah = skb_header_pointer(skb, offset, sizeof(_arph), &_arph);
 		if (ah == NULL ||
 		    ah->ar_pln != sizeof(uint32_t) ||
 		    ah->ar_hln != ETH_ALEN)
 			return -1;
-		bp = skb_header_pointer(skb, sizeof(struct arphdr) +
+		bp = skb_header_pointer(skb, offset + sizeof(struct arphdr) +
 					2 * ETH_ALEN + sizeof(uint32_t),
 					sizeof(uint32_t), &buf);
 		if (bp == NULL)
@@ -100,23 +108,30 @@
 
 static int get_ip_src(const struct sk_buff *skb, uint32_t *addr)
 {
-	if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) {
+	uint16_t h_proto = eth_hdr(skb)->h_proto;
+	int offset = 0;
+	if (h_proto == __constant_htons(ETH_P_8021Q)) {
+		h_proto = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;;
+		offset = VLAN_HLEN;
+	}
+
+	if (h_proto == __constant_htons(ETH_P_IP)) {
 		struct iphdr _iph, *ih;
 
-		ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
+		ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
 		if (ih == NULL)
 			return -1;
 		*addr = ih->saddr;
-	} else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {
+	} else if (h_proto == __constant_htons(ETH_P_ARP)) {
 		struct arphdr _arph, *ah;
 		uint32_t buf, *bp;
 
-		ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
+		ah = skb_header_pointer(skb, offset, sizeof(_arph), &_arph);
 		if (ah == NULL ||
 		    ah->ar_pln != sizeof(uint32_t) ||
 		    ah->ar_hln != ETH_ALEN)
 			return -1;
-		bp = skb_header_pointer(skb, sizeof(struct arphdr) +
+		bp = skb_header_pointer(skb, offset + sizeof(struct arphdr) +
 					ETH_ALEN, sizeof(uint32_t), &buf);
 		if (bp == NULL)
 			return -1;
Index: netfilter/ebt_arp.c
===================================================================
RCS file: /home/ljaenicke/source/mguard/linux-2.6/linux-2.6.15/net/bridge/netfilter/ebt_arp.c,v
retrieving revision 1.2
diff -u -r1.2 ebt_arp.c
--- netfilter/ebt_arp.c	16 Jun 2006 14:20:29 -0000	1.2
+++ netfilter/ebt_arp.c	19 Oct 2006 18:11:19 -0000
@@ -13,6 +13,7 @@
 #include <linux/netfilter_bridge/ebt_arp.h>
 #include <linux/if_arp.h>
 #include <linux/if_ether.h>
+#include <linux/if_vlan.h>
 #include <linux/module.h>
 
 static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
@@ -20,8 +21,9 @@
 {
 	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
 	struct arphdr _arph, *ah;
+	int offset = (skb->protocol == __constant_htons(ETH_P_8021Q)) ? VLAN_HLEN : 0;
 
-	ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
+	ah = skb_header_pointer(skb, offset, sizeof(_arph), &_arph);
 	if (ah == NULL)
 		return EBT_NOMATCH;
 	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
@@ -41,7 +43,7 @@
 		if (ah->ar_pln != sizeof(uint32_t))
 			return EBT_NOMATCH;
 		if (info->bitmask & EBT_ARP_SRC_IP) {
-			ap = skb_header_pointer(skb, sizeof(struct arphdr) +
+			ap = skb_header_pointer(skb, offset + sizeof(struct arphdr) +
 						ah->ar_hln, sizeof(_addr),
 						&_addr);
 			if (ap == NULL)
@@ -52,7 +54,7 @@
 		}
 
 		if (info->bitmask & EBT_ARP_DST_IP) {
-			ap = skb_header_pointer(skb, sizeof(struct arphdr) +
+			ap = skb_header_pointer(skb, offset + sizeof(struct arphdr) +
 						2*ah->ar_hln+sizeof(uint32_t),
 						sizeof(_addr), &_addr);
 			if (ap == NULL)
@@ -71,7 +73,7 @@
 		if (ah->ar_hln != ETH_ALEN)
 			return EBT_NOMATCH;
 		if (info->bitmask & EBT_ARP_SRC_MAC) {
-			mp = skb_header_pointer(skb, sizeof(struct arphdr),
+			mp = skb_header_pointer(skb, offset + sizeof(struct arphdr),
 						sizeof(_mac), &_mac);
 			if (mp == NULL)
 				return EBT_NOMATCH;
@@ -84,7 +86,7 @@
 		}
 
 		if (info->bitmask & EBT_ARP_DST_MAC) {
-			mp = skb_header_pointer(skb, sizeof(struct arphdr) +
+			mp = skb_header_pointer(skb, offset + sizeof(struct arphdr) +
 						ah->ar_hln + ah->ar_pln,
 						sizeof(_mac), &_mac);
 			if (mp == NULL)
Index: netfilter/ebt_arpreply.c
===================================================================
RCS file: /home/ljaenicke/source/mguard/linux-2.6/linux-2.6.15/net/bridge/netfilter/ebt_arpreply.c,v
retrieving revision 1.2
diff -u -r1.2 ebt_arpreply.c
--- netfilter/ebt_arpreply.c	16 Jun 2006 14:20:29 -0000	1.2
+++ netfilter/ebt_arpreply.c	20 Oct 2006 10:16:44 -0000
@@ -12,6 +12,7 @@
 #include <linux/netfilter_bridge/ebtables.h>
 #include <linux/netfilter_bridge/ebt_arpreply.h>
 #include <linux/if_arp.h>
+#include <linux/if_vlan.h>
 #include <net/arp.h>
 #include <linux/module.h>
 
@@ -24,8 +25,14 @@
 	struct arphdr _ah, *ap;
 	unsigned char _sha[ETH_ALEN], *shp;
 	struct sk_buff *skb = *pskb;
-
-	ap = skb_header_pointer(skb, 0, sizeof(_ah), &_ah);
+	struct sk_buff *newskb;
+	struct vlan_hdr _frame, *fp = NULL, *vhdr;
+	int offset = (skb->protocol == __constant_htons(ETH_P_8021Q)) ? VLAN_HLEN : 0;
+
+	if (offset) {
+		fp = skb_header_pointer(skb, 0, sizeof(_frame), &_frame);
+	}
+	ap = skb_header_pointer(skb, offset, sizeof(_ah), &_ah);
 	if (ap == NULL)
 		return EBT_DROP;
 
@@ -35,23 +42,32 @@
 	    ap->ar_pln != 4)
 		return EBT_CONTINUE;
 
-	shp = skb_header_pointer(skb, sizeof(_ah), ETH_ALEN, &_sha);
+	shp = skb_header_pointer(skb, offset + sizeof(_ah), ETH_ALEN, &_sha);
 	if (shp == NULL)
 		return EBT_DROP;
 
-	siptr = skb_header_pointer(skb, sizeof(_ah) + ETH_ALEN,
+	siptr = skb_header_pointer(skb, offset + sizeof(_ah) + ETH_ALEN,
 				   sizeof(_sip), &_sip);
 	if (siptr == NULL)
 		return EBT_DROP;
 
-	diptr = skb_header_pointer(skb,
+	diptr = skb_header_pointer(skb, offset +
 				   sizeof(_ah) + 2 * ETH_ALEN + sizeof(_sip),
 				   sizeof(_dip), &_dip);
 	if (diptr == NULL)
 		return EBT_DROP;
 
-	arp_send(ARPOP_REPLY, ETH_P_ARP, *siptr, (struct net_device *)in,
-	         *diptr, shp, info->mac, shp);
+	newskb = arp_create(ARPOP_REPLY, ETH_P_ARP, *siptr,
+			    (struct net_device *)in,
+			    *diptr, shp, info->mac, shp);
+	if (newskb) {
+		if (fp) {
+			vhdr = (struct vlan_hdr *) skb_push(skb, VLAN_HLEN);
+			vhdr->h_vlan_TCI = fp->h_vlan_TCI;
+			vhdr->h_vlan_encapsulated_proto = __constant_htons(ETH_P_ARP);
+		}
+		arp_xmit(skb);
+	}
 
 	return info->target;
 }
Index: netfilter/ebt_ip.c
===================================================================
RCS file: /home/ljaenicke/source/mguard/linux-2.6/linux-2.6.15/net/bridge/netfilter/ebt_ip.c,v
retrieving revision 1.2
diff -u -r1.2 ebt_ip.c
--- netfilter/ebt_ip.c	16 Jun 2006 14:20:29 -0000	1.2
+++ netfilter/ebt_ip.c	19 Oct 2006 14:57:34 -0000
@@ -18,6 +18,7 @@
 #include <net/ip.h>
 #include <linux/in.h>
 #include <linux/module.h>
+#include <linux/if_vlan.h>
 
 struct tcpudphdr {
 	uint16_t src;
@@ -31,8 +32,8 @@
 	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
 	struct iphdr _iph, *ih;
 	struct tcpudphdr _ports, *pptr;
-
-	ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
+	int offset = (skb->protocol == __constant_htons(ETH_P_8021Q)) ? VLAN_HLEN : 0;
+	ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
 	if (ih == NULL)
 		return EBT_NOMATCH;
 	if (info->bitmask & EBT_IP_TOS &&
@@ -54,7 +55,7 @@
 			return EBT_MATCH;
 		if (ntohs(ih->frag_off) & IP_OFFSET)
 			return EBT_NOMATCH;
-		pptr = skb_header_pointer(skb, ih->ihl*4,
+		pptr = skb_header_pointer(skb, offset + ih->ihl*4,
 					  sizeof(_ports), &_ports);
 		if (pptr == NULL)
 			return EBT_NOMATCH;
Index: netfilter/ebt_log.c
===================================================================
RCS file: /home/ljaenicke/source/mguard/linux-2.6/linux-2.6.15/net/bridge/netfilter/ebt_log.c,v
retrieving revision 1.2
diff -u -r1.2 ebt_log.c
--- netfilter/ebt_log.c	16 Jun 2006 14:20:29 -0000	1.2
+++ netfilter/ebt_log.c	19 Oct 2006 17:59:20 -0000
@@ -66,6 +66,8 @@
    const char *prefix)
 {
 	unsigned int bitmask;
+	__be16 h_proto = eth_hdr(skb)->h_proto;
+	int offset = 0;
 
 	spin_lock_bh(&ebt_log_lock);
 	printk("<%c>%s IN=%s OUT=%s MAC source = ", '0' + loginfo->u.log.level,
@@ -82,11 +84,10 @@
 	else
 		bitmask = NF_LOG_MASK;
 
-	if ((bitmask & EBT_LOG_IP) && eth_hdr(skb)->h_proto ==
-	   htons(ETH_P_IP)){
+	if ((bitmask & EBT_LOG_IP) && h_proto == htons(ETH_P_IP)) {
 		struct iphdr _iph, *ih;
 
-		ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
+		ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
 		if (ih == NULL) {
 			printk(" INCOMPLETE IP header");
 			goto out;
@@ -100,7 +101,7 @@
 		    ih->protocol == IPPROTO_DCCP) {
 			struct tcpudphdr _ports, *pptr;
 
-			pptr = skb_header_pointer(skb, ih->ihl*4,
+			pptr = skb_header_pointer(skb, offset + ih->ihl*4,
 						  sizeof(_ports), &_ports);
 			if (pptr == NULL) {
 				printk(" INCOMPLETE TCP/UDP header");
@@ -113,11 +114,11 @@
 	}
 
 	if ((bitmask & EBT_LOG_ARP) &&
-	    ((eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) ||
-	     (eth_hdr(skb)->h_proto == htons(ETH_P_RARP)))) {
+	    ((h_proto == htons(ETH_P_ARP)) ||
+	     (h_proto == htons(ETH_P_RARP)))) {
 		struct arphdr _arph, *ah;
 
-		ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
+		ah = skb_header_pointer(skb, offset, sizeof(_arph), &_arph);
 		if (ah == NULL) {
 			printk(" INCOMPLETE ARP header");
 			goto out;
@@ -133,7 +134,7 @@
 		    ah->ar_pln == sizeof(uint32_t)) {
 			struct arppayload _arpp, *ap;
 
-			ap = skb_header_pointer(skb, sizeof(_arph),
+			ap = skb_header_pointer(skb, offset + sizeof(_arph),
 						sizeof(_arpp), &_arpp);
 			if (ap == NULL) {
 				printk(" INCOMPLETE ARP payload");
Index: netfilter/ebtables.c
===================================================================
RCS file: /home/ljaenicke/source/mguard/linux-2.6/linux-2.6.15/net/bridge/netfilter/ebtables.c,v
retrieving revision 1.2
diff -u -r1.2 ebtables.c
--- netfilter/ebtables.c	16 Jun 2006 14:20:29 -0000	1.2
+++ netfilter/ebtables.c	19 Oct 2006 14:41:14 -0000
@@ -24,6 +24,7 @@
 #include <linux/vmalloc.h>
 #include <linux/netfilter_bridge/ebtables.h>
 #include <linux/spinlock.h>
+#include <linux/if_vlan.h>
 #include <asm/uaccess.h>
 #include <linux/smp.h>
 #include <linux/cpumask.h>
@@ -132,11 +133,20 @@
 	int verdict, i;
 
 	if (e->bitmask & EBT_802_3) {
-		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
+		unsigned short proto = h->h_proto;
+		struct vlan_ethhdr *vh = (struct vlan_ethhdr *)h;
+		if (h->h_proto == __constant_htons(ETH_P_8021Q) && ebtables_treat_vlan_transparent)
+			proto = vh->h_vlan_encapsulated_proto;
+		if (FWINV2(ntohs(proto) >= 1536, EBT_IPROTO))
 			return 1;
-	} else if (!(e->bitmask & EBT_NOPROTO) &&
-	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
+	} else if (!(e->bitmask & EBT_NOPROTO)) {
+	   unsigned short proto = h->h_proto;
+	   struct vlan_ethhdr *vh = (struct vlan_ethhdr *)h;
+	   if (h->h_proto == __constant_htons(ETH_P_8021Q) && ebtables_treat_vlan_transparent)
+		proto = vh->h_vlan_encapsulated_proto;
+	   if (FWINV2(e->ethproto != proto, EBT_IPROTO))
 		return 1;
+	}
 
 	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
 		return 1;