Skip to content

Commit 145beee

Browse files
Vlad Yasevichdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: Vlad Yasevich <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f3a6ddf commit 145beee

File tree

1 file changed

+69
-6
lines changed

1 file changed

+69
-6
lines changed

net/bridge/br_fdb.c

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,58 @@ static void fdb_rcu_free(struct rcu_head *head)
8585
kmem_cache_free(br_fdb_cache, ent);
8686
}
8787

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+
88135
static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
89136
{
137+
if (f->is_static)
138+
fdb_del_hw(br, f->addr.addr);
139+
90140
hlist_del_rcu(&f->hlist);
91141
fdb_notify(br, f, RTM_DELNEIGH);
92142
call_rcu(&f->rcu, fdb_rcu_free);
@@ -466,6 +516,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
466516
return -ENOMEM;
467517

468518
fdb->is_local = fdb->is_static = 1;
519+
fdb_add_hw(br, addr);
469520
fdb_notify(br, fdb, RTM_NEWNEIGH);
470521
return 0;
471522
}
@@ -678,13 +729,25 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
678729
}
679730

680731
if (fdb_to_nud(fdb) != state) {
681-
if (state & NUD_PERMANENT)
682-
fdb->is_local = fdb->is_static = 1;
683-
else if (state & NUD_NOARP) {
732+
if (state & NUD_PERMANENT) {
733+
fdb->is_local = 1;
734+
if (!fdb->is_static) {
735+
fdb->is_static = 1;
736+
fdb_add_hw(br, addr);
737+
}
738+
} else if (state & NUD_NOARP) {
684739
fdb->is_local = 0;
685-
fdb->is_static = 1;
686-
} else
687-
fdb->is_local = fdb->is_static = 0;
740+
if (!fdb->is_static) {
741+
fdb->is_static = 1;
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+
}
688751

689752
modified = true;
690753
}

0 commit comments

Comments
 (0)