diff options
Diffstat (limited to 'net/bridge/br_fdb.c')
-rw-r--r-- | net/bridge/br_fdb.c | 258 |
1 files changed, 207 insertions, 51 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index d9576e6de2b8..8117900af4de 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c | |||
@@ -23,11 +23,12 @@ | |||
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/atomic.h> | 24 | #include <linux/atomic.h> |
25 | #include <asm/unaligned.h> | 25 | #include <asm/unaligned.h> |
26 | #include <linux/if_vlan.h> | ||
26 | #include "br_private.h" | 27 | #include "br_private.h" |
27 | 28 | ||
28 | static struct kmem_cache *br_fdb_cache __read_mostly; | 29 | static struct kmem_cache *br_fdb_cache __read_mostly; |
29 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | 30 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, |
30 | const unsigned char *addr); | 31 | const unsigned char *addr, u16 vid); |
31 | static void fdb_notify(struct net_bridge *br, | 32 | static void fdb_notify(struct net_bridge *br, |
32 | const struct net_bridge_fdb_entry *, int); | 33 | const struct net_bridge_fdb_entry *, int); |
33 | 34 | ||
@@ -67,11 +68,11 @@ static inline int has_expired(const struct net_bridge *br, | |||
67 | time_before_eq(fdb->updated + hold_time(br), jiffies); | 68 | time_before_eq(fdb->updated + hold_time(br), jiffies); |
68 | } | 69 | } |
69 | 70 | ||
70 | static inline int br_mac_hash(const unsigned char *mac) | 71 | static inline int br_mac_hash(const unsigned char *mac, __u16 vid) |
71 | { | 72 | { |
72 | /* use 1 byte of OUI cnd 3 bytes of NIC */ | 73 | /* use 1 byte of OUI and 3 bytes of NIC */ |
73 | u32 key = get_unaligned((u32 *)(mac + 2)); | 74 | u32 key = get_unaligned((u32 *)(mac + 2)); |
74 | return jhash_1word(key, fdb_salt) & (BR_HASH_SIZE - 1); | 75 | return jhash_2words(key, vid, fdb_salt) & (BR_HASH_SIZE - 1); |
75 | } | 76 | } |
76 | 77 | ||
77 | static void fdb_rcu_free(struct rcu_head *head) | 78 | static void fdb_rcu_free(struct rcu_head *head) |
@@ -91,6 +92,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) | |||
91 | void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) | 92 | void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) |
92 | { | 93 | { |
93 | 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; | ||
94 | int i; | 96 | int i; |
95 | 97 | ||
96 | spin_lock_bh(&br->hash_lock); | 98 | spin_lock_bh(&br->hash_lock); |
@@ -105,10 +107,12 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) | |||
105 | if (f->dst == p && f->is_local) { | 107 | if (f->dst == p && f->is_local) { |
106 | /* maybe another port has same hw addr? */ | 108 | /* maybe another port has same hw addr? */ |
107 | struct net_bridge_port *op; | 109 | struct net_bridge_port *op; |
110 | u16 vid = f->vlan_id; | ||
108 | list_for_each_entry(op, &br->port_list, list) { | 111 | list_for_each_entry(op, &br->port_list, list) { |
109 | if (op != p && | 112 | if (op != p && |
110 | ether_addr_equal(op->dev->dev_addr, | 113 | ether_addr_equal(op->dev->dev_addr, |
111 | f->addr.addr)) { | 114 | f->addr.addr) && |
115 | nbp_vlan_find(op, vid)) { | ||
112 | f->dst = op; | 116 | f->dst = op; |
113 | goto insert; | 117 | goto insert; |
114 | } | 118 | } |
@@ -116,27 +120,55 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) | |||
116 | 120 | ||
117 | /* delete old one */ | 121 | /* delete old one */ |
118 | fdb_delete(br, f); | 122 | fdb_delete(br, f); |
119 | goto insert; | 123 | insert: |
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; | ||
120 | } | 135 | } |
121 | } | 136 | } |
122 | } | 137 | } |
123 | insert: | ||
124 | /* insert new address, may fail if invalid address or dup. */ | ||
125 | fdb_insert(br, p, newaddr); | ||
126 | 138 | ||
139 | done: | ||
127 | spin_unlock_bh(&br->hash_lock); | 140 | spin_unlock_bh(&br->hash_lock); |
128 | } | 141 | } |
129 | 142 | ||
130 | void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) | 143 | void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) |
131 | { | 144 | { |
132 | struct net_bridge_fdb_entry *f; | 145 | struct net_bridge_fdb_entry *f; |
146 | struct net_port_vlans *pv; | ||
147 | u16 vid = 0; | ||
133 | 148 | ||
134 | /* If old entry was unassociated with any port, then delete it. */ | 149 | /* If old entry was unassociated with any port, then delete it. */ |
135 | f = __br_fdb_get(br, br->dev->dev_addr); | 150 | f = __br_fdb_get(br, br->dev->dev_addr, 0); |
136 | if (f && f->is_local && !f->dst) | 151 | if (f && f->is_local && !f->dst) |
137 | fdb_delete(br, f); | 152 | fdb_delete(br, f); |
138 | 153 | ||
139 | 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 | } | ||
140 | } | 172 | } |
141 | 173 | ||
142 | void br_fdb_cleanup(unsigned long _data) | 174 | void br_fdb_cleanup(unsigned long _data) |
@@ -231,13 +263,16 @@ void br_fdb_delete_by_port(struct net_bridge *br, | |||
231 | 263 | ||
232 | /* No locking or refcounting, assumes caller has rcu_read_lock */ | 264 | /* No locking or refcounting, assumes caller has rcu_read_lock */ |
233 | struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, | 265 | struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, |
234 | const unsigned char *addr) | 266 | const unsigned char *addr, |
267 | __u16 vid) | ||
235 | { | 268 | { |
236 | struct hlist_node *h; | 269 | struct hlist_node *h; |
237 | struct net_bridge_fdb_entry *fdb; | 270 | struct net_bridge_fdb_entry *fdb; |
238 | 271 | ||
239 | hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) { | 272 | hlist_for_each_entry_rcu(fdb, h, |
240 | if (ether_addr_equal(fdb->addr.addr, addr)) { | 273 | &br->hash[br_mac_hash(addr, vid)], hlist) { |
274 | if (ether_addr_equal(fdb->addr.addr, addr) && | ||
275 | fdb->vlan_id == vid) { | ||
241 | if (unlikely(has_expired(br, fdb))) | 276 | if (unlikely(has_expired(br, fdb))) |
242 | break; | 277 | break; |
243 | return fdb; | 278 | return fdb; |
@@ -261,7 +296,7 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr) | |||
261 | if (!port) | 296 | if (!port) |
262 | ret = 0; | 297 | ret = 0; |
263 | else { | 298 | else { |
264 | fdb = __br_fdb_get(port->br, addr); | 299 | fdb = __br_fdb_get(port->br, addr, 0); |
265 | ret = fdb && fdb->dst && fdb->dst->dev != dev && | 300 | ret = fdb && fdb->dst && fdb->dst->dev != dev && |
266 | fdb->dst->state == BR_STATE_FORWARDING; | 301 | fdb->dst->state == BR_STATE_FORWARDING; |
267 | } | 302 | } |
@@ -325,26 +360,30 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf, | |||
325 | } | 360 | } |
326 | 361 | ||
327 | static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, | 362 | static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, |
328 | const unsigned char *addr) | 363 | const unsigned char *addr, |
364 | __u16 vid) | ||
329 | { | 365 | { |
330 | struct hlist_node *h; | 366 | struct hlist_node *h; |
331 | struct net_bridge_fdb_entry *fdb; | 367 | struct net_bridge_fdb_entry *fdb; |
332 | 368 | ||
333 | hlist_for_each_entry(fdb, h, head, hlist) { | 369 | hlist_for_each_entry(fdb, h, head, hlist) { |
334 | if (ether_addr_equal(fdb->addr.addr, addr)) | 370 | if (ether_addr_equal(fdb->addr.addr, addr) && |
371 | fdb->vlan_id == vid) | ||
335 | return fdb; | 372 | return fdb; |
336 | } | 373 | } |
337 | return NULL; | 374 | return NULL; |
338 | } | 375 | } |
339 | 376 | ||
340 | static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head, | 377 | static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head, |
341 | const unsigned char *addr) | 378 | const unsigned char *addr, |
379 | __u16 vid) | ||
342 | { | 380 | { |
343 | struct hlist_node *h; | 381 | struct hlist_node *h; |
344 | struct net_bridge_fdb_entry *fdb; | 382 | struct net_bridge_fdb_entry *fdb; |
345 | 383 | ||
346 | hlist_for_each_entry_rcu(fdb, h, head, hlist) { | 384 | hlist_for_each_entry_rcu(fdb, h, head, hlist) { |
347 | if (ether_addr_equal(fdb->addr.addr, addr)) | 385 | if (ether_addr_equal(fdb->addr.addr, addr) && |
386 | fdb->vlan_id == vid) | ||
348 | return fdb; | 387 | return fdb; |
349 | } | 388 | } |
350 | return NULL; | 389 | return NULL; |
@@ -352,7 +391,8 @@ static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head, | |||
352 | 391 | ||
353 | static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, | 392 | static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, |
354 | struct net_bridge_port *source, | 393 | struct net_bridge_port *source, |
355 | const unsigned char *addr) | 394 | const unsigned char *addr, |
395 | __u16 vid) | ||
356 | { | 396 | { |
357 | struct net_bridge_fdb_entry *fdb; | 397 | struct net_bridge_fdb_entry *fdb; |
358 | 398 | ||
@@ -360,6 +400,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, | |||
360 | if (fdb) { | 400 | if (fdb) { |
361 | memcpy(fdb->addr.addr, addr, ETH_ALEN); | 401 | memcpy(fdb->addr.addr, addr, ETH_ALEN); |
362 | fdb->dst = source; | 402 | fdb->dst = source; |
403 | fdb->vlan_id = vid; | ||
363 | fdb->is_local = 0; | 404 | fdb->is_local = 0; |
364 | fdb->is_static = 0; | 405 | fdb->is_static = 0; |
365 | fdb->updated = fdb->used = jiffies; | 406 | fdb->updated = fdb->used = jiffies; |
@@ -369,15 +410,15 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, | |||
369 | } | 410 | } |
370 | 411 | ||
371 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | 412 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, |
372 | const unsigned char *addr) | 413 | const unsigned char *addr, u16 vid) |
373 | { | 414 | { |
374 | struct hlist_head *head = &br->hash[br_mac_hash(addr)]; | 415 | struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; |
375 | struct net_bridge_fdb_entry *fdb; | 416 | struct net_bridge_fdb_entry *fdb; |
376 | 417 | ||
377 | if (!is_valid_ether_addr(addr)) | 418 | if (!is_valid_ether_addr(addr)) |
378 | return -EINVAL; | 419 | return -EINVAL; |
379 | 420 | ||
380 | fdb = fdb_find(head, addr); | 421 | fdb = fdb_find(head, addr, vid); |
381 | if (fdb) { | 422 | if (fdb) { |
382 | /* it is okay to have multiple ports with same | 423 | /* it is okay to have multiple ports with same |
383 | * address, just use the first one. | 424 | * address, just use the first one. |
@@ -390,7 +431,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | |||
390 | fdb_delete(br, fdb); | 431 | fdb_delete(br, fdb); |
391 | } | 432 | } |
392 | 433 | ||
393 | fdb = fdb_create(head, source, addr); | 434 | fdb = fdb_create(head, source, addr, vid); |
394 | if (!fdb) | 435 | if (!fdb) |
395 | return -ENOMEM; | 436 | return -ENOMEM; |
396 | 437 | ||
@@ -401,20 +442,20 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | |||
401 | 442 | ||
402 | /* Add entry for local address of interface */ | 443 | /* Add entry for local address of interface */ |
403 | int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | 444 | int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, |
404 | const unsigned char *addr) | 445 | const unsigned char *addr, u16 vid) |
405 | { | 446 | { |
406 | int ret; | 447 | int ret; |
407 | 448 | ||
408 | spin_lock_bh(&br->hash_lock); | 449 | spin_lock_bh(&br->hash_lock); |
409 | ret = fdb_insert(br, source, addr); | 450 | ret = fdb_insert(br, source, addr, vid); |
410 | spin_unlock_bh(&br->hash_lock); | 451 | spin_unlock_bh(&br->hash_lock); |
411 | return ret; | 452 | return ret; |
412 | } | 453 | } |
413 | 454 | ||
414 | void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, | 455 | void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, |
415 | const unsigned char *addr) | 456 | const unsigned char *addr, u16 vid) |
416 | { | 457 | { |
417 | struct hlist_head *head = &br->hash[br_mac_hash(addr)]; | 458 | struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; |
418 | struct net_bridge_fdb_entry *fdb; | 459 | struct net_bridge_fdb_entry *fdb; |
419 | 460 | ||
420 | /* some users want to always flood. */ | 461 | /* some users want to always flood. */ |
@@ -426,7 +467,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, | |||
426 | source->state == BR_STATE_FORWARDING)) | 467 | source->state == BR_STATE_FORWARDING)) |
427 | return; | 468 | return; |
428 | 469 | ||
429 | fdb = fdb_find_rcu(head, addr); | 470 | fdb = fdb_find_rcu(head, addr, vid); |
430 | if (likely(fdb)) { | 471 | if (likely(fdb)) { |
431 | /* attempt to update an entry for a local interface */ | 472 | /* attempt to update an entry for a local interface */ |
432 | if (unlikely(fdb->is_local)) { | 473 | if (unlikely(fdb->is_local)) { |
@@ -441,8 +482,8 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, | |||
441 | } | 482 | } |
442 | } else { | 483 | } else { |
443 | spin_lock(&br->hash_lock); | 484 | spin_lock(&br->hash_lock); |
444 | if (likely(!fdb_find(head, addr))) { | 485 | if (likely(!fdb_find(head, addr, vid))) { |
445 | fdb = fdb_create(head, source, addr); | 486 | fdb = fdb_create(head, source, addr, vid); |
446 | if (fdb) | 487 | if (fdb) |
447 | fdb_notify(br, fdb, RTM_NEWNEIGH); | 488 | fdb_notify(br, fdb, RTM_NEWNEIGH); |
448 | } | 489 | } |
@@ -495,6 +536,10 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, | |||
495 | ci.ndm_refcnt = 0; | 536 | ci.ndm_refcnt = 0; |
496 | if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) | 537 | if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) |
497 | goto nla_put_failure; | 538 | goto nla_put_failure; |
539 | |||
540 | if (nla_put(skb, NDA_VLAN, sizeof(u16), &fdb->vlan_id)) | ||
541 | goto nla_put_failure; | ||
542 | |||
498 | return nlmsg_end(skb, nlh); | 543 | return nlmsg_end(skb, nlh); |
499 | 544 | ||
500 | nla_put_failure: | 545 | nla_put_failure: |
@@ -506,6 +551,7 @@ static inline size_t fdb_nlmsg_size(void) | |||
506 | { | 551 | { |
507 | return NLMSG_ALIGN(sizeof(struct ndmsg)) | 552 | return NLMSG_ALIGN(sizeof(struct ndmsg)) |
508 | + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ | 553 | + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ |
554 | + nla_total_size(sizeof(u16)) /* NDA_VLAN */ | ||
509 | + nla_total_size(sizeof(struct nda_cacheinfo)); | 555 | + nla_total_size(sizeof(struct nda_cacheinfo)); |
510 | } | 556 | } |
511 | 557 | ||
@@ -571,18 +617,18 @@ out: | |||
571 | 617 | ||
572 | /* Update (create or replace) forwarding database entry */ | 618 | /* Update (create or replace) forwarding database entry */ |
573 | static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, | 619 | static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, |
574 | __u16 state, __u16 flags) | 620 | __u16 state, __u16 flags, __u16 vid) |
575 | { | 621 | { |
576 | struct net_bridge *br = source->br; | 622 | struct net_bridge *br = source->br; |
577 | struct hlist_head *head = &br->hash[br_mac_hash(addr)]; | 623 | struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; |
578 | struct net_bridge_fdb_entry *fdb; | 624 | struct net_bridge_fdb_entry *fdb; |
579 | 625 | ||
580 | fdb = fdb_find(head, addr); | 626 | fdb = fdb_find(head, addr, vid); |
581 | if (fdb == NULL) { | 627 | if (fdb == NULL) { |
582 | if (!(flags & NLM_F_CREATE)) | 628 | if (!(flags & NLM_F_CREATE)) |
583 | return -ENOENT; | 629 | return -ENOENT; |
584 | 630 | ||
585 | fdb = fdb_create(head, source, addr); | 631 | fdb = fdb_create(head, source, addr, vid); |
586 | if (!fdb) | 632 | if (!fdb) |
587 | return -ENOMEM; | 633 | return -ENOMEM; |
588 | fdb_notify(br, fdb, RTM_NEWNEIGH); | 634 | fdb_notify(br, fdb, RTM_NEWNEIGH); |
@@ -607,6 +653,25 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, | |||
607 | return 0; | 653 | return 0; |
608 | } | 654 | } |
609 | 655 | ||
656 | static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p, | ||
657 | const unsigned char *addr, u16 nlh_flags, u16 vid) | ||
658 | { | ||
659 | int err = 0; | ||
660 | |||
661 | if (ndm->ndm_flags & NTF_USE) { | ||
662 | rcu_read_lock(); | ||
663 | br_fdb_update(p->br, p, addr, vid); | ||
664 | rcu_read_unlock(); | ||
665 | } else { | ||
666 | spin_lock_bh(&p->br->hash_lock); | ||
667 | err = fdb_add_entry(p, addr, ndm->ndm_state, | ||
668 | nlh_flags, vid); | ||
669 | spin_unlock_bh(&p->br->hash_lock); | ||
670 | } | ||
671 | |||
672 | return err; | ||
673 | } | ||
674 | |||
610 | /* Add new permanent fdb entry with RTM_NEWNEIGH */ | 675 | /* Add new permanent fdb entry with RTM_NEWNEIGH */ |
611 | int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], | 676 | int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], |
612 | struct net_device *dev, | 677 | struct net_device *dev, |
@@ -614,12 +679,29 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], | |||
614 | { | 679 | { |
615 | struct net_bridge_port *p; | 680 | struct net_bridge_port *p; |
616 | int err = 0; | 681 | int err = 0; |
682 | struct net_port_vlans *pv; | ||
683 | unsigned short vid = VLAN_N_VID; | ||
617 | 684 | ||
618 | if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { | 685 | if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { |
619 | pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state); | 686 | pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state); |
620 | return -EINVAL; | 687 | return -EINVAL; |
621 | } | 688 | } |
622 | 689 | ||
690 | if (tb[NDA_VLAN]) { | ||
691 | if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) { | ||
692 | pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n"); | ||
693 | return -EINVAL; | ||
694 | } | ||
695 | |||
696 | vid = nla_get_u16(tb[NDA_VLAN]); | ||
697 | |||
698 | if (vid >= VLAN_N_VID) { | ||
699 | pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n", | ||
700 | vid); | ||
701 | return -EINVAL; | ||
702 | } | ||
703 | } | ||
704 | |||
623 | p = br_port_get_rtnl(dev); | 705 | p = br_port_get_rtnl(dev); |
624 | if (p == NULL) { | 706 | if (p == NULL) { |
625 | pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", | 707 | pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", |
@@ -627,40 +709,90 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], | |||
627 | return -EINVAL; | 709 | return -EINVAL; |
628 | } | 710 | } |
629 | 711 | ||
630 | if (ndm->ndm_flags & NTF_USE) { | 712 | pv = nbp_get_vlan_info(p); |
631 | rcu_read_lock(); | 713 | if (vid != VLAN_N_VID) { |
632 | br_fdb_update(p->br, p, addr); | 714 | if (!pv || !test_bit(vid, pv->vlan_bitmap)) { |
633 | rcu_read_unlock(); | 715 | pr_info("bridge: RTM_NEWNEIGH with unconfigured " |
716 | "vlan %d on port %s\n", vid, dev->name); | ||
717 | return -EINVAL; | ||
718 | } | ||
719 | |||
720 | /* VID was specified, so use it. */ | ||
721 | err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); | ||
634 | } else { | 722 | } else { |
635 | spin_lock_bh(&p->br->hash_lock); | 723 | if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { |
636 | err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags); | 724 | err = __br_fdb_add(ndm, p, addr, nlh_flags, 0); |
637 | spin_unlock_bh(&p->br->hash_lock); | 725 | goto out; |
726 | } | ||
727 | |||
728 | /* We have vlans configured on this port and user didn't | ||
729 | * specify a VLAN. To be nice, add/update entry for every | ||
730 | * vlan on this port. | ||
731 | */ | ||
732 | vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN); | ||
733 | while (vid < BR_VLAN_BITMAP_LEN) { | ||
734 | err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); | ||
735 | if (err) | ||
736 | goto out; | ||
737 | vid = find_next_bit(pv->vlan_bitmap, | ||
738 | BR_VLAN_BITMAP_LEN, vid+1); | ||
739 | } | ||
638 | } | 740 | } |
639 | 741 | ||
742 | out: | ||
640 | return err; | 743 | return err; |
641 | } | 744 | } |
642 | 745 | ||
643 | static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) | 746 | int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, |
747 | u16 vlan) | ||
644 | { | 748 | { |
645 | struct net_bridge *br = p->br; | 749 | struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)]; |
646 | struct hlist_head *head = &br->hash[br_mac_hash(addr)]; | ||
647 | struct net_bridge_fdb_entry *fdb; | 750 | struct net_bridge_fdb_entry *fdb; |
648 | 751 | ||
649 | fdb = fdb_find(head, addr); | 752 | fdb = fdb_find(head, addr, vlan); |
650 | if (!fdb) | 753 | if (!fdb) |
651 | return -ENOENT; | 754 | return -ENOENT; |
652 | 755 | ||
653 | fdb_delete(p->br, fdb); | 756 | fdb_delete(br, fdb); |
654 | return 0; | 757 | return 0; |
655 | } | 758 | } |
656 | 759 | ||
760 | static int __br_fdb_delete(struct net_bridge_port *p, | ||
761 | const unsigned char *addr, u16 vid) | ||
762 | { | ||
763 | int err; | ||
764 | |||
765 | spin_lock_bh(&p->br->hash_lock); | ||
766 | err = fdb_delete_by_addr(p->br, addr, vid); | ||
767 | spin_unlock_bh(&p->br->hash_lock); | ||
768 | |||
769 | return err; | ||
770 | } | ||
771 | |||
657 | /* Remove neighbor entry with RTM_DELNEIGH */ | 772 | /* Remove neighbor entry with RTM_DELNEIGH */ |
658 | int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev, | 773 | int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], |
774 | struct net_device *dev, | ||
659 | const unsigned char *addr) | 775 | const unsigned char *addr) |
660 | { | 776 | { |
661 | struct net_bridge_port *p; | 777 | struct net_bridge_port *p; |
662 | int err; | 778 | int err; |
779 | struct net_port_vlans *pv; | ||
780 | unsigned short vid = VLAN_N_VID; | ||
781 | |||
782 | if (tb[NDA_VLAN]) { | ||
783 | if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) { | ||
784 | pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n"); | ||
785 | return -EINVAL; | ||
786 | } | ||
787 | |||
788 | vid = nla_get_u16(tb[NDA_VLAN]); | ||
663 | 789 | ||
790 | if (vid >= VLAN_N_VID) { | ||
791 | pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n", | ||
792 | vid); | ||
793 | return -EINVAL; | ||
794 | } | ||
795 | } | ||
664 | p = br_port_get_rtnl(dev); | 796 | p = br_port_get_rtnl(dev); |
665 | if (p == NULL) { | 797 | if (p == NULL) { |
666 | pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", | 798 | pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", |
@@ -668,9 +800,33 @@ int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev, | |||
668 | return -EINVAL; | 800 | return -EINVAL; |
669 | } | 801 | } |
670 | 802 | ||
671 | spin_lock_bh(&p->br->hash_lock); | 803 | pv = nbp_get_vlan_info(p); |
672 | err = fdb_delete_by_addr(p, addr); | 804 | if (vid != VLAN_N_VID) { |
673 | spin_unlock_bh(&p->br->hash_lock); | 805 | if (!pv || !test_bit(vid, pv->vlan_bitmap)) { |
806 | pr_info("bridge: RTM_DELNEIGH with unconfigured " | ||
807 | "vlan %d on port %s\n", vid, dev->name); | ||
808 | return -EINVAL; | ||
809 | } | ||
810 | |||
811 | err = __br_fdb_delete(p, addr, vid); | ||
812 | } else { | ||
813 | if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { | ||
814 | err = __br_fdb_delete(p, addr, 0); | ||
815 | goto out; | ||
816 | } | ||
674 | 817 | ||
818 | /* We have vlans configured on this port and user didn't | ||
819 | * specify a VLAN. To be nice, add/update entry for every | ||
820 | * vlan on this port. | ||
821 | */ | ||
822 | err = -ENOENT; | ||
823 | vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN); | ||
824 | while (vid < BR_VLAN_BITMAP_LEN) { | ||
825 | err &= __br_fdb_delete(p, addr, vid); | ||
826 | vid = find_next_bit(pv->vlan_bitmap, | ||
827 | BR_VLAN_BITMAP_LEN, vid+1); | ||
828 | } | ||
829 | } | ||
830 | out: | ||
675 | return err; | 831 | return err; |
676 | } | 832 | } |