summaryrefslogtreecommitdiffstats
path: root/net/bridge
diff options
context:
space:
mode:
authorMike Manning <mmanning@vyatta.att-mail.com>2019-04-18 13:35:33 -0400
committerDavid S. Miller <davem@davemloft.net>2019-04-19 16:58:17 -0400
commit9c0ec2e7182a508335364c752da0883a2a7f3999 (patch)
tree24450f8c5a0377effa51131ca78c4df51bdbbc82 /net/bridge
parent76052d8c4f2dda6f31390521069bc109204e2f28 (diff)
bridge: support binding vlan dev link state to vlan member bridge ports
In the case of vlan filtering on bridges, the bridge may also have the corresponding vlan devices as upper devices. A vlan bridge binding mode is added to allow the link state of the vlan device to track only the state of the subset of bridge ports that are also members of the vlan, rather than that of all bridge ports. This mode is set with a vlan flag rather than a bridge sysfs so that the 8021q module is aware that it should not set the link state for the vlan device. If bridge vlan is configured, the bridge device event handling results in the link state for an upper device being set, if it is a vlan device with the vlan bridge binding mode enabled. This also sets a vlan_bridge_binding flag so that subsequent UP/DOWN/CHANGE events for the ports in that bridge result in a link state update of the vlan device if required. The link state of the vlan device is up if there is at least one bridge port that is a vlan member that is admin & oper up, otherwise its oper state is IF_OPER_LOWERLAYERDOWN. Signed-off-by: Mike Manning <mmanning@vyatta.att-mail.com> Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/br.c13
-rw-r--r--net/bridge/br_private.h14
-rw-r--r--net/bridge/br_vlan.c151
3 files changed, 174 insertions, 4 deletions
diff --git a/net/bridge/br.c b/net/bridge/br.c
index a5174e5001d8..e69fc87a13e0 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -40,10 +40,13 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
40 bool changed_addr; 40 bool changed_addr;
41 int err; 41 int err;
42 42
43 /* register of bridge completed, add sysfs entries */ 43 if (dev->priv_flags & IFF_EBRIDGE) {
44 if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) { 44 if (event == NETDEV_REGISTER) {
45 br_sysfs_addbr(dev); 45 /* register of bridge completed, add sysfs entries */
46 return NOTIFY_DONE; 46 br_sysfs_addbr(dev);
47 return NOTIFY_DONE;
48 }
49 br_vlan_bridge_event(dev, event, ptr);
47 } 50 }
48 51
49 /* not a port of a bridge */ 52 /* not a port of a bridge */
@@ -126,6 +129,8 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
126 break; 129 break;
127 } 130 }
128 131
132 br_vlan_port_event(p, event);
133
129 /* Events that may cause spanning tree to refresh */ 134 /* Events that may cause spanning tree to refresh */
130 if (!notified && (event == NETDEV_CHANGEADDR || event == NETDEV_UP || 135 if (!notified && (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
131 event == NETDEV_CHANGE || event == NETDEV_DOWN)) 136 event == NETDEV_CHANGE || event == NETDEV_DOWN))
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 4bea2f11da9b..334a8c496b50 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -321,6 +321,7 @@ enum net_bridge_opts {
321 BROPT_MTU_SET_BY_USER, 321 BROPT_MTU_SET_BY_USER,
322 BROPT_VLAN_STATS_PER_PORT, 322 BROPT_VLAN_STATS_PER_PORT,
323 BROPT_NO_LL_LEARN, 323 BROPT_NO_LL_LEARN,
324 BROPT_VLAN_BRIDGE_BINDING,
324}; 325};
325 326
326struct net_bridge { 327struct net_bridge {
@@ -895,6 +896,9 @@ int nbp_vlan_init(struct net_bridge_port *port, struct netlink_ext_ack *extack);
895int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask); 896int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask);
896void br_vlan_get_stats(const struct net_bridge_vlan *v, 897void br_vlan_get_stats(const struct net_bridge_vlan *v,
897 struct br_vlan_stats *stats); 898 struct br_vlan_stats *stats);
899void br_vlan_port_event(struct net_bridge_port *p, unsigned long event);
900void br_vlan_bridge_event(struct net_device *dev, unsigned long event,
901 void *ptr);
898 902
899static inline struct net_bridge_vlan_group *br_vlan_group( 903static inline struct net_bridge_vlan_group *br_vlan_group(
900 const struct net_bridge *br) 904 const struct net_bridge *br)
@@ -1078,6 +1082,16 @@ static inline void br_vlan_get_stats(const struct net_bridge_vlan *v,
1078 struct br_vlan_stats *stats) 1082 struct br_vlan_stats *stats)
1079{ 1083{
1080} 1084}
1085
1086static inline void br_vlan_port_event(struct net_bridge_port *p,
1087 unsigned long event)
1088{
1089}
1090
1091static inline void br_vlan_bridge_event(struct net_device *dev,
1092 unsigned long event, void *ptr)
1093{
1094}
1081#endif 1095#endif
1082 1096
1083struct nf_br_ops { 1097struct nf_br_ops {
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 0a02822b5667..b903689a8fc5 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -1264,3 +1264,154 @@ int br_vlan_get_info(const struct net_device *dev, u16 vid,
1264 return 0; 1264 return 0;
1265} 1265}
1266EXPORT_SYMBOL_GPL(br_vlan_get_info); 1266EXPORT_SYMBOL_GPL(br_vlan_get_info);
1267
1268static int br_vlan_is_bind_vlan_dev(const struct net_device *dev)
1269{
1270 return is_vlan_dev(dev) &&
1271 !!(vlan_dev_priv(dev)->flags & VLAN_FLAG_BRIDGE_BINDING);
1272}
1273
1274static int br_vlan_is_bind_vlan_dev_fn(struct net_device *dev,
1275 __always_unused void *data)
1276{
1277 return br_vlan_is_bind_vlan_dev(dev);
1278}
1279
1280static bool br_vlan_has_upper_bind_vlan_dev(struct net_device *dev)
1281{
1282 int found;
1283
1284 rcu_read_lock();
1285 found = netdev_walk_all_upper_dev_rcu(dev, br_vlan_is_bind_vlan_dev_fn,
1286 NULL);
1287 rcu_read_unlock();
1288
1289 return !!found;
1290}
1291
1292struct br_vlan_bind_walk_data {
1293 u16 vid;
1294 struct net_device *result;
1295};
1296
1297static int br_vlan_match_bind_vlan_dev_fn(struct net_device *dev,
1298 void *data_in)
1299{
1300 struct br_vlan_bind_walk_data *data = data_in;
1301 int found = 0;
1302
1303 if (br_vlan_is_bind_vlan_dev(dev) &&
1304 vlan_dev_priv(dev)->vlan_id == data->vid) {
1305 data->result = dev;
1306 found = 1;
1307 }
1308
1309 return found;
1310}
1311
1312static struct net_device *
1313br_vlan_get_upper_bind_vlan_dev(struct net_device *dev, u16 vid)
1314{
1315 struct br_vlan_bind_walk_data data = {
1316 .vid = vid,
1317 };
1318
1319 rcu_read_lock();
1320 netdev_walk_all_upper_dev_rcu(dev, br_vlan_match_bind_vlan_dev_fn,
1321 &data);
1322 rcu_read_unlock();
1323
1324 return data.result;
1325}
1326
1327static bool br_vlan_is_dev_up(const struct net_device *dev)
1328{
1329 return !!(dev->flags & IFF_UP) && netif_oper_up(dev);
1330}
1331
1332static void br_vlan_set_vlan_dev_state(const struct net_bridge *br,
1333 struct net_device *vlan_dev)
1334{
1335 u16 vid = vlan_dev_priv(vlan_dev)->vlan_id;
1336 struct net_bridge_vlan_group *vg;
1337 struct net_bridge_port *p;
1338 bool has_carrier = false;
1339
1340 list_for_each_entry(p, &br->port_list, list) {
1341 vg = nbp_vlan_group(p);
1342 if (br_vlan_find(vg, vid) && br_vlan_is_dev_up(p->dev)) {
1343 has_carrier = true;
1344 break;
1345 }
1346 }
1347
1348 if (has_carrier)
1349 netif_carrier_on(vlan_dev);
1350 else
1351 netif_carrier_off(vlan_dev);
1352}
1353
1354static void br_vlan_set_all_vlan_dev_state(struct net_bridge_port *p)
1355{
1356 struct net_bridge_vlan_group *vg = nbp_vlan_group(p);
1357 struct net_bridge_vlan *vlan;
1358 struct net_device *vlan_dev;
1359
1360 list_for_each_entry(vlan, &vg->vlan_list, vlist) {
1361 vlan_dev = br_vlan_get_upper_bind_vlan_dev(p->br->dev,
1362 vlan->vid);
1363 if (vlan_dev) {
1364 if (br_vlan_is_dev_up(p->dev))
1365 netif_carrier_on(vlan_dev);
1366 else
1367 br_vlan_set_vlan_dev_state(p->br, vlan_dev);
1368 }
1369 }
1370}
1371
1372static void br_vlan_upper_change(struct net_device *dev,
1373 struct net_device *upper_dev,
1374 bool linking)
1375{
1376 struct net_bridge *br = netdev_priv(dev);
1377
1378 if (!br_vlan_is_bind_vlan_dev(upper_dev))
1379 return;
1380
1381 if (linking) {
1382 br_vlan_set_vlan_dev_state(br, upper_dev);
1383 br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, true);
1384 } else {
1385 br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING,
1386 br_vlan_has_upper_bind_vlan_dev(dev));
1387 }
1388}
1389
1390/* Must be protected by RTNL. */
1391void br_vlan_bridge_event(struct net_device *dev, unsigned long event,
1392 void *ptr)
1393{
1394 struct netdev_notifier_changeupper_info *info;
1395
1396 switch (event) {
1397 case NETDEV_CHANGEUPPER:
1398 info = ptr;
1399 br_vlan_upper_change(dev, info->upper_dev, info->linking);
1400 break;
1401 }
1402}
1403
1404/* Must be protected by RTNL. */
1405void br_vlan_port_event(struct net_bridge_port *p, unsigned long event)
1406{
1407 if (!br_opt_get(p->br, BROPT_VLAN_BRIDGE_BINDING))
1408 return;
1409
1410 switch (event) {
1411 case NETDEV_CHANGE:
1412 case NETDEV_DOWN:
1413 case NETDEV_UP:
1414 br_vlan_set_all_vlan_dev_state(p);
1415 break;
1416 }
1417}