diff options
author | Vlad Yasevich <vyasevic@redhat.com> | 2014-05-16 09:59:19 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-05-16 17:06:33 -0400 |
commit | 145beee8d6bbd18dbebf9f71a40ba99af80d71f7 (patch) | |
tree | 1da0a5d6c374686bba0836b7f2e9c521a5f60ad8 /net/bridge/br_fdb.c | |
parent | f3a6ddf15209cfad90b83e04168f42a5d9c8cc17 (diff) |
bridge: Add addresses from static fdbs to non-promisc ports
When a static fdb entry is created, add the mac address
from this fdb entry to any ports that are currently running
in non-promiscuous mode. These ports need this data so that
they can receive traffic destined to these addresses.
By default ports start in promiscuous mode, so this feature
is disabled.
Acked-by: Michael S. Tsirkin <mst@redhat.com>
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.c | 75 |
1 files changed, 69 insertions, 6 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index fe124e59a344..648d0e849595 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c | |||
@@ -85,8 +85,58 @@ static void fdb_rcu_free(struct rcu_head *head) | |||
85 | kmem_cache_free(br_fdb_cache, ent); | 85 | kmem_cache_free(br_fdb_cache, ent); |
86 | } | 86 | } |
87 | 87 | ||
88 | /* When a static FDB entry is added, the mac address from the entry is | ||
89 | * added to the bridge private HW address list and all required ports | ||
90 | * are then updated with the new information. | ||
91 | * Called under RTNL. | ||
92 | */ | ||
93 | static void fdb_add_hw(struct net_bridge *br, const unsigned char *addr) | ||
94 | { | ||
95 | int err; | ||
96 | struct net_bridge_port *p, *tmp; | ||
97 | |||
98 | ASSERT_RTNL(); | ||
99 | |||
100 | list_for_each_entry(p, &br->port_list, list) { | ||
101 | if (!br_promisc_port(p)) { | ||
102 | err = dev_uc_add(p->dev, addr); | ||
103 | if (err) | ||
104 | goto undo; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | return; | ||
109 | undo: | ||
110 | list_for_each_entry(tmp, &br->port_list, list) { | ||
111 | if (tmp == p) | ||
112 | break; | ||
113 | if (!br_promisc_port(tmp)) | ||
114 | dev_uc_del(tmp->dev, addr); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | /* When a static FDB entry is deleted, the HW address from that entry is | ||
119 | * also removed from the bridge private HW address list and updates all | ||
120 | * the ports with needed information. | ||
121 | * Called under RTNL. | ||
122 | */ | ||
123 | static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr) | ||
124 | { | ||
125 | struct net_bridge_port *p; | ||
126 | |||
127 | ASSERT_RTNL(); | ||
128 | |||
129 | list_for_each_entry(p, &br->port_list, list) { | ||
130 | if (!br_promisc_port(p)) | ||
131 | dev_uc_del(p->dev, addr); | ||
132 | } | ||
133 | } | ||
134 | |||
88 | static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) | 135 | static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) |
89 | { | 136 | { |
137 | if (f->is_static) | ||
138 | fdb_del_hw(br, f->addr.addr); | ||
139 | |||
90 | hlist_del_rcu(&f->hlist); | 140 | hlist_del_rcu(&f->hlist); |
91 | fdb_notify(br, f, RTM_DELNEIGH); | 141 | fdb_notify(br, f, RTM_DELNEIGH); |
92 | call_rcu(&f->rcu, fdb_rcu_free); | 142 | call_rcu(&f->rcu, fdb_rcu_free); |
@@ -466,6 +516,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | |||
466 | return -ENOMEM; | 516 | return -ENOMEM; |
467 | 517 | ||
468 | fdb->is_local = fdb->is_static = 1; | 518 | fdb->is_local = fdb->is_static = 1; |
519 | fdb_add_hw(br, addr); | ||
469 | fdb_notify(br, fdb, RTM_NEWNEIGH); | 520 | fdb_notify(br, fdb, RTM_NEWNEIGH); |
470 | return 0; | 521 | return 0; |
471 | } | 522 | } |
@@ -678,13 +729,25 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, | |||
678 | } | 729 | } |
679 | 730 | ||
680 | if (fdb_to_nud(fdb) != state) { | 731 | if (fdb_to_nud(fdb) != state) { |
681 | if (state & NUD_PERMANENT) | 732 | if (state & NUD_PERMANENT) { |
682 | fdb->is_local = fdb->is_static = 1; | 733 | fdb->is_local = 1; |
683 | else if (state & NUD_NOARP) { | 734 | if (!fdb->is_static) { |
735 | fdb->is_static = 1; | ||
736 | fdb_add_hw(br, addr); | ||
737 | } | ||
738 | } else if (state & NUD_NOARP) { | ||
684 | fdb->is_local = 0; | 739 | fdb->is_local = 0; |
685 | fdb->is_static = 1; | 740 | if (!fdb->is_static) { |
686 | } else | 741 | fdb->is_static = 1; |
687 | fdb->is_local = fdb->is_static = 0; | 742 | fdb_add_hw(br, addr); |
743 | } | ||
744 | } else { | ||
745 | fdb->is_local = 0; | ||
746 | if (fdb->is_static) { | ||
747 | fdb->is_static = 0; | ||
748 | fdb_del_hw(br, addr); | ||
749 | } | ||
750 | } | ||
688 | 751 | ||
689 | modified = true; | 752 | modified = true; |
690 | } | 753 | } |