Stephen Hemminger wrote:
What about the nested vlan case?
Below is a new patch that handles the double-tagging case. I'm not sure if it is worth a more generic case. ¿Are triple-tagging and so really used?
This is a user/kernel ABI change. Does it break old tools?
New patch gets rid of the unused field but it still doesn't break old tools. Anyway, the user part is not really needed. I just think it could be useful. Regards, Jaime. --- Subject: [PATCH] Add vlan id to bridge forward database This makes forwarding table aware of 802.1Q vlan ids and stores id with MACs in the table. Up to two vlan tags are handled. It solves problems when having same MAC on diffent pairs (vlan, port). Current code gets confused at that situation. Local MACs are managed as present on every vlan. Signed-off-by: Jaime Medrano <jaime.medrano@xxxxxxxxx> --- include/linux/if_bridge.h | 3 ++- net/atm/lec.c | 2 +- net/bridge/br_device.c | 23 +++++++++++++++++++---- net/bridge/br_fdb.c | 42 ++++++++++++++++++++++++++++++------------ net/bridge/br_input.c | 31 +++++++++++++++++++++++++++---- net/bridge/br_private.h | 18 ++++++++++++++---- 6 files changed, 93 insertions(+), 26 deletions(-) Index: linux-2.6.23/net/bridge/br_private.h =================================================================== --- linux-2.6.23.orig/net/bridge/br_private.h 2007-12-18 09:42:45.000000000 +0100 +++ linux-2.6.23/net/bridge/br_private.h 2007-12-18 09:43:37.000000000 +0100 @@ -55,6 +55,8 @@ atomic_t use_count; unsigned long ageing_timer; mac_addr addr; + unsigned short vlan_first_id; + unsigned short vlan_second_id; unsigned char is_local; unsigned char is_static; }; @@ -150,9 +152,13 @@ extern void br_fdb_delete_by_port(struct net_bridge *br, const struct net_bridge_port *p, int do_all); extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, - const unsigned char *addr); + const unsigned char *addr, + unsigned short vlan_first_id, + unsigned short vlan_second_id); extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, - unsigned char *addr); + unsigned char *addr, + unsigned short vlan_first_id, + unsigned short vlan_second_id); extern void br_fdb_put(struct net_bridge_fdb_entry *ent); extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count, unsigned long off); @@ -161,7 +167,9 @@ const unsigned char *addr); extern void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr); + const unsigned char *addr, + unsigned short vlan_first_id, + unsigned short vlan_second_id); /* br_forward.c */ extern void br_deliver(const struct net_bridge_port *to, @@ -237,7 +245,9 @@ /* br.c */ extern struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br, - unsigned char *addr); + unsigned char *addr, + unsigned short vlan_first_id, + unsigned short vlan_second_id); extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent); Index: linux-2.6.23/net/bridge/br_device.c =================================================================== --- linux-2.6.23.orig/net/bridge/br_device.c 2007-12-18 09:42:45.000000000 +0100 +++ linux-2.6.23/net/bridge/br_device.c 2007-12-18 09:43:37.000000000 +0100 @@ -17,6 +17,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> +#include <linux/if_vlan.h> #include <asm/uaccess.h> #include "br_private.h" @@ -42,10 +43,24 @@ if (dest[0] & 1) br_flood_deliver(br, skb); - else if ((dst = __br_fdb_get(br, dest)) != NULL) - br_deliver(dst->dst, skb); - else - br_flood_deliver(br, skb); + else { + struct vlan_ethhdr *veth = vlan_eth_hdr(skb); + unsigned short vlan_first_id = 0; + unsigned short vlan_second_id = 0; + if (veth->h_vlan_proto == __constant_htons(ETH_P_8021Q)) { + vlan_first_id = ntohs(veth->h_vlan_TCI) & VLAN_VID_MASK; + if (veth->h_vlan_encapsulated_proto == + __constant_htons(ETH_P_8021Q)) { + struct vlan_hdr *veth2 = (struct vlan_hdr *) (veth+1); + vlan_second_id = ntohs(veth2->h_vlan_TCI) & VLAN_VID_MASK; + } + } + if ((dst = __br_fdb_get(br, dest, vlan_first_id, + vlan_second_id)) != NULL) + br_deliver(dst->dst, skb); + else + br_flood_deliver(br, skb); + } return 0; } Index: linux-2.6.23/net/bridge/br_fdb.c =================================================================== --- linux-2.6.23.orig/net/bridge/br_fdb.c 2007-12-18 09:42:45.000000000 +0100 +++ linux-2.6.23/net/bridge/br_fdb.c 2007-12-18 09:43:37.000000000 +0100 @@ -211,13 +211,17 @@ /* No locking or refcounting, assumes caller has no preempt (rcu_read_lock) */ struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, - const unsigned char *addr) + const unsigned char *addr, + unsigned short vlan_first_id, + unsigned short vlan_second_id) { struct hlist_node *h; struct net_bridge_fdb_entry *fdb; hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) { - if (!compare_ether_addr(fdb->addr.addr, addr)) { + if (!compare_ether_addr(fdb->addr.addr, addr) + && (fdb->is_local || (fdb->vlan_first_id == vlan_first_id && + fdb->vlan_second_id == vlan_second_id))) { if (unlikely(has_expired(br, fdb))) break; return fdb; @@ -229,12 +233,14 @@ /* Interface used by ATM hook that keeps a ref count */ struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, - unsigned char *addr) + unsigned char *addr, + unsigned short vlan_first_id, + unsigned short vlan_second_id) { struct net_bridge_fdb_entry *fdb; rcu_read_lock(); - fdb = __br_fdb_get(br, addr); + fdb = __br_fdb_get(br, addr, vlan_first_id, vlan_second_id); if (fdb && !atomic_inc_not_zero(&fdb->use_count)) fdb = NULL; rcu_read_unlock(); @@ -289,6 +295,8 @@ fe->is_local = f->is_local; if (!f->is_static) fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->ageing_timer); + fe->vlan_first_id = f->vlan_first_id; + fe->vlan_second_id = f->vlan_second_id; ++fe; ++num; } @@ -301,13 +309,17 @@ } static inline struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, - const unsigned char *addr) + const unsigned char *addr, + unsigned short vlan_first_id, + unsigned short vlan_second_id) { struct hlist_node *h; struct net_bridge_fdb_entry *fdb; hlist_for_each_entry_rcu(fdb, h, head, hlist) { - if (!compare_ether_addr(fdb->addr.addr, addr)) + if (!compare_ether_addr(fdb->addr.addr, addr) + && (fdb->is_local || (fdb->vlan_first_id == vlan_first_id + && fdb->vlan_second_id == vlan_second_id))) return fdb; } return NULL; @@ -316,6 +328,8 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, struct net_bridge_port *source, const unsigned char *addr, + unsigned short vlan_first_id, + unsigned short vlan_second_id, int is_local) { struct net_bridge_fdb_entry *fdb; @@ -323,6 +337,8 @@ fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC); if (fdb) { memcpy(fdb->addr.addr, addr, ETH_ALEN); + fdb->vlan_first_id = vlan_first_id; + fdb->vlan_second_id = vlan_second_id; atomic_set(&fdb->use_count, 1); hlist_add_head_rcu(&fdb->hlist, head); @@ -343,7 +359,7 @@ if (!is_valid_ether_addr(addr)) return -EINVAL; - fdb = fdb_find(head, addr); + fdb = fdb_find(head, addr, 0, 0); if (fdb) { /* it is okay to have multiple ports with same * address, just use the first one. @@ -357,7 +373,7 @@ fdb_delete(fdb); } - if (!fdb_create(head, source, addr, 1)) + if (!fdb_create(head, source, addr, 0, 0, 1)) return -ENOMEM; return 0; @@ -375,7 +391,8 @@ } void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr) + const unsigned char *addr, unsigned short vlan_first_id, + unsigned short vlan_second_id) { struct hlist_head *head = &br->hash[br_mac_hash(addr)]; struct net_bridge_fdb_entry *fdb; @@ -389,7 +406,7 @@ source->state == BR_STATE_FORWARDING)) return; - fdb = fdb_find(head, addr); + fdb = fdb_find(head, addr, vlan_first_id, vlan_second_id); if (likely(fdb)) { /* attempt to update an entry for a local interface */ if (unlikely(fdb->is_local)) { @@ -404,8 +421,9 @@ } } else { spin_lock(&br->hash_lock); - if (!fdb_find(head, addr)) - fdb_create(head, source, addr, 0); + if (!fdb_find(head, addr, vlan_first_id, vlan_second_id)) + fdb_create(head, source, addr, vlan_first_id, + vlan_second_id, 0); /* else we lose race and someone else inserts * it first, don't bother updating */ Index: linux-2.6.23/net/bridge/br_input.c =================================================================== --- linux-2.6.23.orig/net/bridge/br_input.c 2007-12-18 09:42:45.000000000 +0100 +++ linux-2.6.23/net/bridge/br_input.c 2007-12-18 09:43:37.000000000 +0100 @@ -17,6 +17,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/netfilter_bridge.h> +#include <linux/if_vlan.h> #include "br_private.h" /* Bridge group multicast address 802.1d (pg 51). */ @@ -44,13 +45,24 @@ struct net_bridge *br; struct net_bridge_fdb_entry *dst; struct sk_buff *skb2; + struct vlan_ethhdr *veth = vlan_eth_hdr(skb); + unsigned short vlan_first_id = 0; + unsigned short vlan_second_id = 0; if (!p || p->state == BR_STATE_DISABLED) goto drop; /* insert into forwarding database after filtering to avoid spoofing */ + if (veth->h_vlan_proto == __constant_htons(ETH_P_8021Q)) { + vlan_first_id = ntohs(veth->h_vlan_TCI) & VLAN_VID_MASK; + if (veth->h_vlan_encapsulated_proto == + __constant_htons(ETH_P_8021Q)) { + struct vlan_hdr *veth2 = (struct vlan_hdr *) (veth+1); + vlan_second_id = ntohs(veth2->h_vlan_TCI) & VLAN_VID_MASK; + } + } br = p->br; - br_fdb_update(br, p, eth_hdr(skb)->h_source); + br_fdb_update(br, p, eth_hdr(skb)->h_source, vlan_first_id, vlan_second_id); if (p->state == BR_STATE_LEARNING) goto drop; @@ -66,7 +78,7 @@ if (is_multicast_ether_addr(dest)) { br->statistics.multicast++; skb2 = skb; - } else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) { + } else if ((dst = __br_fdb_get(br, dest, vlan_first_id, vlan_second_id)) && dst->is_local) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; @@ -96,9 +108,20 @@ static int br_handle_local_finish(struct sk_buff *skb) { struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); - + struct vlan_ethhdr *veth = vlan_eth_hdr(skb); + unsigned short vlan_first_id = 0; + unsigned short vlan_second_id = 0; + + if (veth->h_vlan_proto == __constant_htons(ETH_P_8021Q)) { + vlan_first_id = ntohs(veth->h_vlan_TCI) & VLAN_VID_MASK; + if (veth->h_vlan_encapsulated_proto == + __constant_htons(ETH_P_8021Q)) { + struct vlan_hdr *veth2 = (struct vlan_hdr *) (veth+1); + vlan_second_id = ntohs(veth2->h_vlan_TCI) & VLAN_VID_MASK; + } + } if (p) - br_fdb_update(p->br, p, eth_hdr(skb)->h_source); + br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vlan_first_id, vlan_second_id); return 0; /* process further */ } Index: linux-2.6.23/net/atm/lec.c =================================================================== --- linux-2.6.23.orig/net/atm/lec.c 2007-12-18 09:42:45.000000000 +0100 +++ linux-2.6.23/net/atm/lec.c 2007-12-18 09:43:37.000000000 +0100 @@ -557,7 +557,7 @@ break; f = br_fdb_get_hook(dev->br_port->br, - mesg->content.proxy.mac_addr); + mesg->content.proxy.mac_addr, 0, 0); if (f != NULL && f->dst->dev != dev && f->dst->state == BR_STATE_FORWARDING) { /* hit from bridge table, send LE_ARP_RESPONSE */ Index: linux-2.6.23/include/linux/if_bridge.h =================================================================== --- linux-2.6.23.orig/include/linux/if_bridge.h 2007-12-18 09:42:45.000000000 +0100 +++ linux-2.6.23/include/linux/if_bridge.h 2007-12-18 09:43:37.000000000 +0100 @@ -97,7 +97,8 @@ __u8 port_no; __u8 is_local; __u32 ageing_timer_value; - __u32 unused; + __u16 vlan_first_id; + __u16 vlan_second_id; }; #ifdef __KERNEL__ -- _______________________________________________ Bridge mailing list Bridge@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/bridge