From patchwork Wed Jan 9 17:18:02 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vlad Yasevich X-Patchwork-Id: 210788 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 352352C03A0 for ; Thu, 10 Jan 2013 04:18:36 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932331Ab3AIRSd (ORCPT ); Wed, 9 Jan 2013 12:18:33 -0500 Received: from mx1.redhat.com ([209.132.183.28]:54210 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932317Ab3AIRS3 (ORCPT ); Wed, 9 Jan 2013 12:18:29 -0500 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r09HIOP2002936 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 9 Jan 2013 12:18:25 -0500 Received: from galen.redhat.com (ovpn-113-168.phx2.redhat.com [10.3.113.168]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id r09HI35B008687; Wed, 9 Jan 2013 12:18:23 -0500 From: Vlad Yasevich To: netdev@vger.kernel.org Cc: davem@davemloft.net, stephen@redhat.com, bridge@lists.linux-foundation.org, shmulik.ladkani@gmail.com, mst@redhat.com Subject: [PATCH net-next v5 14/14] bridge: Add vlan support for local fdb entries Date: Wed, 9 Jan 2013 12:18:02 -0500 Message-Id: <1357751882-8619-16-git-send-email-vyasevic@redhat.com> In-Reply-To: <1357751882-8619-1-git-send-email-vyasevic@redhat.com> References: <1357751882-8619-1-git-send-email-vyasevic@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.11 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org When VLAN is added to the port, a local fdb entry for that port (the entry with the mac address of the port) is added for that VLAN. This way we can correctly determine if the traffic is for the bridge itself. If the address of the port changes, we try to change all the local fdb entries we have for that port. Signed-off-by: Vlad Yasevich --- net/bridge/br_fdb.c | 62 +++++++++++++++++++++++++++++++++++----------- net/bridge/br_if.c | 27 ++++++++++++++++++-- net/bridge/br_private.h | 4 ++- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 90d0bc9..942fe49 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -28,7 +28,7 @@ static struct kmem_cache *br_fdb_cache __read_mostly; static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr); + const unsigned char *addr, u16 vid); static void fdb_notify(struct net_bridge *br, const struct net_bridge_fdb_entry *, int); @@ -92,6 +92,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) { struct net_bridge *br = p->br; + int no_vlan = list_empty(&p->vlan_info.vlan_list); int i; spin_lock_bh(&br->hash_lock); @@ -106,10 +107,13 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) if (f->dst == p && f->is_local) { /* maybe another port has same hw addr? */ struct net_bridge_port *op; + u16 vid = f->vlan_id; list_for_each_entry(op, &br->port_list, list) { if (op != p && ether_addr_equal(op->dev->dev_addr, - f->addr.addr)) { + f->addr.addr) && + nbp_vlan_find(&op->vlan_info, + vid)) { f->dst = op; goto insert; } @@ -117,27 +121,55 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) /* delete old one */ fdb_delete(br, f); - goto insert; +insert: + /* insert new address, may fail if invalid + * address or dup. + */ + fdb_insert(br, p, newaddr, vid); + + /* if this port has no vlan information + * configured, we can safely be done at + * this point. + */ + if (no_vlan) + goto done; } } } - insert: - /* insert new address, may fail if invalid address or dup. */ - fdb_insert(br, p, newaddr); +done: spin_unlock_bh(&br->hash_lock); } void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) { struct net_bridge_fdb_entry *f; + struct net_bridge_vlan *vlan; + struct hlist_node *node; + int i; /* If old entry was unassociated with any port, then delete it. */ f = __br_fdb_get(br, br->dev->dev_addr, 0); if (f && f->is_local && !f->dst) fdb_delete(br, f); - fdb_insert(br, NULL, newaddr); + fdb_insert(br, NULL, newaddr, 0); + + /* Now remove and add entries for every VLAN configured on the + * bridge. + */ + for (i = 0; i < BR_VID_HASH_SIZE; i++) { + hlist_for_each_entry_rcu(vlan, node, + &br->vlan_hlist[i], hlist) { + if (vlan->vid == BR_INVALID_VID || vlan->vid == 0) + continue; + + f = __br_fdb_get(br, br->dev->dev_addr, vlan->vid); + if (f && f->is_local && !f->dst) + fdb_delete(br, f); + fdb_insert(br, NULL, newaddr, vlan->vid); + } + } } void br_fdb_cleanup(unsigned long _data) @@ -380,15 +412,15 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, } static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr) + const unsigned char *addr, u16 vid) { - struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)]; + struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; struct net_bridge_fdb_entry *fdb; if (!is_valid_ether_addr(addr)) return -EINVAL; - fdb = fdb_find(head, addr, 0); + fdb = fdb_find(head, addr, vid); if (fdb) { /* it is okay to have multiple ports with same * address, just use the first one. @@ -401,7 +433,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, fdb_delete(br, fdb); } - fdb = fdb_create(head, source, addr, 0); + fdb = fdb_create(head, source, addr, vid); if (!fdb) return -ENOMEM; @@ -412,12 +444,12 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, /* Add entry for local address of interface */ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr) + const unsigned char *addr, u16 vid) { int ret; spin_lock_bh(&br->hash_lock); - ret = fdb_insert(br, source, addr); + ret = fdb_insert(br, source, addr, vid); spin_unlock_bh(&br->hash_lock); return ret; } @@ -710,8 +742,8 @@ out: return err; } -static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, - u16 vlan) +int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, + u16 vlan) { struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)]; struct net_bridge_fdb_entry *fdb; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index e846f2f..159b8bf 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -333,6 +333,14 @@ int nbp_vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) if (err) goto del_vlan; + err = br_fdb_insert(br, p, + (p ? p->dev->dev_addr : br->dev->dev_addr), vid); + if (err) { + br_err(br, + "failed insert local address bridge forwarding table\n"); + goto del_vlan; + } + return 0; clean_up: @@ -350,13 +358,17 @@ int nbp_vlan_delete(struct net_port_vlans *v, u16 vid) struct net_port_vlan *pve; struct net_bridge_vlan *vlan; struct net_bridge *br; + unsigned char *addr; ASSERT_RTNL(); - if (v->port_idx) + if (v->port_idx) { br = vlans_to_port(v)->br; - else + addr = vlans_to_port(v)->dev->dev_addr; + } else { br = vlans_to_bridge(v); + addr = br->dev->dev_addr; + } pve = nbp_vlan_find(v, vid); if (!pve) @@ -377,6 +389,15 @@ int nbp_vlan_delete(struct net_port_vlans *v, u16 vid) vid, dev->name); } + if (vid) { + /* If the VID !=0 remove fdb for this vid. VID 0 is special + * in that it's the default and is always there in the fdb. + */ + spin_lock_bh(&br->hash_lock); + fdb_delete_by_addr(br, addr, vid); + spin_unlock_bh(&br->hash_lock); + } + pve->vid = BR_INVALID_VID; rcu_assign_pointer(pve->vlan, NULL); @@ -719,7 +740,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) dev_set_mtu(br->dev, br_min_mtu(br)); - if (br_fdb_insert(br, p, dev->dev_addr)) + if (br_fdb_insert(br, p, dev->dev_addr, 0)) netdev_err(dev, "failed insert local address bridge forwarding table\n"); kobject_uevent(&p->kobj, KOBJ_ADD); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 92b695e..9b74883 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -437,11 +437,13 @@ extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count, unsigned long off); extern int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr); + const unsigned char *addr, + u16 vid); extern void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr, u16 vid); +extern int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid); extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev,