ipv6: adjust MLD membership on address state changes
If MLD support is enabled, each locally assigned IPv6 address in the
appropriate state must be a member of the solicited-node multicast
group corresponding to that address. Ensure that this is always the
case by (re-)deciding on the membership upon every address state
change. By doing so, this patch enforces that user-initiated state
changes to addresses (e.g., deletion) never cause a desynchronization
with the corresponding solicited-node multicast group membership,
thereby making such user-initiated state changes simpler and safer.
diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c
index ca7d8e9..23cbf5f 100644
--- a/src/core/ipv6/nd6.c
+++ b/src/core/ipv6/nd6.c
@@ -171,12 +171,6 @@
/* We are using a duplicate address. */
netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
-#if LWIP_IPV6_MLD
- /* Leave solicited node multicast group. */
- ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(inp, i)->addr[3]);
- mld6_leavegroup_netif(inp, &multicast_address);
-#endif /* LWIP_IPV6_MLD */
-
#if LWIP_IPV6_AUTOCONFIG
/* Check to see if this address was autoconfigured. */
if (!ip6_addr_islinklocal(&target_address)) {
@@ -871,13 +865,6 @@
netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED);
/* @todo implement preferred and valid lifetimes. */
} else if (netif->flags & NETIF_FLAG_UP) {
-#if LWIP_IPV6_MLD
- if ((addr_state & IP6_ADDR_TENTATIVE_COUNT_MASK) == 0) {
- /* Join solicited node multicast group. */
- ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]);
- mld6_joingroup_netif(netif, &multicast_address);
- }
-#endif /* LWIP_IPV6_MLD */
/* Send a NS for this address. */
nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST);
/* tentative: set next state by increasing by one */
@@ -2074,4 +2061,38 @@
}
}
+#if LWIP_IPV6_MLD
+/**
+ * The state of a local IPv6 address entry is about to change. If needed, join
+ * or leave the solicited-node multicast group for the address.
+ *
+ * @param netif The netif that owns the address.
+ * @param addr_idx The index of the address.
+ * @param new_state The new (IP6_ADDR_) state for the address.
+ */
+void
+nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state)
+{
+ u8_t old_state, old_member, new_member;
+
+ old_state = netif_ip6_addr_state(netif, addr_idx);
+
+ /* Determine whether we were, and should be, a member of the solicited-node
+ * multicast group for this address. For tentative addresses, the group is
+ * not joined until the address enters the TENTATIVE_1 (or VALID) state. */
+ old_member = (old_state != IP6_ADDR_INVALID && old_state != IP6_ADDR_TENTATIVE);
+ new_member = (new_state != IP6_ADDR_INVALID && new_state != IP6_ADDR_TENTATIVE);
+
+ if (old_member != new_member) {
+ ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]);
+
+ if (new_member) {
+ mld6_joingroup_netif(netif, &multicast_address);
+ } else {
+ mld6_leavegroup_netif(netif, &multicast_address);
+ }
+ }
+}
+#endif /* LWIP_IPV6_MLD */
+
#endif /* LWIP_IPV6 */
diff --git a/src/core/netif.c b/src/core/netif.c
index 1d3de0f..428b148 100644
--- a/src/core/netif.c
+++ b/src/core/netif.c
@@ -1096,6 +1096,13 @@
u8_t new_valid = state & IP6_ADDR_VALID;
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set_state: netif address state being changed\n"));
+#if LWIP_IPV6_MLD
+ /* Reevaluate solicited-node multicast group membership. */
+ if (netif->flags & NETIF_FLAG_MLD6) {
+ nd6_adjust_mld_membership(netif, addr_idx, state);
+ }
+#endif /* LWIP_IPV6_MLD */
+
if (old_valid && !new_valid) {
/* address about to be removed by setting invalid */
#if LWIP_TCP
diff --git a/src/include/lwip/nd6.h b/src/include/lwip/nd6.h
index 817dfb9..8204fa4 100644
--- a/src/include/lwip/nd6.h
+++ b/src/include/lwip/nd6.h
@@ -71,6 +71,9 @@
void nd6_reachability_hint(const ip6_addr_t *ip6addr);
#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
void nd6_cleanup_netif(struct netif *netif);
+#if LWIP_IPV6_MLD
+void nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state);
+#endif /* LWIP_IPV6_MLD */
#ifdef __cplusplus
}