Skip to content

Commit 2796d0c

Browse files
Vlad Yasevichdavem330
Vlad Yasevich
authored andcommitted
bridge: Automatically manage port promiscuous mode.
There exist configurations where the administrator or another management entity has the foreknowledge of all the mac addresses of end systems that are being bridged together. In these environments, the administrator can statically configure known addresses in the bridge FDB and disable flooding and learning on ports. This makes it possible to turn off promiscuous mode on the interfaces connected to the bridge. Here is why disabling flooding and learning allows us to control promiscuity: Consider port X. All traffic coming into this port from outside the bridge (ingress) will be either forwarded through other ports of the bridge (egress) or dropped. Forwarding (egress) is defined by FDB entries and by flooding in the event that no FDB entry exists. In the event that flooding is disabled, only FDB entries define the egress. Once learning is disabled, only static FDB entries provided by a management entity define the egress. If we provide information from these static FDBs to the ingress port X, then we'll be able to accept all traffic that can be successfully forwarded and drop all the other traffic sooner without spending CPU cycles to process it. Another way to define the above is as following equations: ingress = egress + drop expanding egress ingress = static FDB + learned FDB + flooding + drop disabling flooding and learning we a left with ingress = static FDB + drop By adding addresses from the static FDB entries to the MAC address filter of an ingress port X, we fully define what the bridge can process without dropping and can thus turn off promiscuous mode, thus dropping packets sooner. There have been suggestions that we may want to allow learning and update the filters with learned addresses as well. This would require mac-level authentication similar to 802.1x to prevent attacks against the hw filters as they are limited resource. Additionally, if the user places the bridge device in promiscuous mode, all ports are placed in promiscuous mode regardless of the changes to flooding and learning. Since the above functionality depends on full static configuration, we have also require that vlan filtering be enabled to take advantage of this. The reason is that the bridge has to be able to receive and process VLAN-tagged frames and the there are only 2 ways to accomplish this right now: promiscuous mode or vlan filtering. Suggested-by: Michael S. Tsirkin <[email protected]> Acked-by: Michael S. Tsirkin <[email protected]> Signed-off-by: Vlad Yasevich <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 145beee commit 2796d0c

File tree

4 files changed

+116
-7
lines changed

4 files changed

+116
-7
lines changed

net/bridge/br_device.c

+7
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ static void br_dev_set_multicast_list(struct net_device *dev)
112112
{
113113
}
114114

115+
static void br_dev_change_rx_flags(struct net_device *dev, int change)
116+
{
117+
if (change & IFF_PROMISC)
118+
br_manage_promisc(netdev_priv(dev));
119+
}
120+
115121
static int br_dev_stop(struct net_device *dev)
116122
{
117123
struct net_bridge *br = netdev_priv(dev);
@@ -309,6 +315,7 @@ static const struct net_device_ops br_netdev_ops = {
309315
.ndo_get_stats64 = br_get_stats64,
310316
.ndo_set_mac_address = br_set_mac_address,
311317
.ndo_set_rx_mode = br_dev_set_multicast_list,
318+
.ndo_change_rx_flags = br_dev_change_rx_flags,
312319
.ndo_change_mtu = br_change_mtu,
313320
.ndo_do_ioctl = br_dev_ioctl,
314321
#ifdef CONFIG_NET_POLL_CONTROLLER

net/bridge/br_if.c

+98-7
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,82 @@ void br_port_carrier_check(struct net_bridge_port *p)
8585
spin_unlock_bh(&br->lock);
8686
}
8787

88+
static void br_port_set_promisc(struct net_bridge_port *p)
89+
{
90+
int err = 0;
91+
92+
if (br_promisc_port(p))
93+
return;
94+
95+
err = dev_set_promiscuity(p->dev, 1);
96+
if (err)
97+
return;
98+
99+
br_fdb_unsync_static(p->br, p);
100+
p->flags |= BR_PROMISC;
101+
}
102+
103+
static void br_port_clear_promisc(struct net_bridge_port *p)
104+
{
105+
int err;
106+
107+
/* Check if the port is already non-promisc or if it doesn't
108+
* support UNICAST filtering. Without unicast filtering support
109+
* we'll end up re-enabling promisc mode anyway, so just check for
110+
* it here.
111+
*/
112+
if (!br_promisc_port(p) || !(p->dev->priv_flags & IFF_UNICAST_FLT))
113+
return;
114+
115+
/* Since we'll be clearing the promisc mode, program the port
116+
* first so that we don't have interruption in traffic.
117+
*/
118+
err = br_fdb_sync_static(p->br, p);
119+
if (err)
120+
return;
121+
122+
dev_set_promiscuity(p->dev, -1);
123+
p->flags &= ~BR_PROMISC;
124+
}
125+
126+
/* When a port is added or removed or when certain port flags
127+
* change, this function is called to automatically manage
128+
* promiscuity setting of all the bridge ports. We are always called
129+
* under RTNL so can skip using rcu primitives.
130+
*/
131+
void br_manage_promisc(struct net_bridge *br)
132+
{
133+
struct net_bridge_port *p;
134+
bool set_all = false;
135+
136+
/* If vlan filtering is disabled or bridge interface is placed
137+
* into promiscuous mode, place all ports in promiscuous mode.
138+
*/
139+
if ((br->dev->flags & IFF_PROMISC) || !br_vlan_enabled(br))
140+
set_all = true;
141+
142+
list_for_each_entry(p, &br->port_list, list) {
143+
if (set_all) {
144+
br_port_set_promisc(p);
145+
} else {
146+
/* If the number of auto-ports is <= 1, then all other
147+
* ports will have their output configuration
148+
* statically specified through fdbs. Since ingress
149+
* on the auto-port becomes forwarding/egress to other
150+
* ports and egress configuration is statically known,
151+
* we can say that ingress configuration of the
152+
* auto-port is also statically known.
153+
* This lets us disable promiscuous mode and write
154+
* this config to hw.
155+
*/
156+
if (br->auto_cnt <= br_auto_port(p))
157+
br_port_clear_promisc(p);
158+
else
159+
br_port_set_promisc(p);
160+
}
161+
}
162+
}
163+
88164
static void nbp_update_port_count(struct net_bridge *br)
89165
{
90166
struct net_bridge_port *p;
@@ -94,7 +170,23 @@ static void nbp_update_port_count(struct net_bridge *br)
94170
if (br_auto_port(p))
95171
cnt++;
96172
}
97-
br->auto_cnt = cnt;
173+
if (br->auto_cnt != cnt) {
174+
br->auto_cnt = cnt;
175+
br_manage_promisc(br);
176+
}
177+
}
178+
179+
static void nbp_delete_promisc(struct net_bridge_port *p)
180+
{
181+
/* If port is currently promiscous, unset promiscuity.
182+
* Otherwise, it is a static port so remove all addresses
183+
* from it.
184+
*/
185+
dev_set_allmulti(p->dev, -1);
186+
if (br_promisc_port(p))
187+
dev_set_promiscuity(p->dev, -1);
188+
else
189+
br_fdb_unsync_static(p->br, p);
98190
}
99191

100192
static void release_nbp(struct kobject *kobj)
@@ -145,19 +237,18 @@ static void del_nbp(struct net_bridge_port *p)
145237

146238
sysfs_remove_link(br->ifobj, p->dev->name);
147239

148-
dev_set_promiscuity(dev, -1);
240+
nbp_delete_promisc(p);
149241

150242
spin_lock_bh(&br->lock);
151243
br_stp_disable_port(p);
152244
spin_unlock_bh(&br->lock);
153245

154246
br_ifinfo_notify(RTM_DELLINK, p);
155247

156-
nbp_vlan_flush(p);
157-
br_fdb_delete_by_port(br, p, 1);
158-
159248
list_del_rcu(&p->list);
160249

250+
nbp_vlan_flush(p);
251+
br_fdb_delete_by_port(br, p, 1);
161252
nbp_update_port_count(br);
162253

163254
dev->priv_flags &= ~IFF_BRIDGE_PORT;
@@ -238,7 +329,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
238329
p->path_cost = port_cost(dev);
239330
p->priority = 0x8000 >> BR_PORT_BITS;
240331
p->port_no = index;
241-
p->flags = BR_LEARNING | BR_FLOOD | BR_PROMISC;
332+
p->flags = BR_LEARNING | BR_FLOOD;
242333
br_init_port(p);
243334
p->state = BR_STATE_DISABLED;
244335
br_stp_port_timer_init(p);
@@ -367,7 +458,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
367458

368459
call_netdevice_notifiers(NETDEV_JOIN, dev);
369460

370-
err = dev_set_promiscuity(dev, 1);
461+
err = dev_set_allmulti(dev, 1);
371462
if (err)
372463
goto put_back;
373464

net/bridge/br_private.h

+10
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ int br_min_mtu(const struct net_bridge *br);
424424
netdev_features_t br_features_recompute(struct net_bridge *br,
425425
netdev_features_t features);
426426
void br_port_flags_change(struct net_bridge_port *port, unsigned long mask);
427+
void br_manage_promisc(struct net_bridge *br);
427428

428429
/* br_input.c */
429430
int br_handle_frame_finish(struct sk_buff *skb);
@@ -641,6 +642,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
641642
return v->pvid ?: VLAN_N_VID;
642643
}
643644

645+
static inline int br_vlan_enabled(struct net_bridge *br)
646+
{
647+
return br->vlan_enabled;
648+
}
644649
#else
645650
static inline bool br_allowed_ingress(struct net_bridge *br,
646651
struct net_port_vlans *v,
@@ -721,6 +726,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
721726
{
722727
return VLAN_N_VID; /* Returns invalid vid */
723728
}
729+
730+
static inline int br_vlan_enabled(struct net_bridge *br);
731+
{
732+
return 0;
733+
}
724734
#endif
725735

726736
/* br_netfilter.c */

net/bridge/br_vlan.c

+1
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
332332
goto unlock;
333333

334334
br->vlan_enabled = val;
335+
br_manage_promisc(br);
335336

336337
unlock:
337338
rtnl_unlock();

0 commit comments

Comments
 (0)