aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_fdb.c
diff options
context:
space:
mode:
authorVlad Yasevich <vyasevic@redhat.com>2013-02-13 07:00:19 -0500
committerDavid S. Miller <davem@davemloft.net>2013-02-13 19:42:16 -0500
commitbc9a25d21ef8bad30e259af5114ccfb845c066db (patch)
tree70b44a0a84ecf60c227af9bb35c43c2f7c718d5e /net/bridge/br_fdb.c
parent1690be63a27b20ae65c792729a44f5970561ffa4 (diff)
bridge: Add vlan support for local fdb entries
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 <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_fdb.c')
-rw-r--r--net/bridge/br_fdb.c61
1 files changed, 46 insertions, 15 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 4b75ad43aa85..8117900af4de 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -28,7 +28,7 @@
28 28
29static struct kmem_cache *br_fdb_cache __read_mostly; 29static struct kmem_cache *br_fdb_cache __read_mostly;
30static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, 30static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
31 const unsigned char *addr); 31 const unsigned char *addr, u16 vid);
32static void fdb_notify(struct net_bridge *br, 32static void fdb_notify(struct net_bridge *br,
33 const struct net_bridge_fdb_entry *, int); 33 const struct net_bridge_fdb_entry *, int);
34 34
@@ -92,6 +92,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
92void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) 92void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
93{ 93{
94 struct net_bridge *br = p->br; 94 struct net_bridge *br = p->br;
95 bool no_vlan = (nbp_get_vlan_info(p) == NULL) ? true : false;
95 int i; 96 int i;
96 97
97 spin_lock_bh(&br->hash_lock); 98 spin_lock_bh(&br->hash_lock);
@@ -106,10 +107,12 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
106 if (f->dst == p && f->is_local) { 107 if (f->dst == p && f->is_local) {
107 /* maybe another port has same hw addr? */ 108 /* maybe another port has same hw addr? */
108 struct net_bridge_port *op; 109 struct net_bridge_port *op;
110 u16 vid = f->vlan_id;
109 list_for_each_entry(op, &br->port_list, list) { 111 list_for_each_entry(op, &br->port_list, list) {
110 if (op != p && 112 if (op != p &&
111 ether_addr_equal(op->dev->dev_addr, 113 ether_addr_equal(op->dev->dev_addr,
112 f->addr.addr)) { 114 f->addr.addr) &&
115 nbp_vlan_find(op, vid)) {
113 f->dst = op; 116 f->dst = op;
114 goto insert; 117 goto insert;
115 } 118 }
@@ -117,27 +120,55 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
117 120
118 /* delete old one */ 121 /* delete old one */
119 fdb_delete(br, f); 122 fdb_delete(br, f);
120 goto insert; 123insert:
124 /* insert new address, may fail if invalid
125 * address or dup.
126 */
127 fdb_insert(br, p, newaddr, vid);
128
129 /* if this port has no vlan information
130 * configured, we can safely be done at
131 * this point.
132 */
133 if (no_vlan)
134 goto done;
121 } 135 }
122 } 136 }
123 } 137 }
124 insert:
125 /* insert new address, may fail if invalid address or dup. */
126 fdb_insert(br, p, newaddr);
127 138
139done:
128 spin_unlock_bh(&br->hash_lock); 140 spin_unlock_bh(&br->hash_lock);
129} 141}
130 142
131void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) 143void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
132{ 144{
133 struct net_bridge_fdb_entry *f; 145 struct net_bridge_fdb_entry *f;
146 struct net_port_vlans *pv;
147 u16 vid = 0;
134 148
135 /* If old entry was unassociated with any port, then delete it. */ 149 /* If old entry was unassociated with any port, then delete it. */
136 f = __br_fdb_get(br, br->dev->dev_addr, 0); 150 f = __br_fdb_get(br, br->dev->dev_addr, 0);
137 if (f && f->is_local && !f->dst) 151 if (f && f->is_local && !f->dst)
138 fdb_delete(br, f); 152 fdb_delete(br, f);
139 153
140 fdb_insert(br, NULL, newaddr); 154 fdb_insert(br, NULL, newaddr, 0);
155
156 /* Now remove and add entries for every VLAN configured on the
157 * bridge. This function runs under RTNL so the bitmap will not
158 * change from under us.
159 */
160 pv = br_get_vlan_info(br);
161 if (!pv)
162 return;
163
164 for (vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid);
165 vid < BR_VLAN_BITMAP_LEN;
166 vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid+1)) {
167 f = __br_fdb_get(br, br->dev->dev_addr, vid);
168 if (f && f->is_local && !f->dst)
169 fdb_delete(br, f);
170 fdb_insert(br, NULL, newaddr, vid);
171 }
141} 172}
142 173
143void br_fdb_cleanup(unsigned long _data) 174void br_fdb_cleanup(unsigned long _data)
@@ -379,15 +410,15 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
379} 410}
380 411
381static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, 412static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
382 const unsigned char *addr) 413 const unsigned char *addr, u16 vid)
383{ 414{
384 struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)]; 415 struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];
385 struct net_bridge_fdb_entry *fdb; 416 struct net_bridge_fdb_entry *fdb;
386 417
387 if (!is_valid_ether_addr(addr)) 418 if (!is_valid_ether_addr(addr))
388 return -EINVAL; 419 return -EINVAL;
389 420
390 fdb = fdb_find(head, addr, 0); 421 fdb = fdb_find(head, addr, vid);
391 if (fdb) { 422 if (fdb) {
392 /* it is okay to have multiple ports with same 423 /* it is okay to have multiple ports with same
393 * address, just use the first one. 424 * address, just use the first one.
@@ -400,7 +431,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
400 fdb_delete(br, fdb); 431 fdb_delete(br, fdb);
401 } 432 }
402 433
403 fdb = fdb_create(head, source, addr, 0); 434 fdb = fdb_create(head, source, addr, vid);
404 if (!fdb) 435 if (!fdb)
405 return -ENOMEM; 436 return -ENOMEM;
406 437
@@ -411,12 +442,12 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
411 442
412/* Add entry for local address of interface */ 443/* Add entry for local address of interface */
413int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, 444int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
414 const unsigned char *addr) 445 const unsigned char *addr, u16 vid)
415{ 446{
416 int ret; 447 int ret;
417 448
418 spin_lock_bh(&br->hash_lock); 449 spin_lock_bh(&br->hash_lock);
419 ret = fdb_insert(br, source, addr); 450 ret = fdb_insert(br, source, addr, vid);
420 spin_unlock_bh(&br->hash_lock); 451 spin_unlock_bh(&br->hash_lock);
421 return ret; 452 return ret;
422} 453}
@@ -712,8 +743,8 @@ out:
712 return err; 743 return err;
713} 744}
714 745
715static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, 746int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr,
716 u16 vlan) 747 u16 vlan)
717{ 748{
718 struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)]; 749 struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)];
719 struct net_bridge_fdb_entry *fdb; 750 struct net_bridge_fdb_entry *fdb;