Trigger the bridge to (re)start sending out Queries to the Host once IPv6 address becomes valid.
In current implementation, once the bridge (interface) is brought up, the bridge will start trying to send v4 and v6 Queries to the Host immediately. However, at that time most likely the IPv6 address of the bridge interface is not valid yet, and thus the send (actually the alloc) operation will fail. So the first v6 Startup Query is always missed. This caused a ripple effect on the timing of Querier Election. In current implementation, :: always wins the election. In order for the "real" election to take place, the bridge would have to first select itself (this happens when a v6 Query is successfully sent to the Host), and then do the real address comparison when the next Query is received. In worst cast scenario, the bridge would have to wait for [Startup Query Interval] seconds (for the second Query to be sent to the Host) plus [Query Interval] seconds (for the real Querier to send the next Query) before it can recognize the real Querier. This patch adds a new notification NETDEV_NEWADDR when IPv6 address becomes valid. When the bridge receives the notification, it will restart the Startup Queries (much like how the bridge handles port NETDEV_CHANGE events today). Signed-off-by: Joseph Huang <joseph.hu...@garmin.com> --- include/linux/netdevice.h | 1 + net/bridge/br.c | 5 +++++ net/bridge/br_multicast.c | 16 ++++++++++++++++ net/bridge/br_private.h | 1 + net/core/dev.c | 10 +++++----- net/ipv6/addrconf.c | 3 +++ 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f3a3b761abfb..27297e46e064 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3129,6 +3129,7 @@ enum netdev_cmd { NETDEV_REGISTER, NETDEV_UNREGISTER, NETDEV_CHANGEMTU, /* notify after mtu change happened */ + NETDEV_NEWADDR, NETDEV_CHANGEADDR, /* notify after the address change */ NETDEV_PRE_CHANGEADDR, /* notify before the address change */ NETDEV_GOING_DOWN, diff --git a/net/bridge/br.c b/net/bridge/br.c index c683baa3847f..6f66965e8075 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -49,6 +49,11 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v return NOTIFY_DONE; } + + if (event == NETDEV_NEWADDR) { + br_multicast_enable_host(netdev_priv(dev)); + return NOTIFY_DONE; + } } if (is_vlan_dev(dev)) { diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 8ce145938b02..5a138c5731f5 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -2076,6 +2076,22 @@ static void br_multicast_enable(struct bridge_mcast_own_query *query) mod_timer(&query->timer, jiffies); } +void br_multicast_enable_host(struct net_bridge *br) +{ +#if IS_ENABLED(CONFIG_IPV6) + spin_lock_bh(&br->multicast_lock); + + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED) || + !netif_running(br->dev)) + goto out; + + br_multicast_enable(&br->multicast_ctx.ip6_own_query); + +out: + spin_unlock_bh(&br->multicast_lock); +#endif +} + static void __br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx) { struct net_bridge *br = pmctx->port->br; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 8de0904b9627..16864286dc0d 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -967,6 +967,7 @@ br_mdb_entry_skb_get(struct net_bridge_mcast *brmctx, struct sk_buff *skb, u16 vid); int br_multicast_add_port(struct net_bridge_port *port); void br_multicast_del_port(struct net_bridge_port *port); +void br_multicast_enable_host(struct net_bridge *br); void br_multicast_enable_port(struct net_bridge_port *port); void br_multicast_disable_port(struct net_bridge_port *port); void br_multicast_init(struct net_bridge *br); diff --git a/net/core/dev.c b/net/core/dev.c index 93a25d87b86b..70a9f379f003 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1843,11 +1843,11 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd) return "NETDEV_" __stringify(val); switch (cmd) { N(UP) N(DOWN) N(REBOOT) N(CHANGE) N(REGISTER) N(UNREGISTER) - N(CHANGEMTU) N(CHANGEADDR) N(GOING_DOWN) N(CHANGENAME) N(FEAT_CHANGE) - N(BONDING_FAILOVER) N(PRE_UP) N(PRE_TYPE_CHANGE) N(POST_TYPE_CHANGE) - N(POST_INIT) N(PRE_UNINIT) N(RELEASE) N(NOTIFY_PEERS) N(JOIN) - N(CHANGEUPPER) N(RESEND_IGMP) N(PRECHANGEMTU) N(CHANGEINFODATA) - N(BONDING_INFO) N(PRECHANGEUPPER) N(CHANGELOWERSTATE) + N(CHANGEMTU) N(NEWADDR) N(CHANGEADDR) N(GOING_DOWN) N(CHANGENAME) + N(FEAT_CHANGE) N(BONDING_FAILOVER) N(PRE_UP) N(PRE_TYPE_CHANGE) + N(POST_TYPE_CHANGE) N(POST_INIT) N(PRE_UNINIT) N(RELEASE) + N(NOTIFY_PEERS) N(JOIN) N(CHANGEUPPER) N(RESEND_IGMP) N(PRECHANGEMTU) + N(CHANGEINFODATA) N(BONDING_INFO) N(PRECHANGEUPPER) N(CHANGELOWERSTATE) N(UDP_TUNNEL_PUSH_INFO) N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN) N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO) N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f17a5dd4789f..785952377d69 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6292,6 +6292,9 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) addrconf_prefix_route(&ifp->peer_addr, 128, ifp->rt_priority, ifp->idev->dev, 0, 0, GFP_ATOMIC); + + call_netdevice_notifiers(NETDEV_NEWADDR, ifp->idev->dev); + break; case RTM_DELADDR: if (ifp->idev->cnf.forwarding) -- 2.50.1 ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you.