Vlad Yasevich
2014-Feb-26 15:18 UTC
[Bridge] [PATCH RFC 0/7] Non-promisc bidge ports support
This patch series is a complete re-design and re-implementation of prior attempts to support non-promiscuous bridge ports. The basic design is as follows. The bridge keeps track of all the ports that flood packets to unknown destinations. If the flooding is disabled on the port, to get traffic to flow through, user/management would need to add an fdb describing such traffic. When such fdb is added, we save the address to bridge private hardware address list. Since we now have static configuration for all non-flooding ports and only 1 flooding port, we can make this single port non-promiscuous and program the receive filter with our list of addresses. On HW that doesn't support unicast filtering or if the list too bit, the device will be placed in promiscuous mode by the application of the filter. There are multiple reasons I chose to do private hw address list in the bridge in patch 3: 1) I tried using the fdb table itself as main repository, but this caused difficulties in synchronizing this table with the interface filters later on. 2) I tried using the bridge device 'uc' list to store these addresses, but that caused issues with devices on top of a bridge (vlans, bonds) that changed their mac addresses and propagated this down to bridge. I recently figured out a way that might allow us to do this which involves learning to be added br_dev_xmi(). We can discuss this, if there serious objections to current proposal. There are some other cases when promiscuous mode has to be turned back on. One is when the bridge itself if placed in promiscuous mode (use sets promisc flag). The other is when vlans devices are configured on top of the bridge and vlan filtering is disabled (default). This allows the bridge to receive all tagged frames and doesn't create a dependency between this code and vlan filtering. The last patch in the series is a special case where all ports are non-flooding. This could be useful in a routed configurations. In this case, since all ports will be configured manually, we can sync the our address list across all port of the bridge and make all ports non-promiscuous. Thanks -vlad Vlad Yasevich (7): bridge: Turn flag change macro into a function. bridge: Keep track of ports capable of flooding. bridge: Add addresses from static fdbs to bridge address list bridge: Automatically manage port promiscuous mode. bridge: Correctly manage promiscuity when user requested it. bridge: Manage promisc mode when vlans are configured on top of a bridge bridge: Support promisc management when all ports are non-flooding include/linux/netdevice.h | 9 +++ net/bridge/br_device.c | 23 +++++++ net/bridge/br_fdb.c | 122 +++++++++++++++++++++++++++++++++-- net/bridge/br_if.c | 159 ++++++++++++++++++++++++++++++++++++++++++++-- net/bridge/br_netlink.c | 3 + net/bridge/br_private.h | 18 ++++++ net/bridge/br_sysfs_if.c | 33 +++++++--- net/bridge/br_vlan.c | 1 + net/core/dev.c | 1 + net/core/dev_addr_lists.c | 21 +++--- 10 files changed, 361 insertions(+), 29 deletions(-) -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[Bridge] [PATCH 1/7] bridge: Turn flag change macro into a function.
Turn the flag change macro into a function to allow easier updates and to reduce space. Signed-off-by: Vlad Yasevich <vyasevic at redhat.com> --- net/bridge/br_sysfs_if.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index dd595bd..7f66aa4 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -25,6 +25,8 @@ struct brport_attribute { ssize_t (*show)(struct net_bridge_port *, char *); int (*store)(struct net_bridge_port *, unsigned long); }; +static int store_flag(struct net_bridge_port *p, unsigned long v, + unsigned long mask); #define BRPORT_ATTR(_name, _mode, _show, _store) \ const struct brport_attribute brport_attr_##_name = { \ @@ -41,20 +43,27 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \ } \ static int store_##_name(struct net_bridge_port *p, unsigned long v) \ { \ - unsigned long flags = p->flags; \ - if (v) \ - flags |= _mask; \ - else \ - flags &= ~_mask; \ - if (flags != p->flags) { \ - p->flags = flags; \ - br_ifinfo_notify(RTM_NEWLINK, p); \ - } \ - return 0; \ + return store_flag(p, v, _mask); \ } \ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR, \ show_##_name, store_##_name) +static int store_flag(struct net_bridge_port *p, unsigned long v, + unsigned long mask) +{ + unsigned long flags = p->flags; + + if (v) + flags |= mask; + else + flags &= ~mask; + + if (flags != p->flags) { + p->flags = flags; + br_ifinfo_notify(RTM_NEWLINK, p); + } + return 0; +} static ssize_t show_path_cost(struct net_bridge_port *p, char *buf) { -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[PATCH 1/7] bridge: Turn flag change macro into a function.
Turn the flag change macro into a function to allow easier updates and to reduce space. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> --- net/bridge/br_sysfs_if.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index dd595bd..7f66aa4 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -25,6 +25,8 @@ struct brport_attribute { ssize_t (*show)(struct net_bridge_port *, char *); int (*store)(struct net_bridge_port *, unsigned long); }; +static int store_flag(struct net_bridge_port *p, unsigned long v, + unsigned long mask); #define BRPORT_ATTR(_name, _mode, _show, _store) \ const struct brport_attribute brport_attr_##_name = { \ @@ -41,20 +43,27 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \ } \ static int store_##_name(struct net_bridge_port *p, unsigned long v) \ { \ - unsigned long flags = p->flags; \ - if (v) \ - flags |= _mask; \ - else \ - flags &= ~_mask; \ - if (flags != p->flags) { \ - p->flags = flags; \ - br_ifinfo_notify(RTM_NEWLINK, p); \ - } \ - return 0; \ + return store_flag(p, v, _mask); \ } \ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR, \ show_##_name, store_##_name) +static int store_flag(struct net_bridge_port *p, unsigned long v, + unsigned long mask) +{ + unsigned long flags = p->flags; + + if (v) + flags |= mask; + else + flags &= ~mask; + + if (flags != p->flags) { + p->flags = flags; + br_ifinfo_notify(RTM_NEWLINK, p); + } + return 0; +} static ssize_t show_path_cost(struct net_bridge_port *p, char *buf) { -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[PATCH 2/7] bridge: Keep track of ports capable of flooding.
Keep track of bridge ports that have unicast flooding turned on. This will later be used by the algorithm to automatically manage address programming and promisc mode. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> --- net/bridge/br_if.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++-- net/bridge/br_netlink.c | 3 +++ net/bridge/br_private.h | 4 ++++ net/bridge/br_sysfs_if.c | 6 ++++- 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 54d207d..f072b34 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -27,6 +27,9 @@ #include "br_private.h" +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br); +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br); + /* * Determine initial path cost based on speed. * using recommendations from 802.1d standard @@ -141,11 +144,14 @@ static void del_nbp(struct net_bridge_port *p) br_ifinfo_notify(RTM_DELLINK, p); + list_del_rcu(&p->list); + + if (p->flags & BR_FLOOD) + br_del_flood_port(p, br); + nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 1); - list_del_rcu(&p->list); - dev->priv_flags &= ~IFF_BRIDGE_PORT; netdev_rx_handler_unregister(dev); @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) dev_disable_lro(dev); list_add_rcu(&p->list, &br->port_list); + + if (p->flags & BR_FLOOD) + br_add_flood_port(p, br); netdev_update_features(br->dev); @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) return 0; } + +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) +{ + /* Increment the number of flooding ports, and if we + * only have 1 flooding port cache if for future use. + */ + br->n_flood_ports++; + if (br->n_flood_ports == 1) + br->c_flood_port = p; +} + +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) +{ + struct net_bridge_port *port; + + /* Decrement the number of flood port. + * If we are deleting the current flood port, clear + * the cached port. If we are down to 1 flood port, + * set it if it is not set. + */ + br->n_flood_ports--; + if (p == br->c_flood_port) + br->c_flood_port = NULL; + + if (br->n_flood_ports == 1) { + list_for_each_entry(port, &p->br->port_list, list) { + if (port->flags & BR_FLOOD) { + br->c_flood_port = port; + break; + } + } + } +} + +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask) +{ + struct net_bridge *br = p->br; + + /* We are only interested FLOOD flag */ + if (!(mask & BR_FLOOD)) + return; + + if (p->flags & BR_FLOOD) + br_add_flood_port(p, br); + else + br_del_flood_port(p, br); +} diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e74b6d53..01382b9 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) { int err; + unsigned long old_flags = p->flags; br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) if (err) return err; } + + br_port_flags_change(p, old_flags ^ p->flags); return 0; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 3ba11bc..26a3987 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -290,6 +290,8 @@ struct net_bridge struct timer_list topology_change_timer; struct timer_list gc_timer; struct kobject *ifobj; + struct net_bridge_port *c_flood_port; + u32 n_flood_ports; #ifdef CONFIG_BRIDGE_VLAN_FILTERING u8 vlan_enabled; struct net_port_vlans __rcu *vlan_info; @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev); int br_min_mtu(const struct net_bridge *br); netdev_features_t br_features_recompute(struct net_bridge *br, netdev_features_t features); +void br_port_flags_change(struct net_bridge_port *port, + unsigned long mask); /* br_input.c */ int br_handle_frame_finish(struct sk_buff *skb); diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 7f66aa4..9ff6691 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR, \ static int store_flag(struct net_bridge_port *p, unsigned long v, unsigned long mask) { - unsigned long flags = p->flags; + unsigned long flags; + unsigned long old_flags; + + old_flags = flags = p->flags; if (v) flags |= mask; @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v, if (flags != p->flags) { p->flags = flags; + br_port_flags_change(p, old_flags ^ flags); br_ifinfo_notify(RTM_NEWLINK, p); } return 0; -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[Bridge] [PATCH 2/7] bridge: Keep track of ports capable of flooding.
Keep track of bridge ports that have unicast flooding turned on. This will later be used by the algorithm to automatically manage address programming and promisc mode. Signed-off-by: Vlad Yasevich <vyasevic at redhat.com> --- net/bridge/br_if.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++-- net/bridge/br_netlink.c | 3 +++ net/bridge/br_private.h | 4 ++++ net/bridge/br_sysfs_if.c | 6 ++++- 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 54d207d..f072b34 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -27,6 +27,9 @@ #include "br_private.h" +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br); +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br); + /* * Determine initial path cost based on speed. * using recommendations from 802.1d standard @@ -141,11 +144,14 @@ static void del_nbp(struct net_bridge_port *p) br_ifinfo_notify(RTM_DELLINK, p); + list_del_rcu(&p->list); + + if (p->flags & BR_FLOOD) + br_del_flood_port(p, br); + nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 1); - list_del_rcu(&p->list); - dev->priv_flags &= ~IFF_BRIDGE_PORT; netdev_rx_handler_unregister(dev); @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) dev_disable_lro(dev); list_add_rcu(&p->list, &br->port_list); + + if (p->flags & BR_FLOOD) + br_add_flood_port(p, br); netdev_update_features(br->dev); @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) return 0; } + +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) +{ + /* Increment the number of flooding ports, and if we + * only have 1 flooding port cache if for future use. + */ + br->n_flood_ports++; + if (br->n_flood_ports == 1) + br->c_flood_port = p; +} + +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) +{ + struct net_bridge_port *port; + + /* Decrement the number of flood port. + * If we are deleting the current flood port, clear + * the cached port. If we are down to 1 flood port, + * set it if it is not set. + */ + br->n_flood_ports--; + if (p == br->c_flood_port) + br->c_flood_port = NULL; + + if (br->n_flood_ports == 1) { + list_for_each_entry(port, &p->br->port_list, list) { + if (port->flags & BR_FLOOD) { + br->c_flood_port = port; + break; + } + } + } +} + +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask) +{ + struct net_bridge *br = p->br; + + /* We are only interested FLOOD flag */ + if (!(mask & BR_FLOOD)) + return; + + if (p->flags & BR_FLOOD) + br_add_flood_port(p, br); + else + br_del_flood_port(p, br); +} diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e74b6d53..01382b9 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) { int err; + unsigned long old_flags = p->flags; br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) if (err) return err; } + + br_port_flags_change(p, old_flags ^ p->flags); return 0; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 3ba11bc..26a3987 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -290,6 +290,8 @@ struct net_bridge struct timer_list topology_change_timer; struct timer_list gc_timer; struct kobject *ifobj; + struct net_bridge_port *c_flood_port; + u32 n_flood_ports; #ifdef CONFIG_BRIDGE_VLAN_FILTERING u8 vlan_enabled; struct net_port_vlans __rcu *vlan_info; @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev); int br_min_mtu(const struct net_bridge *br); netdev_features_t br_features_recompute(struct net_bridge *br, netdev_features_t features); +void br_port_flags_change(struct net_bridge_port *port, + unsigned long mask); /* br_input.c */ int br_handle_frame_finish(struct sk_buff *skb); diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 7f66aa4..9ff6691 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR, \ static int store_flag(struct net_bridge_port *p, unsigned long v, unsigned long mask) { - unsigned long flags = p->flags; + unsigned long flags; + unsigned long old_flags; + + old_flags = flags = p->flags; if (v) flags |= mask; @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v, if (flags != p->flags) { p->flags = flags; + br_port_flags_change(p, old_flags ^ flags); br_ifinfo_notify(RTM_NEWLINK, p); } return 0; -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[Bridge] [PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
When a static fdb entry is created, add the mac address to the bridge address list. This list is used to program the proper port's address list. Signed-off-by: Vlad Yasevich <vyasevic at redhat.com> --- include/linux/netdevice.h | 6 +++ net/bridge/br_device.c | 2 + net/bridge/br_fdb.c | 110 +++++++++++++++++++++++++++++++++++++++++++--- net/bridge/br_if.c | 14 ++++-- net/bridge/br_private.h | 3 ++ net/core/dev.c | 1 + net/core/dev_addr_lists.c | 14 +++--- 7 files changed, 134 insertions(+), 16 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 440a02e..e29cce1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev); void unregister_netdev(struct net_device *dev); /* General hardware address lists handling functions */ +int __hw_addr_add(struct netdev_hw_addr_list *list, + const unsigned char *addr, int addr_len, + unsigned char addr_type); +int __hw_addr_del(struct netdev_hw_addr_list *list, + const unsigned char *addr, int addr_len, + unsigned char addr_type); int __hw_addr_sync(struct netdev_hw_addr_list *to_list, struct netdev_hw_addr_list *from_list, int addr_len); void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 63f0455..1521db6 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev) u64_stats_init(&br_dev_stats->syncp); } + __hw_addr_init(&br->conf_addrs); + return 0; } diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 9203d5a..ef95e81 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, static void fdb_notify(struct net_bridge *br, const struct net_bridge_fdb_entry *, int); +static int br_addr_add(struct net_bridge *br, const unsigned char *addr); +static int br_addr_del(struct net_bridge *br, const unsigned char *addr); + static u32 fdb_salt __read_mostly; int __init br_fdb_init(void) @@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head) static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) { + if (f->is_static) + br_addr_del(br, f->addr.addr); + hlist_del_rcu(&f->hlist); fdb_notify(br, f, RTM_DELNEIGH); call_rcu(&f->rcu, fdb_rcu_free); @@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, return -ENOMEM; fdb->is_local = fdb->is_static = 1; + br_addr_add(br, addr); fdb_notify(br, fdb, RTM_NEWNEIGH); return 0; } @@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, } if (fdb_to_nud(fdb) != state) { - if (state & NUD_PERMANENT) - fdb->is_local = fdb->is_static = 1; - else if (state & NUD_NOARP) { + if (state & NUD_PERMANENT) { + fdb->is_local = 1; + if (!fdb->is_static) { + fdb->is_static = 1; + br_addr_add(br, addr); + } + } else if (state & NUD_NOARP) { + fdb->is_local = 0; + if (!fdb->is_static) { + fdb->is_static = 1; + br_addr_add(br, addr); + } + } else { fdb->is_local = 0; - fdb->is_static = 1; - } else - fdb->is_local = fdb->is_static = 0; + if (fdb->is_static) { + fdb->is_static = 0; + br_addr_del(br, addr); + } + } modified = true; } + fdb->added_by_user = 1; fdb->used = jiffies; @@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], out: return err; } + + +/* Sync the current list to the correct flood port. */ +void br_fdb_addrs_sync(struct net_bridge *br) +{ + struct net_bridge_port *p; + int err; + + /* This function has to run under RTNL. + * Program the user added addresses into the proper port. This + * fuction follows the following algorithm: + * a) If only 1 port is flooding: + * - write all the addresses to this one port. + */ + if (br->n_flood_ports == 1) { + p = br->c_flood_port; + netif_addr_lock(p->dev); + err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs, + p->dev->addr_len); + if (!err) + __dev_set_rx_mode(p->dev); + netif_addr_unlock(p->dev); + + } +} + +void br_fdb_addrs_unsync(struct net_bridge *br) +{ + struct net_bridge_port *p = br->c_flood_port; + + if (!p) + return; + + netif_addr_lock(p->dev); + __hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len); + __dev_set_rx_mode(p->dev); + netif_addr_unlock(p->dev); +} + +/* When a static FDB entry is added, the mac address from the entry is + * added to the bridge private HW address list and all required ports + * are then updated with the new information. + * Called under RTNL. + */ +static int br_addr_add(struct net_bridge *br, const unsigned char *addr) +{ + int err; + + ASSERT_RTNL(); + + err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len, + NETDEV_HW_ADDR_T_UNICAST); + + if (!err) + br_fdb_addrs_sync(br); + + return err; +} + +/* When a static FDB entry is deleted, the HW address from that entry is + * also removed from the bridge private HW address list and updates all + * the ports with needed information. + * Called under RTNL. + */ +static int br_addr_del(struct net_bridge *br, const unsigned char *addr) +{ + int err; + + ASSERT_RTNL(); + + err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len, + NETDEV_HW_ADDR_T_UNICAST); + if (!err) + br_fdb_addrs_sync(br); + + return err; +} + diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index f072b34..e782c2e 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p) br_ifinfo_notify(RTM_DELLINK, p); - list_del_rcu(&p->list); + dev->priv_flags &= ~IFF_BRIDGE_PORT; if (p->flags & BR_FLOOD) br_del_flood_port(p, br); @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p) nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 1); - dev->priv_flags &= ~IFF_BRIDGE_PORT; + list_del_rcu(&p->list); netdev_rx_handler_unregister(dev); @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) br->n_flood_ports++; if (br->n_flood_ports == 1) br->c_flood_port = p; + + br_fdb_addrs_sync(br); } static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) * set it if it is not set. */ br->n_flood_ports--; - if (p == br->c_flood_port) + if (p == br->c_flood_port) { + br_fdb_addrs_unsync(br); br->c_flood_port = NULL; + } if (br->n_flood_ports == 1) { list_for_each_entry(port, &p->br->port_list, list) { - if (port->flags & BR_FLOOD) { + if (br_port_exists(port->dev) && + (port->flags & BR_FLOOD)) { br->c_flood_port = port; + br_fdb_addrs_sync(br); break; } } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 26a3987..40a6927 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -296,6 +296,7 @@ struct net_bridge u8 vlan_enabled; struct net_port_vlans __rcu *vlan_info; #endif + struct netdev_hw_addr_list conf_addrs; }; struct br_input_skb_cb { @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr, u16 nlh_flags); int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, int idx); +void br_fdb_addrs_sync(struct net_bridge *br); +void br_fdb_addrs_unsync(struct net_bridge *br); /* br_forward.c */ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); diff --git a/net/core/dev.c b/net/core/dev.c index 4ad1b78..eca4d476 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev) if (ops->ndo_set_rx_mode) ops->ndo_set_rx_mode(dev); } +EXPORT_SYMBOL(__dev_set_rx_mode); void dev_set_rx_mode(struct net_device *dev) { diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 329d579..3de44a3 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, sync); } -static int __hw_addr_add(struct netdev_hw_addr_list *list, - const unsigned char *addr, int addr_len, - unsigned char addr_type) +int __hw_addr_add(struct netdev_hw_addr_list *list, + const unsigned char *addr, int addr_len, + unsigned char addr_type) { return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false, 0); } +EXPORT_SYMBOL(__hw_addr_add); static int __hw_addr_del_entry(struct netdev_hw_addr_list *list, struct netdev_hw_addr *ha, bool global, @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list, return -ENOENT; } -static int __hw_addr_del(struct netdev_hw_addr_list *list, - const unsigned char *addr, int addr_len, - unsigned char addr_type) +int __hw_addr_del(struct netdev_hw_addr_list *list, + const unsigned char *addr, int addr_len, + unsigned char addr_type) { return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false); } +EXPORT_SYMBOL(__hw_addr_del); static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list, struct netdev_hw_addr *ha, -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[PATCH 3/7] bridge: Add addresses from static fdbs to bridge address list
When a static fdb entry is created, add the mac address to the bridge address list. This list is used to program the proper port's address list. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> --- include/linux/netdevice.h | 6 +++ net/bridge/br_device.c | 2 + net/bridge/br_fdb.c | 110 +++++++++++++++++++++++++++++++++++++++++++--- net/bridge/br_if.c | 14 ++++-- net/bridge/br_private.h | 3 ++ net/core/dev.c | 1 + net/core/dev_addr_lists.c | 14 +++--- 7 files changed, 134 insertions(+), 16 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 440a02e..e29cce1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2881,6 +2881,12 @@ int register_netdev(struct net_device *dev); void unregister_netdev(struct net_device *dev); /* General hardware address lists handling functions */ +int __hw_addr_add(struct netdev_hw_addr_list *list, + const unsigned char *addr, int addr_len, + unsigned char addr_type); +int __hw_addr_del(struct netdev_hw_addr_list *list, + const unsigned char *addr, int addr_len, + unsigned char addr_type); int __hw_addr_sync(struct netdev_hw_addr_list *to_list, struct netdev_hw_addr_list *from_list, int addr_len); void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 63f0455..1521db6 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -100,6 +100,8 @@ static int br_dev_init(struct net_device *dev) u64_stats_init(&br_dev_stats->syncp); } + __hw_addr_init(&br->conf_addrs); + return 0; } diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 9203d5a..ef95e81 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -35,6 +35,9 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, static void fdb_notify(struct net_bridge *br, const struct net_bridge_fdb_entry *, int); +static int br_addr_add(struct net_bridge *br, const unsigned char *addr); +static int br_addr_del(struct net_bridge *br, const unsigned char *addr); + static u32 fdb_salt __read_mostly; int __init br_fdb_init(void) @@ -87,6 +90,9 @@ static void fdb_rcu_free(struct rcu_head *head) static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) { + if (f->is_static) + br_addr_del(br, f->addr.addr); + hlist_del_rcu(&f->hlist); fdb_notify(br, f, RTM_DELNEIGH); call_rcu(&f->rcu, fdb_rcu_free); @@ -466,6 +472,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, return -ENOMEM; fdb->is_local = fdb->is_static = 1; + br_addr_add(br, addr); fdb_notify(br, fdb, RTM_NEWNEIGH); return 0; } @@ -678,16 +685,29 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, } if (fdb_to_nud(fdb) != state) { - if (state & NUD_PERMANENT) - fdb->is_local = fdb->is_static = 1; - else if (state & NUD_NOARP) { + if (state & NUD_PERMANENT) { + fdb->is_local = 1; + if (!fdb->is_static) { + fdb->is_static = 1; + br_addr_add(br, addr); + } + } else if (state & NUD_NOARP) { + fdb->is_local = 0; + if (!fdb->is_static) { + fdb->is_static = 1; + br_addr_add(br, addr); + } + } else { fdb->is_local = 0; - fdb->is_static = 1; - } else - fdb->is_local = fdb->is_static = 0; + if (fdb->is_static) { + fdb->is_static = 0; + br_addr_del(br, addr); + } + } modified = true; } + fdb->added_by_user = 1; fdb->used = jiffies; @@ -874,3 +894,81 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], out: return err; } + + +/* Sync the current list to the correct flood port. */ +void br_fdb_addrs_sync(struct net_bridge *br) +{ + struct net_bridge_port *p; + int err; + + /* This function has to run under RTNL. + * Program the user added addresses into the proper port. This + * fuction follows the following algorithm: + * a) If only 1 port is flooding: + * - write all the addresses to this one port. + */ + if (br->n_flood_ports == 1) { + p = br->c_flood_port; + netif_addr_lock(p->dev); + err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs, + p->dev->addr_len); + if (!err) + __dev_set_rx_mode(p->dev); + netif_addr_unlock(p->dev); + + } +} + +void br_fdb_addrs_unsync(struct net_bridge *br) +{ + struct net_bridge_port *p = br->c_flood_port; + + if (!p) + return; + + netif_addr_lock(p->dev); + __hw_addr_unsync(&p->dev->uc, &br->conf_addrs, p->dev->addr_len); + __dev_set_rx_mode(p->dev); + netif_addr_unlock(p->dev); +} + +/* When a static FDB entry is added, the mac address from the entry is + * added to the bridge private HW address list and all required ports + * are then updated with the new information. + * Called under RTNL. + */ +static int br_addr_add(struct net_bridge *br, const unsigned char *addr) +{ + int err; + + ASSERT_RTNL(); + + err = __hw_addr_add(&br->conf_addrs, addr, br->dev->addr_len, + NETDEV_HW_ADDR_T_UNICAST); + + if (!err) + br_fdb_addrs_sync(br); + + return err; +} + +/* When a static FDB entry is deleted, the HW address from that entry is + * also removed from the bridge private HW address list and updates all + * the ports with needed information. + * Called under RTNL. + */ +static int br_addr_del(struct net_bridge *br, const unsigned char *addr) +{ + int err; + + ASSERT_RTNL(); + + err = __hw_addr_del(&br->conf_addrs, addr, br->dev->addr_len, + NETDEV_HW_ADDR_T_UNICAST); + if (!err) + br_fdb_addrs_sync(br); + + return err; +} + diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index f072b34..e782c2e 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -144,7 +144,7 @@ static void del_nbp(struct net_bridge_port *p) br_ifinfo_notify(RTM_DELLINK, p); - list_del_rcu(&p->list); + dev->priv_flags &= ~IFF_BRIDGE_PORT; if (p->flags & BR_FLOOD) br_del_flood_port(p, br); @@ -152,7 +152,7 @@ static void del_nbp(struct net_bridge_port *p) nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 1); - dev->priv_flags &= ~IFF_BRIDGE_PORT; + list_del_rcu(&p->list); netdev_rx_handler_unregister(dev); @@ -473,6 +473,8 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) br->n_flood_ports++; if (br->n_flood_ports == 1) br->c_flood_port = p; + + br_fdb_addrs_sync(br); } static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) @@ -485,13 +487,17 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) * set it if it is not set. */ br->n_flood_ports--; - if (p == br->c_flood_port) + if (p == br->c_flood_port) { + br_fdb_addrs_unsync(br); br->c_flood_port = NULL; + } if (br->n_flood_ports == 1) { list_for_each_entry(port, &p->br->port_list, list) { - if (port->flags & BR_FLOOD) { + if (br_port_exists(port->dev) && + (port->flags & BR_FLOOD)) { br->c_flood_port = port; + br_fdb_addrs_sync(br); break; } } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 26a3987..40a6927 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -296,6 +296,7 @@ struct net_bridge u8 vlan_enabled; struct net_port_vlans __rcu *vlan_info; #endif + struct netdev_hw_addr_list conf_addrs; }; struct br_input_skb_cb { @@ -397,6 +398,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr, u16 nlh_flags); int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, int idx); +void br_fdb_addrs_sync(struct net_bridge *br); +void br_fdb_addrs_unsync(struct net_bridge *br); /* br_forward.c */ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); diff --git a/net/core/dev.c b/net/core/dev.c index 4ad1b78..eca4d476 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5212,6 +5212,7 @@ void __dev_set_rx_mode(struct net_device *dev) if (ops->ndo_set_rx_mode) ops->ndo_set_rx_mode(dev); } +EXPORT_SYMBOL(__dev_set_rx_mode); void dev_set_rx_mode(struct net_device *dev) { diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 329d579..3de44a3 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -81,13 +81,14 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, sync); } -static int __hw_addr_add(struct netdev_hw_addr_list *list, - const unsigned char *addr, int addr_len, - unsigned char addr_type) +int __hw_addr_add(struct netdev_hw_addr_list *list, + const unsigned char *addr, int addr_len, + unsigned char addr_type) { return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false, 0); } +EXPORT_SYMBOL(__hw_addr_add); static int __hw_addr_del_entry(struct netdev_hw_addr_list *list, struct netdev_hw_addr *ha, bool global, @@ -127,12 +128,13 @@ static int __hw_addr_del_ex(struct netdev_hw_addr_list *list, return -ENOENT; } -static int __hw_addr_del(struct netdev_hw_addr_list *list, - const unsigned char *addr, int addr_len, - unsigned char addr_type) +int __hw_addr_del(struct netdev_hw_addr_list *list, + const unsigned char *addr, int addr_len, + unsigned char addr_type) { return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false); } +EXPORT_SYMBOL(__hw_addr_del); static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list, struct netdev_hw_addr *ha, -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[Bridge] [PATCH 4/7] bridge: Automatically manage port promiscuous mode.
When there is only 1 flooding port, this port is programmed with all the address the bridge accumulated. This allows us to place this port into non-promiscuous mode. At other times, all ports are set as promiscuous. To help track whether the bridge set the mode or not, a new flag is introduced. Signed-off-by: Vlad Yasevich <vyasevic at redhat.com> --- net/bridge/br_if.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- net/bridge/br_private.h | 1 + 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index e782c2e..51df642 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -136,7 +136,7 @@ static void del_nbp(struct net_bridge_port *p) sysfs_remove_link(br->ifobj, p->dev->name); - dev_set_promiscuity(dev, -1); + dev_set_allmulti(dev, -1); spin_lock_bh(&br->lock); br_stp_disable_port(p); @@ -359,7 +359,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) call_netdevice_notifiers(NETDEV_JOIN, dev); - err = dev_set_promiscuity(dev, 1); + err = dev_set_allmulti(dev, 1); if (err) goto put_back; @@ -465,6 +465,48 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) return 0; } +static int br_port_set_promisc(struct net_bridge_port *p) +{ + int err = 0; + + if (p->flags & BR_PROMISC) + return err; + + err = dev_set_promiscuity(p->dev, 1); + if (err) + return err; + + p->flags |= BR_PROMISC; + return err; +} + +static void br_port_clear_promisc(struct net_bridge_port *p) +{ + if (!(p->flags & BR_PROMISC)) + return; + + dev_set_promiscuity(p->dev, -1); + p->flags &= ~BR_PROMISC; +} + +/* When a port is added or removed or when the flooding status of + * the port changes, this function is called to automatically mange + * promiscuity setting of all the bridge ports. We are always called + * under RTNL so can skip using rcu primitives. + */ +static void br_manage_promisc(struct net_bridge *br) +{ + struct net_bridge_port *p; + + list_for_each_entry(p, &br->port_list, list) { + if (!br_port_exists(p->dev) || + (br->n_flood_ports == 1 && br->c_flood_port == p)) + br_port_clear_promisc(p); + else + br_port_set_promisc(p); + } +} + static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) { /* Increment the number of flooding ports, and if we @@ -475,6 +517,7 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) br->c_flood_port = p; br_fdb_addrs_sync(br); + br_manage_promisc(br); } static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) @@ -502,6 +545,7 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) } } } + br_manage_promisc(br); } void br_port_flags_change(struct net_bridge_port *p, unsigned long mask) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 40a6927..6670cb3 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -174,6 +174,7 @@ struct net_bridge_port #define BR_ADMIN_COST 0x00000010 #define BR_LEARNING 0x00000020 #define BR_FLOOD 0x00000040 +#define BR_PROMISC 0x00000080 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING struct bridge_mcast_query ip4_query; -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[PATCH 4/7] bridge: Automatically manage port promiscuous mode.
When there is only 1 flooding port, this port is programmed with all the address the bridge accumulated. This allows us to place this port into non-promiscuous mode. At other times, all ports are set as promiscuous. To help track whether the bridge set the mode or not, a new flag is introduced. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> --- net/bridge/br_if.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- net/bridge/br_private.h | 1 + 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index e782c2e..51df642 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -136,7 +136,7 @@ static void del_nbp(struct net_bridge_port *p) sysfs_remove_link(br->ifobj, p->dev->name); - dev_set_promiscuity(dev, -1); + dev_set_allmulti(dev, -1); spin_lock_bh(&br->lock); br_stp_disable_port(p); @@ -359,7 +359,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) call_netdevice_notifiers(NETDEV_JOIN, dev); - err = dev_set_promiscuity(dev, 1); + err = dev_set_allmulti(dev, 1); if (err) goto put_back; @@ -465,6 +465,48 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) return 0; } +static int br_port_set_promisc(struct net_bridge_port *p) +{ + int err = 0; + + if (p->flags & BR_PROMISC) + return err; + + err = dev_set_promiscuity(p->dev, 1); + if (err) + return err; + + p->flags |= BR_PROMISC; + return err; +} + +static void br_port_clear_promisc(struct net_bridge_port *p) +{ + if (!(p->flags & BR_PROMISC)) + return; + + dev_set_promiscuity(p->dev, -1); + p->flags &= ~BR_PROMISC; +} + +/* When a port is added or removed or when the flooding status of + * the port changes, this function is called to automatically mange + * promiscuity setting of all the bridge ports. We are always called + * under RTNL so can skip using rcu primitives. + */ +static void br_manage_promisc(struct net_bridge *br) +{ + struct net_bridge_port *p; + + list_for_each_entry(p, &br->port_list, list) { + if (!br_port_exists(p->dev) || + (br->n_flood_ports == 1 && br->c_flood_port == p)) + br_port_clear_promisc(p); + else + br_port_set_promisc(p); + } +} + static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) { /* Increment the number of flooding ports, and if we @@ -475,6 +517,7 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) br->c_flood_port = p; br_fdb_addrs_sync(br); + br_manage_promisc(br); } static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) @@ -502,6 +545,7 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) } } } + br_manage_promisc(br); } void br_port_flags_change(struct net_bridge_port *p, unsigned long mask) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 40a6927..6670cb3 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -174,6 +174,7 @@ struct net_bridge_port #define BR_ADMIN_COST 0x00000010 #define BR_LEARNING 0x00000020 #define BR_FLOOD 0x00000040 +#define BR_PROMISC 0x00000080 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING struct bridge_mcast_query ip4_query; -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[Bridge] [PATCH 5/7] bridge: Correctly manage promiscuity when user requested it.
When the user places the bridge device in promiscuous mode, all ports are placed in promisc mode regardless of the number of flooding ports configured. Signed-off-by: Vlad Yasevich <vyasevic at redhat.com> --- net/bridge/br_device.c | 7 +++++++ net/bridge/br_if.c | 18 +++++++++++++----- net/bridge/br_private.h | 1 + 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 1521db6..0af9d6c 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -121,6 +121,12 @@ static void br_dev_set_multicast_list(struct net_device *dev) { } +static void br_dev_change_rx_flags(struct net_device *dev, int change) +{ + if (change & IFF_PROMISC) + br_manage_promisc(netdev_priv(dev)); +} + static int br_dev_stop(struct net_device *dev) { struct net_bridge *br = netdev_priv(dev); @@ -319,6 +325,7 @@ static const struct net_device_ops br_netdev_ops = { .ndo_get_stats64 = br_get_stats64, .ndo_set_mac_address = br_set_mac_address, .ndo_set_rx_mode = br_dev_set_multicast_list, + .ndo_change_rx_flags = br_dev_change_rx_flags, .ndo_change_mtu = br_change_mtu, .ndo_do_ioctl = br_dev_ioctl, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 51df642..7e92bd0 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -494,16 +494,24 @@ static void br_port_clear_promisc(struct net_bridge_port *p) * promiscuity setting of all the bridge ports. We are always called * under RTNL so can skip using rcu primitives. */ -static void br_manage_promisc(struct net_bridge *br) +void br_manage_promisc(struct net_bridge *br) { struct net_bridge_port *p; list_for_each_entry(p, &br->port_list, list) { - if (!br_port_exists(p->dev) || - (br->n_flood_ports == 1 && br->c_flood_port == p)) - br_port_clear_promisc(p); - else + if (br->dev->flags & IFF_PROMISC) { + /* PROMISC flag has been turned on for the bridge + * itself. Turn on promisc on all ports. + */ br_port_set_promisc(p); + + } else { + if (!br_port_exists(p->dev) || + (br->n_flood_ports == 1 && br->c_flood_port == p)) + br_port_clear_promisc(p); + else if (br_port_exists(p->dev)) + br_port_set_promisc(p); + } } } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 6670cb3..4042f86 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -423,6 +423,7 @@ netdev_features_t br_features_recompute(struct net_bridge *br, netdev_features_t features); void br_port_flags_change(struct net_bridge_port *port, unsigned long mask); +void br_manage_promisc(struct net_bridge *br); /* br_input.c */ int br_handle_frame_finish(struct sk_buff *skb); -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[PATCH 5/7] bridge: Correctly manage promiscuity when user requested it.
When the user places the bridge device in promiscuous mode, all ports are placed in promisc mode regardless of the number of flooding ports configured. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> --- net/bridge/br_device.c | 7 +++++++ net/bridge/br_if.c | 18 +++++++++++++----- net/bridge/br_private.h | 1 + 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 1521db6..0af9d6c 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -121,6 +121,12 @@ static void br_dev_set_multicast_list(struct net_device *dev) { } +static void br_dev_change_rx_flags(struct net_device *dev, int change) +{ + if (change & IFF_PROMISC) + br_manage_promisc(netdev_priv(dev)); +} + static int br_dev_stop(struct net_device *dev) { struct net_bridge *br = netdev_priv(dev); @@ -319,6 +325,7 @@ static const struct net_device_ops br_netdev_ops = { .ndo_get_stats64 = br_get_stats64, .ndo_set_mac_address = br_set_mac_address, .ndo_set_rx_mode = br_dev_set_multicast_list, + .ndo_change_rx_flags = br_dev_change_rx_flags, .ndo_change_mtu = br_change_mtu, .ndo_do_ioctl = br_dev_ioctl, #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 51df642..7e92bd0 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -494,16 +494,24 @@ static void br_port_clear_promisc(struct net_bridge_port *p) * promiscuity setting of all the bridge ports. We are always called * under RTNL so can skip using rcu primitives. */ -static void br_manage_promisc(struct net_bridge *br) +void br_manage_promisc(struct net_bridge *br) { struct net_bridge_port *p; list_for_each_entry(p, &br->port_list, list) { - if (!br_port_exists(p->dev) || - (br->n_flood_ports == 1 && br->c_flood_port == p)) - br_port_clear_promisc(p); - else + if (br->dev->flags & IFF_PROMISC) { + /* PROMISC flag has been turned on for the bridge + * itself. Turn on promisc on all ports. + */ br_port_set_promisc(p); + + } else { + if (!br_port_exists(p->dev) || + (br->n_flood_ports == 1 && br->c_flood_port == p)) + br_port_clear_promisc(p); + else if (br_port_exists(p->dev)) + br_port_set_promisc(p); + } } } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 6670cb3..4042f86 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -423,6 +423,7 @@ netdev_features_t br_features_recompute(struct net_bridge *br, netdev_features_t features); void br_port_flags_change(struct net_bridge_port *port, unsigned long mask); +void br_manage_promisc(struct net_bridge *br); /* br_input.c */ int br_handle_frame_finish(struct sk_buff *skb); -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
If the user configures vlan interfaces on top of the bridge and the bridge doesn't have vlan filtering enabled, we have to place all the ports in promsic mode so that we can correctly receive tagged frames. When vlan filtering is enabled, the vlan configuration will be provided via filtering interface. When the vlan filtering is toggled, we also have mange promiscuity. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> --- net/bridge/br_device.c | 14 ++++++++++++++ net/bridge/br_if.c | 17 +++++++++++++---- net/bridge/br_private.h | 9 +++++++++ net/bridge/br_vlan.c | 1 + 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 0af9d6c..967abb3 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p) #endif +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid) +{ + br_manage_promisc(netdev_priv(br_dev)); + return 0; +} + +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid) +{ + br_manage_promisc(netdev_priv(br_dev)); + return 0; +} + static int br_add_slave(struct net_device *dev, struct net_device *slave_dev) { @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = { .ndo_change_rx_flags = br_dev_change_rx_flags, .ndo_change_mtu = br_change_mtu, .ndo_do_ioctl = br_dev_ioctl, + .ndo_vlan_rx_add_vid = br_dev_rx_add_vid, + .ndo_vlan_rx_kill_vid = br_dev_rx_kill_vid, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_netpoll_setup = br_netpoll_setup, .ndo_netpoll_cleanup = br_netpoll_cleanup, diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 7e92bd0..55e4e28 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p) void br_manage_promisc(struct net_bridge *br) { struct net_bridge_port *p; + int set_all = false; + + if (br->dev->flags & IFF_PROMISC) + set_all = true; + + /* If vlan filtering is disabled and there are any VLANs + * configured on top of the bridge, set promisc on all + * ports. + */ + if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev)) + set_all = true; list_for_each_entry(p, &br->port_list, list) { - if (br->dev->flags & IFF_PROMISC) { - /* PROMISC flag has been turned on for the bridge - * itself. Turn on promisc on all ports. - */ + if (set_all) { + /* Set all the ports to promisc mode. */ br_port_set_promisc(p); } else { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 4042f86..87dcc09 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v) return v->pvid ?: VLAN_N_VID; } +static inline int br_vlan_enabled(struct net_bridge *br) +{ + return br->vlan_enabled; +} #else static inline bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v) { return VLAN_N_VID; /* Returns invalid vid */ } + +static inline int br_vlan_enabled(struct net_bridge *br); +{ + return 0; +} #endif /* br_netfilter.c */ diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 8249ca7..eddc2f6 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) goto unlock; br->vlan_enabled = val; + br_manage_promisc(br); unlock: rtnl_unlock(); -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[Bridge] [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
If the user configures vlan interfaces on top of the bridge and the bridge doesn't have vlan filtering enabled, we have to place all the ports in promsic mode so that we can correctly receive tagged frames. When vlan filtering is enabled, the vlan configuration will be provided via filtering interface. When the vlan filtering is toggled, we also have mange promiscuity. Signed-off-by: Vlad Yasevich <vyasevic at redhat.com> --- net/bridge/br_device.c | 14 ++++++++++++++ net/bridge/br_if.c | 17 +++++++++++++---- net/bridge/br_private.h | 9 +++++++++ net/bridge/br_vlan.c | 1 + 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 0af9d6c..967abb3 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p) #endif +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid) +{ + br_manage_promisc(netdev_priv(br_dev)); + return 0; +} + +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid) +{ + br_manage_promisc(netdev_priv(br_dev)); + return 0; +} + static int br_add_slave(struct net_device *dev, struct net_device *slave_dev) { @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = { .ndo_change_rx_flags = br_dev_change_rx_flags, .ndo_change_mtu = br_change_mtu, .ndo_do_ioctl = br_dev_ioctl, + .ndo_vlan_rx_add_vid = br_dev_rx_add_vid, + .ndo_vlan_rx_kill_vid = br_dev_rx_kill_vid, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_netpoll_setup = br_netpoll_setup, .ndo_netpoll_cleanup = br_netpoll_cleanup, diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 7e92bd0..55e4e28 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p) void br_manage_promisc(struct net_bridge *br) { struct net_bridge_port *p; + int set_all = false; + + if (br->dev->flags & IFF_PROMISC) + set_all = true; + + /* If vlan filtering is disabled and there are any VLANs + * configured on top of the bridge, set promisc on all + * ports. + */ + if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev)) + set_all = true; list_for_each_entry(p, &br->port_list, list) { - if (br->dev->flags & IFF_PROMISC) { - /* PROMISC flag has been turned on for the bridge - * itself. Turn on promisc on all ports. - */ + if (set_all) { + /* Set all the ports to promisc mode. */ br_port_set_promisc(p); } else { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 4042f86..87dcc09 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v) return v->pvid ?: VLAN_N_VID; } +static inline int br_vlan_enabled(struct net_bridge *br) +{ + return br->vlan_enabled; +} #else static inline bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v) { return VLAN_N_VID; /* Returns invalid vid */ } + +static inline int br_vlan_enabled(struct net_bridge *br); +{ + return 0; +} #endif /* br_netfilter.c */ diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 8249ca7..eddc2f6 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) goto unlock; br->vlan_enabled = val; + br_manage_promisc(br); unlock: rtnl_unlock(); -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
Configuration where all ports are set to non-flooding is a slight special case. In this config, the user would have to manage all fdbs for all ports. In this special case, since we'll know all addresses, we can turn off promisc on all ports and program all ports with the same set of addresses. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> --- include/linux/netdevice.h | 3 +++ net/bridge/br_fdb.c | 22 +++++++++++++++++----- net/bridge/br_if.c | 40 ++++++++++++++++++++++++++++++++++------ net/bridge/br_private.h | 2 +- net/core/dev_addr_lists.c | 7 ++++--- 5 files changed, 59 insertions(+), 15 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e29cce1..79e97ee 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list, unsigned char addr_type); int __hw_addr_sync(struct netdev_hw_addr_list *to_list, struct netdev_hw_addr_list *from_list, int addr_len); +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len); void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, struct netdev_hw_addr_list *from_list, int addr_len); void __hw_addr_init(struct netdev_hw_addr_list *list); diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index ef95e81..26ea4fe 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br) if (br->n_flood_ports == 1) { p = br->c_flood_port; netif_addr_lock(p->dev); - err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs, - p->dev->addr_len); + err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs, + p->dev->addr_len); if (!err) __dev_set_rx_mode(p->dev); netif_addr_unlock(p->dev); + } else if (br->n_flood_ports == 0) { + list_for_each_entry(p, &br->port_list, list) { + /* skip over ports being deleted. */ + if (!br_port_exists(p->dev)) + continue; + netif_addr_lock_nested(p->dev); + err = __hw_addr_sync_multiple(&p->dev->uc, + &br->conf_addrs, + p->dev->addr_len); + if (!err) + __dev_set_rx_mode(p->dev); + netif_addr_unlock(p->dev); + } } + } -void br_fdb_addrs_unsync(struct net_bridge *br) +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p) { - struct net_bridge_port *p = br->c_flood_port; - if (!p) return; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 55e4e28..4ba62ef 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p) if (p->flags & BR_FLOOD) br_del_flood_port(p, br); + else + br_fdb_addrs_unsync(br, p); nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 1); @@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) * only have 1 flooding port cache if for future use. */ br->n_flood_ports++; - if (br->n_flood_ports == 1) + if (br->n_flood_ports == 1) { + struct net_bridge_port *port; + + /* We are transitioning from 0 flood ports to 1. Remove + * the addresses from all the non-flood ports and turn on + * promisc on those ports. + */ + list_for_each_entry(port, &br->port_list, list) { + /* skip the current port we are changing */ + if (port == p) + continue; + + if (!(port->flags & BR_FLOOD)) { + br_port_set_promisc(port); + br_fdb_addrs_unsync(br, port); + } + } + br->c_flood_port = p; + } br_fdb_addrs_sync(br); br_manage_promisc(br); @@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) * set it if it is not set. */ br->n_flood_ports--; - if (p == br->c_flood_port) { - br_fdb_addrs_unsync(br); - br->c_flood_port = NULL; - } + if (br->n_flood_ports == 0) { + /* We just dropped to 0 flood ports. If we + * are deleting this port, unsync addresses + * from it. + */ + if (!br_port_exists(p->dev)) + br_fdb_addrs_unsync(br, p); - if (br->n_flood_ports == 1) { + br->c_flood_port = NULL; + br_fdb_addrs_sync(br); + } else if (br->n_flood_ports == 1) { + /* We just dropped to 1 flood port. Find this one flood + * port and sync to it. + */ list_for_each_entry(port, &p->br->port_list, list) { if (br_port_exists(port->dev) && (port->flags & BR_FLOOD)) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 87dcc09..13840de 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, int idx); void br_fdb_addrs_sync(struct net_bridge *br); -void br_fdb_addrs_unsync(struct net_bridge *br); +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p); /* br_forward.c */ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 3de44a3..24da78f 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list, __hw_addr_del_entry(from_list, ha, false, false); } -static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len) +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len) { int err = 0; struct netdev_hw_addr *ha, *tmp; @@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list, } return err; } +EXPORT_SYMBOL(__hw_addr_sync_multiple); /* This function only works where there is a strict 1-1 relationship * between source and destionation of they synch. If you ever need to -- 1.8.5.3
Vlad Yasevich
2014-Feb-26 15:18 UTC
[Bridge] [PATCH 7/7] bridge: Support promisc management when all ports are non-flooding
Configuration where all ports are set to non-flooding is a slight special case. In this config, the user would have to manage all fdbs for all ports. In this special case, since we'll know all addresses, we can turn off promisc on all ports and program all ports with the same set of addresses. Signed-off-by: Vlad Yasevich <vyasevic at redhat.com> --- include/linux/netdevice.h | 3 +++ net/bridge/br_fdb.c | 22 +++++++++++++++++----- net/bridge/br_if.c | 40 ++++++++++++++++++++++++++++++++++------ net/bridge/br_private.h | 2 +- net/core/dev_addr_lists.c | 7 ++++--- 5 files changed, 59 insertions(+), 15 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e29cce1..79e97ee 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2889,6 +2889,9 @@ int __hw_addr_del(struct netdev_hw_addr_list *list, unsigned char addr_type); int __hw_addr_sync(struct netdev_hw_addr_list *to_list, struct netdev_hw_addr_list *from_list, int addr_len); +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len); void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, struct netdev_hw_addr_list *from_list, int addr_len); void __hw_addr_init(struct netdev_hw_addr_list *list); diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index ef95e81..26ea4fe 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -911,19 +911,31 @@ void br_fdb_addrs_sync(struct net_bridge *br) if (br->n_flood_ports == 1) { p = br->c_flood_port; netif_addr_lock(p->dev); - err = __hw_addr_sync(&p->dev->uc, &br->conf_addrs, - p->dev->addr_len); + err = __hw_addr_sync_multiple(&p->dev->uc, &br->conf_addrs, + p->dev->addr_len); if (!err) __dev_set_rx_mode(p->dev); netif_addr_unlock(p->dev); + } else if (br->n_flood_ports == 0) { + list_for_each_entry(p, &br->port_list, list) { + /* skip over ports being deleted. */ + if (!br_port_exists(p->dev)) + continue; + netif_addr_lock_nested(p->dev); + err = __hw_addr_sync_multiple(&p->dev->uc, + &br->conf_addrs, + p->dev->addr_len); + if (!err) + __dev_set_rx_mode(p->dev); + netif_addr_unlock(p->dev); + } } + } -void br_fdb_addrs_unsync(struct net_bridge *br) +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p) { - struct net_bridge_port *p = br->c_flood_port; - if (!p) return; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 55e4e28..4ba62ef 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -148,6 +148,8 @@ static void del_nbp(struct net_bridge_port *p) if (p->flags & BR_FLOOD) br_del_flood_port(p, br); + else + br_fdb_addrs_unsync(br, p); nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 1); @@ -530,8 +532,26 @@ static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) * only have 1 flooding port cache if for future use. */ br->n_flood_ports++; - if (br->n_flood_ports == 1) + if (br->n_flood_ports == 1) { + struct net_bridge_port *port; + + /* We are transitioning from 0 flood ports to 1. Remove + * the addresses from all the non-flood ports and turn on + * promisc on those ports. + */ + list_for_each_entry(port, &br->port_list, list) { + /* skip the current port we are changing */ + if (port == p) + continue; + + if (!(port->flags & BR_FLOOD)) { + br_port_set_promisc(port); + br_fdb_addrs_unsync(br, port); + } + } + br->c_flood_port = p; + } br_fdb_addrs_sync(br); br_manage_promisc(br); @@ -547,12 +567,20 @@ static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) * set it if it is not set. */ br->n_flood_ports--; - if (p == br->c_flood_port) { - br_fdb_addrs_unsync(br); - br->c_flood_port = NULL; - } + if (br->n_flood_ports == 0) { + /* We just dropped to 0 flood ports. If we + * are deleting this port, unsync addresses + * from it. + */ + if (!br_port_exists(p->dev)) + br_fdb_addrs_unsync(br, p); - if (br->n_flood_ports == 1) { + br->c_flood_port = NULL; + br_fdb_addrs_sync(br); + } else if (br->n_flood_ports == 1) { + /* We just dropped to 1 flood port. Find this one flood + * port and sync to it. + */ list_for_each_entry(port, &p->br->port_list, list) { if (br_port_exists(port->dev) && (port->flags & BR_FLOOD)) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 87dcc09..13840de 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -400,7 +400,7 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, int idx); void br_fdb_addrs_sync(struct net_bridge *br); -void br_fdb_addrs_unsync(struct net_bridge *br); +void br_fdb_addrs_unsync(struct net_bridge *br, struct net_bridge_port *p); /* br_forward.c */ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 3de44a3..24da78f 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -171,9 +171,9 @@ static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list, __hw_addr_del_entry(from_list, ha, false, false); } -static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len) +int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len) { int err = 0; struct netdev_hw_addr *ha, *tmp; @@ -189,6 +189,7 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list, } return err; } +EXPORT_SYMBOL(__hw_addr_sync_multiple); /* This function only works where there is a strict 1-1 relationship * between source and destionation of they synch. If you ever need to -- 1.8.5.3
Michael S. Tsirkin
2014-Feb-26 16:34 UTC
[Bridge] [PATCH RFC 0/7] Non-promisc bidge ports support
On Wed, Feb 26, 2014 at 10:18:18AM -0500, Vlad Yasevich wrote:> This patch series is a complete re-design and re-implementation of > prior attempts to support non-promiscuous bridge ports.Nice. For those that wonder: the setups that benefit from this could look e.g. like this: A---+ | | BRIDGE--C | | B---+ If A, B, C all use standard NICs without setting them into promiscous mode, unicast packets sent to e.g. A that don't match the address of its NIC will be dropped anyway. Same applies to B and C. With this patchset we'll be able to bypass the need for promisc mode on NICs in the BRIDGE box.> The basic design is as follows. The bridge keeps track of > all the ports that flood packets to unknown destinations. If > the flooding is disabled on the port, to get traffic to flow > through, user/management would need to add an fdb describing > such traffic. When such fdb is added, we save the address > to bridge private hardware address list. > Since we now have static configuration for all non-flooding > ports and only 1 flooding port, we can make this single port > non-promiscuous and program the receive filter with our list > of addresses. On HW that doesn't support unicast filtering or > if the list too bit, the device will be placed in promiscuous mode > by the application of the filter. > > There are multiple reasons I chose to do private hw address > list in the bridge in patch 3: > 1) I tried using the fdb table itself as main repository, but > this caused difficulties in synchronizing this table with > the interface filters later on. > 2) I tried using the bridge device 'uc' list to store these > addresses, but that caused issues with devices on top of > a bridge (vlans, bonds) that changed their mac addresses > and propagated this down to bridge. I recently figured > out a way that might allow us to do this which involves > learning to be added br_dev_xmi(). We can discuss this, > if there serious objections to current proposal. > > There are some other cases when promiscuous mode has to be turned > back on. One is when the bridge itself if placed in promiscuous > mode (use sets promisc flag). The other is when vlans devices are > configured on top of the bridge and vlan filtering is disabled (default). > This allows the bridge to receive all tagged frames and doesn't create > a dependency between this code and vlan filtering. > > The last patch in the series is a special case where all ports > are non-flooding. This could be useful in a routed configurations. > In this case, since all ports will be configured manually, we can > sync the our address list across all port of the bridge and make all > ports non-promiscuous. > > Thanks > -vlad > > Vlad Yasevich (7): > bridge: Turn flag change macro into a function. > bridge: Keep track of ports capable of flooding. > bridge: Add addresses from static fdbs to bridge address list > bridge: Automatically manage port promiscuous mode. > bridge: Correctly manage promiscuity when user requested it. > bridge: Manage promisc mode when vlans are configured on top of a > bridge > bridge: Support promisc management when all ports are non-flooding > > include/linux/netdevice.h | 9 +++ > net/bridge/br_device.c | 23 +++++++ > net/bridge/br_fdb.c | 122 +++++++++++++++++++++++++++++++++-- > net/bridge/br_if.c | 159 ++++++++++++++++++++++++++++++++++++++++++++-- > net/bridge/br_netlink.c | 3 + > net/bridge/br_private.h | 18 ++++++ > net/bridge/br_sysfs_if.c | 33 +++++++--- > net/bridge/br_vlan.c | 1 + > net/core/dev.c | 1 + > net/core/dev_addr_lists.c | 21 +++--- > 10 files changed, 361 insertions(+), 29 deletions(-) > > -- > 1.8.5.3
Jamal Hadi Salim
2014-Feb-26 23:59 UTC
[Bridge] [PATCH RFC 0/7] Non-promisc bidge ports support
On 02/26/14 10:18, Vlad Yasevich wrote:> This patch series is a complete re-design and re-implementation of > prior attempts to support non-promiscuous bridge ports. > > The basic design is as follows. The bridge keeps track of > all the ports that flood packets to unknown destinations. If > the flooding is disabled on the port, to get traffic to flow > through, user/management would need to add an fdb describing > such traffic. When such fdb is added, we save the address > to bridge private hardware address list.Entering the addresses in the uc list on other bridgeports seems reasonable for the scenario described. But would it _also_ need to be added to the fdb of the bridge? i.e how does the bridge (if the packet was to be handed to it) know where to forward? BTW: on the comment that flooding off implies learning off: I would like to be able to turn off flooding on a specific bridge port but still want to learn from it. I dont think those two are mutually exclusive. cheers, jamal
Michael S. Tsirkin
2014-Feb-27 07:20 UTC
Re: [PATCH RFC 0/7] Non-promisc bidge ports support
On Wed, Feb 26, 2014 at 06:59:29PM -0500, Jamal Hadi Salim wrote:> On 02/26/14 10:18, Vlad Yasevich wrote: > >This patch series is a complete re-design and re-implementation of > >prior attempts to support non-promiscuous bridge ports. > > > >The basic design is as follows. The bridge keeps track of > >all the ports that flood packets to unknown destinations. If > >the flooding is disabled on the port, to get traffic to flow > >through, user/management would need to add an fdb describing > >such traffic. When such fdb is added, we save the address > >to bridge private hardware address list. > > Entering the addresses in the uc list on other bridgeports seems > reasonable for the scenario described. > But would it _also_ need to be added to the fdb of the bridge? > i.e how does the bridge (if the packet was to be handed to it) > know where to forward? > BTW: on the comment that flooding off implies learning off: I would like > to be able to turn off flooding on a specific bridge port but > still want to learn from it. I dont think those two are mutually > exclusive. > > cheers, > jamalI agree. It seems a reasonable tradeoff to limit any specific optimization to !flood && !learn if this simplifies the implementation significantly and if everything works as it did before even with learning on. -- MST
Toshiaki Makita
2014-Feb-27 11:59 UTC
Re: [PATCH 2/7] bridge: Keep track of ports capable of flooding.
(2014/02/27 0:18), Vlad Yasevich wrote:> Keep track of bridge ports that have unicast flooding turned on. > This will later be used by the algorithm to automatically manage > address programming and promisc mode. >...> @@ -383,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) > dev_disable_lro(dev); > > list_add_rcu(&p->list, &br->port_list); > + > + if (p->flags & BR_FLOOD) > + br_add_flood_port(p, br);BR_FLOOD seems to be set in new_nbp(). Is this condition check needed?> > netdev_update_features(br->dev); > > @@ -455,3 +464,50 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) > > return 0; > } > + > +static void br_add_flood_port(struct net_bridge_port *p, struct net_bridge *br) > +{ > + /* Increment the number of flooding ports, and if we > + * only have 1 flooding port cache if for future use. > + */ > + br->n_flood_ports++; > + if (br->n_flood_ports == 1) > + br->c_flood_port = p;Needn't c_flood_port be cleared if n_flood_ports == 2? Thanks, Toshiaki Makita> +} > + > +static void br_del_flood_port(struct net_bridge_port *p, struct net_bridge *br) > +{ > + struct net_bridge_port *port; > + > + /* Decrement the number of flood port. > + * If we are deleting the current flood port, clear > + * the cached port. If we are down to 1 flood port, > + * set it if it is not set. > + */ > + br->n_flood_ports--; > + if (p == br->c_flood_port) > + br->c_flood_port = NULL; > + > + if (br->n_flood_ports == 1) { > + list_for_each_entry(port, &p->br->port_list, list) { > + if (port->flags & BR_FLOOD) { > + br->c_flood_port = port; > + break; > + } > + } > + } > +} > + > +void br_port_flags_change(struct net_bridge_port *p, unsigned long mask) > +{ > + struct net_bridge *br = p->br; > + > + /* We are only interested FLOOD flag */ > + if (!(mask & BR_FLOOD)) > + return; > + > + if (p->flags & BR_FLOOD) > + br_add_flood_port(p, br); > + else > + br_del_flood_port(p, br); > +} > diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c > index e74b6d53..01382b9 100644 > --- a/net/bridge/br_netlink.c > +++ b/net/bridge/br_netlink.c > @@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], > static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) > { > int err; > + unsigned long old_flags = p->flags; > > br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); > br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); > @@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) > if (err) > return err; > } > + > + br_port_flags_change(p, old_flags ^ p->flags); > return 0; > } > > diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h > index 3ba11bc..26a3987 100644 > --- a/net/bridge/br_private.h > +++ b/net/bridge/br_private.h > @@ -290,6 +290,8 @@ struct net_bridge > struct timer_list topology_change_timer; > struct timer_list gc_timer; > struct kobject *ifobj; > + struct net_bridge_port *c_flood_port; > + u32 n_flood_ports; > #ifdef CONFIG_BRIDGE_VLAN_FILTERING > u8 vlan_enabled; > struct net_port_vlans __rcu *vlan_info; > @@ -415,6 +417,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev); > int br_min_mtu(const struct net_bridge *br); > netdev_features_t br_features_recompute(struct net_bridge *br, > netdev_features_t features); > +void br_port_flags_change(struct net_bridge_port *port, > + unsigned long mask); > > /* br_input.c */ > int br_handle_frame_finish(struct sk_buff *skb); > diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c > index 7f66aa4..9ff6691 100644 > --- a/net/bridge/br_sysfs_if.c > +++ b/net/bridge/br_sysfs_if.c > @@ -51,7 +51,10 @@ static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR, \ > static int store_flag(struct net_bridge_port *p, unsigned long v, > unsigned long mask) > { > - unsigned long flags = p->flags; > + unsigned long flags; > + unsigned long old_flags; > + > + old_flags = flags = p->flags; > > if (v) > flags |= mask; > @@ -60,6 +63,7 @@ static int store_flag(struct net_bridge_port *p, unsigned long v, > > if (flags != p->flags) { > p->flags = flags; > + br_port_flags_change(p, old_flags ^ flags); > br_ifinfo_notify(RTM_NEWLINK, p); > } > return 0; >
Toshiaki Makita
2014-Feb-27 12:06 UTC
Re: [PATCH 6/7] bridge: Manage promisc mode when vlans are configured on top of a bridge
(2014/02/27 0:18), Vlad Yasevich wrote:> If the user configures vlan interfaces on top of the bridge and the bridge > doesn't have vlan filtering enabled, we have to place all the ports in > promsic mode so that we can correctly receive tagged frames. > When vlan filtering is enabled, the vlan configuration will be provided > via filtering interface. > When the vlan filtering is toggled, we also have mange promiscuity.If we disable vlan_filtering and no vlan interface is configured on the bridge, we cannot forward any tagged traffic? If we want to forward frames from one port to another port (not from/to bridge device), we have to add vlan interface or set promisc mode, right? Sorry if I misunderstood it. I'm not sure.. Thanks, Toshiaki Makita> > Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> > --- > net/bridge/br_device.c | 14 ++++++++++++++ > net/bridge/br_if.c | 17 +++++++++++++---- > net/bridge/br_private.h | 9 +++++++++ > net/bridge/br_vlan.c | 1 + > 4 files changed, 37 insertions(+), 4 deletions(-) > > diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c > index 0af9d6c..967abb3 100644 > --- a/net/bridge/br_device.c > +++ b/net/bridge/br_device.c > @@ -297,6 +297,18 @@ void br_netpoll_disable(struct net_bridge_port *p) > > #endif > > +static int br_dev_rx_add_vid(struct net_device *br_dev, __be16 proto, u16 vid) > +{ > + br_manage_promisc(netdev_priv(br_dev)); > + return 0; > +} > + > +static int br_dev_rx_kill_vid(struct net_device *br_dev, __be16 proto, u16 vid) > +{ > + br_manage_promisc(netdev_priv(br_dev)); > + return 0; > +} > + > static int br_add_slave(struct net_device *dev, struct net_device *slave_dev) > > { > @@ -328,6 +340,8 @@ static const struct net_device_ops br_netdev_ops = { > .ndo_change_rx_flags = br_dev_change_rx_flags, > .ndo_change_mtu = br_change_mtu, > .ndo_do_ioctl = br_dev_ioctl, > + .ndo_vlan_rx_add_vid = br_dev_rx_add_vid, > + .ndo_vlan_rx_kill_vid = br_dev_rx_kill_vid, > #ifdef CONFIG_NET_POLL_CONTROLLER > .ndo_netpoll_setup = br_netpoll_setup, > .ndo_netpoll_cleanup = br_netpoll_cleanup, > diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c > index 7e92bd0..55e4e28 100644 > --- a/net/bridge/br_if.c > +++ b/net/bridge/br_if.c > @@ -497,12 +497,21 @@ static void br_port_clear_promisc(struct net_bridge_port *p) > void br_manage_promisc(struct net_bridge *br) > { > struct net_bridge_port *p; > + int set_all = false; > + > + if (br->dev->flags & IFF_PROMISC) > + set_all = true; > + > + /* If vlan filtering is disabled and there are any VLANs > + * configured on top of the bridge, set promisc on all > + * ports. > + */ > + if (!br_vlan_enabled(br) && vlan_uses_dev(br->dev)) > + set_all = true; > > list_for_each_entry(p, &br->port_list, list) { > - if (br->dev->flags & IFF_PROMISC) { > - /* PROMISC flag has been turned on for the bridge > - * itself. Turn on promisc on all ports. > - */ > + if (set_all) { > + /* Set all the ports to promisc mode. */ > br_port_set_promisc(p); > > } else { > diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h > index 4042f86..87dcc09 100644 > --- a/net/bridge/br_private.h > +++ b/net/bridge/br_private.h > @@ -641,6 +641,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v) > return v->pvid ?: VLAN_N_VID; > } > > +static inline int br_vlan_enabled(struct net_bridge *br) > +{ > + return br->vlan_enabled; > +} > #else > static inline bool br_allowed_ingress(struct net_bridge *br, > struct net_port_vlans *v, > @@ -721,6 +725,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v) > { > return VLAN_N_VID; /* Returns invalid vid */ > } > + > +static inline int br_vlan_enabled(struct net_bridge *br); > +{ > + return 0; > +} > #endif > > /* br_netfilter.c */ > diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c > index 8249ca7..eddc2f6 100644 > --- a/net/bridge/br_vlan.c > +++ b/net/bridge/br_vlan.c > @@ -321,6 +321,7 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) > goto unlock; > > br->vlan_enabled = val; > + br_manage_promisc(br); > > unlock: > rtnl_unlock(); >