aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_if.c
diff options
context:
space:
mode:
authorVlad Yasevich <vyasevic@redhat.com>2014-05-16 09:59:20 -0400
committerDavid S. Miller <davem@davemloft.net>2014-05-16 17:06:33 -0400
commit2796d0c648c940b4796f84384fbcfb0a2399db84 (patch)
treed2dea8ba7d3ccdfae6a0fc7d33b8ee355f7d217e /net/bridge/br_if.c
parent145beee8d6bbd18dbebf9f71a40ba99af80d71f7 (diff)
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 <mst@redhat.com> 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_if.c')
-rw-r--r--net/bridge/br_if.c105
1 files changed, 98 insertions, 7 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 3fefff974540..091d39f5067c 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -85,6 +85,82 @@ void br_port_carrier_check(struct net_bridge_port *p)
85 spin_unlock_bh(&br->lock); 85 spin_unlock_bh(&br->lock);
86} 86}
87 87
88static 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
103static 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 */
131void 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
88static void nbp_update_port_count(struct net_bridge *br) 164static void nbp_update_port_count(struct net_bridge *br)
89{ 165{
90 struct net_bridge_port *p; 166 struct net_bridge_port *p;
@@ -94,7 +170,23 @@ static void nbp_update_port_count(struct net_bridge *br)
94 if (br_auto_port(p)) 170 if (br_auto_port(p))
95 cnt++; 171 cnt++;
96 } 172 }
97 br->auto_cnt = cnt; 173 if (br->auto_cnt != cnt) {
174 br->auto_cnt = cnt;
175 br_manage_promisc(br);
176 }
177}
178
179static 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);
98} 190}
99 191
100static void release_nbp(struct kobject *kobj) 192static void release_nbp(struct kobject *kobj)
@@ -145,7 +237,7 @@ static void del_nbp(struct net_bridge_port *p)
145 237
146 sysfs_remove_link(br->ifobj, p->dev->name); 238 sysfs_remove_link(br->ifobj, p->dev->name);
147 239
148 dev_set_promiscuity(dev, -1); 240 nbp_delete_promisc(p);
149 241
150 spin_lock_bh(&br->lock); 242 spin_lock_bh(&br->lock);
151 br_stp_disable_port(p); 243 br_stp_disable_port(p);
@@ -153,11 +245,10 @@ static void del_nbp(struct net_bridge_port *p)
153 245
154 br_ifinfo_notify(RTM_DELLINK, p); 246 br_ifinfo_notify(RTM_DELLINK, p);
155 247
156 nbp_vlan_flush(p);
157 br_fdb_delete_by_port(br, p, 1);
158
159 list_del_rcu(&p->list); 248 list_del_rcu(&p->list);
160 249
250 nbp_vlan_flush(p);
251 br_fdb_delete_by_port(br, p, 1);
161 nbp_update_port_count(br); 252 nbp_update_port_count(br);
162 253
163 dev->priv_flags &= ~IFF_BRIDGE_PORT; 254 dev->priv_flags &= ~IFF_BRIDGE_PORT;
@@ -238,7 +329,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
238 p->path_cost = port_cost(dev); 329 p->path_cost = port_cost(dev);
239 p->priority = 0x8000 >> BR_PORT_BITS; 330 p->priority = 0x8000 >> BR_PORT_BITS;
240 p->port_no = index; 331 p->port_no = index;
241 p->flags = BR_LEARNING | BR_FLOOD | BR_PROMISC; 332 p->flags = BR_LEARNING | BR_FLOOD;
242 br_init_port(p); 333 br_init_port(p);
243 p->state = BR_STATE_DISABLED; 334 p->state = BR_STATE_DISABLED;
244 br_stp_port_timer_init(p); 335 br_stp_port_timer_init(p);
@@ -367,7 +458,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
367 458
368 call_netdevice_notifiers(NETDEV_JOIN, dev); 459 call_netdevice_notifiers(NETDEV_JOIN, dev);
369 460
370 err = dev_set_promiscuity(dev, 1); 461 err = dev_set_allmulti(dev, 1);
371 if (err) 462 if (err)
372 goto put_back; 463 goto put_back;
373 464