diff --git a/include/nuttx/net/mld.h b/include/nuttx/net/mld.h index f33e5d3a3e..51e24574af 100644 --- a/include/nuttx/net/mld.h +++ b/include/nuttx/net/mld.h @@ -375,8 +375,8 @@ struct mld_mcast_listen_done_v1_s struct mld_stats_s { - net_stats_t joins; /* Requests to join a group */ - net_stats_t leaves; /* Requests to leave a group */ + net_stats_t njoins; /* Requests to join a group */ + net_stats_t nleaves; /* Requests to leave a group */ net_stats_t query_sched; /* General QUERY packets scheduled */ net_stats_t report_sched; /* Unsolicited REPORT packets scheduled */ net_stats_t done_sched; /* DONE packets scheduled */ diff --git a/net/mld/Kconfig b/net/mld/Kconfig index a8256f77d3..6a830dc394 100644 --- a/net/mld/Kconfig +++ b/net/mld/Kconfig @@ -8,7 +8,6 @@ if NET_ICMPv6 menuconfig NET_MLD bool "Multicast Listener Discovery (MLD)" default n - depends on EXPERIMENTAL select NET_MCASTGROUP select NETDEV_IFINDEX ---help--- @@ -16,6 +15,14 @@ menuconfig NET_MLD if NET_MLD +config NET_MLD_ROUTER + bool # "MLD Router support" + default n + depends on EXPERIMENTAL + ---help--- + Enables a few hooks that will be needed for router support in the + future. Not yet ready for prime time. + config NET_MLD_DEBUG bool "Force MLD debug" default n diff --git a/net/mld/mld.h b/net/mld/mld.h index 562ee53e44..d94ce6df3c 100644 --- a/net/mld/mld.h +++ b/net/mld/mld.h @@ -211,10 +211,15 @@ struct mld_group_s WDOG_ID polldog; /* Timer used for periodic or delayed events */ WDOG_ID v1dog; /* MLDv1 compatibility mode timer */ sem_t sem; /* Used to wait for message transmission */ +#ifdef CONFIG_NET_MLD_ROUTER + uint16_t members; /* Number of members currently reporting (excludes us) */ + uint16_t lstmbrs; /* Number of members reporting (last query) */ +#endif uint8_t ifindex; /* Interface index */ uint8_t flags; /* See MLD_ flags definitions */ uint8_t msgtype; /* Pending message type to send (if non-zero) */ uint8_t count; /* Reports remaining in repetition count */ + uint8_t njoins; /* Number of joins from this host */ }; /**************************************************************************** diff --git a/net/mld/mld_done.c b/net/mld/mld_done.c index a95c608758..82fc75db46 100644 --- a/net/mld/mld_done.c +++ b/net/mld/mld_done.c @@ -81,7 +81,7 @@ int mld_done_v1(FAR struct net_driver_s *dev, FAR const struct mld_mcast_listen_done_v1_s *done) { -#ifdef CONFIG_MLD_ROUTER +#ifdef CONFIG_NET_MLD_ROUTER FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; FAR struct mld_group_s *group; diff --git a/net/mld/mld_join.c b/net/mld/mld_join.c index 28c6612a70..b0cc2ed6d1 100644 --- a/net/mld/mld_join.c +++ b/net/mld/mld_join.c @@ -52,6 +52,53 @@ #ifdef CONFIG_NET_MLD +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mld_group_startup + * + * Description: + * Set up the startup actions when the first member from this host joins + * the group. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static int mld_group_startup(FAR const struct ipv6_mreq *mrec, + FAR struct net_driver_s *dev, + FAR struct mld_group_s *group) +{ + int ret; + + /* Send the Version 2 Multicast Listener Report (Assumes MLDv2 mode + * initially). + */ + + MLD_STATINCR(g_netstats.mld.report_sched); + + ret = mld_waitmsg(group, MLD_SEND_V2REPORT); + if (ret < 0) + { + mlderr("ERROR: Failed to schedule Report: %d\n", ret); + return ret; + } + + /* And start the timer at 1 second */ + + SET_MLD_STARTUP(group->flags); + group->count = MLD_UNSOLREPORT_COUNT - 1; + mld_start_polltimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC)); + + /* Add the group (MAC) address to the Ethernet drivers MAC filter list */ + + mld_addmcastmac(dev, mrec->ipv6mr_multiaddr.s6_addr16); + return OK; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -80,7 +127,7 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec) { FAR struct net_driver_s *dev; - struct mld_group_s *group; + FAR struct mld_group_s *group; int ret; DEBUGASSERT(mrec != NULL); @@ -130,36 +177,66 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec) return -ENOMEM; } - MLD_STATINCR(g_netstats.mld.joins); + /* Indicate one request to join the group from this this host */ - /* Send the Version 2 Multicast Listener Report (Assumes MLDv2 mode - * initially?). - */ + group->njoins = 1; - MLD_STATINCR(g_netstats.mld.report_sched); - ret = mld_waitmsg(group, MLD_SEND_V2REPORT); + /* Set up the group startup operations */ + + ret = mld_group_startup(mrec, dev, group); if (ret < 0) { - mlderr("ERROR: Failed to schedule Report: %d\n", ret); + mlderr("ERROR: Failed to start group: %d\n", ret); mld_grpfree(dev, group); return ret; } - /* And start the timer at 1 second */ + MLD_STATINCR(g_netstats.mld.njoins); + } + else + { + /* The group already exists; a task from this host is joining an + * existing group. + */ - SET_MLD_STARTUP(group->flags); - group->count = MLD_UNSOLREPORT_COUNT - 1; - mld_start_polltimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC)); + mldinfo("Join existing group\n"); - /* Add the group (MAC) address to the Ethernet drivers MAC filter list */ +#ifdef CONFIG_NET_MLD_ROUTER + /* In the Router case this could still be the first join from this + * host. If this is the first join from this host, then we need to + * perform the group startup operations. + */ - mld_addmcastmac(dev, mrec->ipv6mr_multiaddr.s6_addr16); - return OK; + if (group->join == 0) + { + /* This is the for join from this host. Perform out start up + * operations. + */ + + ret = mld_group_startup(mrec, dev, group); + if (ret < 0) + { + mlderr("ERROR: Failed to start group: %d\n", ret); + mld_grpfree(dev, group); + return ret; + } + } +#else + /* Not a router? There there must be another join from this host or + * how could the group have been created? + */ + + DEBUGASSERT(group->njoins > 0); +#endif + + /* Indicate one more request to join the group from this this host */ + + DEBUGASSERT(group->njoins < UINT8_MAX); + group->njoins++; + MLD_STATINCR(g_netstats.mld.njoins); } - /* Return EEXIST if the address is already a member of the group */ - - return -EEXIST; + return OK; } #endif /* CONFIG_NET_MLD */ diff --git a/net/mld/mld_leave.c b/net/mld/mld_leave.c index 608af4513f..78655cad02 100644 --- a/net/mld/mld_leave.c +++ b/net/mld/mld_leave.c @@ -122,52 +122,87 @@ int mld_leavegroup(FAR const struct ipv6_mreq *mrec) { mldinfo("Leaving group: %p\n", group); - /* Cancel the timers and discard any queued Reports. Canceling the - * timer will prevent any new Reports from being sent; clearing the - * flags will discard any pending Reports that could interfere with - * leaving the group. + /* Indicate one fewer members of the group from this host */ + + DEBUGASSERT(group->njoins > 0); + group->njoins--; + MLD_STATINCR(g_netstats.mld.nleaves); + + /* Take no further actions if there are other members of this group + * on this host. */ - wd_cancel(group->polldog); - wd_cancel(group->v1dog); - CLR_MLD_SCHEDMSG(group->flags); - CLR_MLD_WAITMSG(group->flags); - - MLD_STATINCR(g_netstats.mld.leaves); - - /* Send a leave if the LASTREPORT flag is set for the group. If there - * are other members of the group, then their reports will clear the - * LAST REPORT flag. In this case we know that there are other - * members of the group and we do not have to send the Done message. - * - * The MLDv1 router responds to the Done message with a multicast-address- - * specific (MAS) Query. If any other node responds to the Query with a - * Report message the there are still listeners present. - * - * REVISIT: What if we are in MLDv2 mode? There is no Done MLDv2 Done - * messages. What should be sent (if anything) instead? - */ - - if (IS_MLD_LASTREPORT(group->flags)) + if (group->njoins > 0) { - mldinfo("Schedule Done message\n"); - - MLD_STATINCR(g_netstats.mld.done_sched); - - ret = mld_waitmsg(group, MLD_SEND_V1DONE); - if (ret < 0) - { - mlderr("ERROR: Failed to schedule message: %d\n", ret); - } + return OK; } - /* Free the group structure */ + /* Perform actions that would be performed only when the number of + * njoins from this host goes to zero. + */ - mld_grpfree(dev, group); + if (group->njoins == 0) + { + /* Send a leave if the LASTREPORT flag is set for the group. If + * there are other members of the group, then their reports will + * clear the LAST REPORT flag. In this case we know that there are + * other members of the group and we do not have to send the Done + * message. + * + * The MLDv1 router responds to the Done message with a multicast- + * address- specific (MAS) Query. If any other node responds to + * the Query with a Report message the there are still listeners + * present. + */ - /* And remove the group address from the Ethernet drivers MAC filter set */ + if (IS_MLD_LASTREPORT(group->flags)) + { + mldinfo("Schedule Done message\n"); + + MLD_STATINCR(g_netstats.mld.done_sched); + + /* REVIST: This will interfere is the are any other tasks + * waiting for a message to be sent. Can that happen? + */ + + ret = mld_waitmsg(group, MLD_SEND_V1DONE); + if (ret < 0) + { + mlderr("ERROR: Failed to schedule message: %d\n", ret); + } + } + + /* Remove the group address from the Ethernet drivers MAC filter + * set + */ + + mld_removemcastmac(dev, mrec->ipv6mr_multiaddr.s6_addr16); + + /* Perform additional actions if not a router OR if a router and the + * number of members not on this host is also zero. + */ + +#ifdef CONFIG_NET_MLD_ROUTER + if (group->members == 0 && group->lstmbrs == 0) +#endif + { + /* Cancel the timers and discard any queued Reports. Canceling + * the timer will prevent any new Reports from being sent; + * clearing the flags will discard any pending Reports that + * could interfere with freeing the group. + */ + + wd_cancel(group->polldog); + wd_cancel(group->v1dog); + CLR_MLD_SCHEDMSG(group->flags); + CLR_MLD_WAITMSG(group->flags); + + /* Free the group structure */ + + mld_grpfree(dev, group); + } + } - mld_removemcastmac(dev, mrec->ipv6mr_multiaddr.s6_addr16); return OK; } diff --git a/net/mld/mld_query.c b/net/mld/mld_query.c index 09c8906bb7..cc458c209c 100644 --- a/net/mld/mld_query.c +++ b/net/mld/mld_query.c @@ -394,6 +394,16 @@ int mld_query(FAR struct net_driver_s *dev, mldinfo("WARNING: MLDv2 query received in MLDv1 " "compatibility mode\n"); } + +#ifdef CONFIG_NET_MLD_ROUTER + /* Save the number of members that reported in the previous + * query cycle; reset the number of members that have + * reported in the new query cycle. + */ + + member->lstmbrs = member->members; + member->members = 0; +#endif } } @@ -458,6 +468,15 @@ int mld_query(FAR struct net_driver_s *dev, mld_check_querier(dev, ipv6, group); +#ifdef CONFIG_NET_MLD_ROUTER + /* Save the number of members that reported in the previous query cycle; + * reset the number of members that have reported in the new query cycle. + */ + + group->lstmbrs = group->members; + group->members = 0; +#endif + /* Warn if we received a MLDv2 query in MLDv1 compatibility mode. */ if (!mldv1 && IS_MLD_V1COMPAT(group->flags)) diff --git a/net/mld/mld_report.c b/net/mld/mld_report.c index b8c222b97f..05a7b7ff23 100644 --- a/net/mld/mld_report.c +++ b/net/mld/mld_report.c @@ -80,7 +80,7 @@ int mld_report(FAR struct net_driver_s *dev, FAR const net_ipv6addr_t grpaddr) * REVISIT: Router support is not yet implemented. */ -#ifdef CONFIG_MLD_ROUTER +#ifdef CONFIG_NET_MLD_ROUTER group = mld_grpallocfind(dev, grpaddr); if (group == NULL) { @@ -103,6 +103,12 @@ int mld_report(FAR struct net_driver_s *dev, FAR const net_ipv6addr_t grpaddr) CLR_MLD_LASTREPORT(group->flags); +#ifdef CONFIG_NET_MLD_ROUTER + /* Increment the number of members that reported in this query cycle. */ + + group->members++; +#endif + /* Need to set d_len to zero to indication that nothing is being sent */ dev->d_len = 0; diff --git a/net/mld/mld_send.c b/net/mld/mld_send.c index 827569744d..fe24b6fef0 100644 --- a/net/mld/mld_send.c +++ b/net/mld/mld_send.c @@ -284,6 +284,15 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, query->chksum = ~icmpv6_chksum(dev, MLD_HDRLEN); MLD_STATINCR(g_netstats.mld.query_sent); + +#ifdef CONFIG_NET_MLD_ROUTER + /* Save the number of members that reported in the previous query cycle; + * reset the number of members that have reported in the new query cycle. + */ + + group->lstmbrs = group->members; + group->members = 0; +#endif } break; diff --git a/net/mld/mld_timer.c b/net/mld/mld_timer.c index f7c9f290e9..6aad12f06a 100644 --- a/net/mld/mld_timer.c +++ b/net/mld/mld_timer.c @@ -145,18 +145,40 @@ static void mld_polldog_work(FAR void *arg) else if (IS_MLD_QUERIER(group->flags)) { - /* Schedule (and forget) the general query. */ - - MLD_STATINCR(g_netstats.mld.query_sched); - ret = mld_schedmsg(group, MLD_SEND_GENQUERY); - if (ret < 0) +#ifdef CONFIG_NET_MLD_ROUTER + if (group->njoins == 0 group->members == 0 && group->lstmbrs == 0) { - mlderr("ERROR: Failed to schedule message: %d\n", ret); + /* Cancel the timers and discard any queued Reports. Canceling + * the timer will prevent any new Reports from being sent; + * clearing the flags will discard any pending Reports that + * could interfere with freeing the group. + */ + + wd_cancel(group->polldog); + wd_cancel(group->v1dog); + CLR_MLD_SCHEDMSG(group->flags); + CLR_MLD_WAITMSG(group->flags); + + /* Free the group structure */ + + mld_grpfree(dev, group); } + else +#endif + { + /* Schedule (and forget) the general query. */ - /* Restart the Querier timer */ + MLD_STATINCR(g_netstats.mld.query_sched); + ret = mld_schedmsg(group, MLD_SEND_GENQUERY); + if (ret < 0) + { + mlderr("ERROR: Failed to schedule message: %d\n", ret); + } - mld_start_polltimer(group, MSEC2TICK(MLD_QUERY_MSEC)); + /* Restart the Querier timer */ + + mld_start_polltimer(group, MSEC2TICK(MLD_QUERY_MSEC)); + } } net_unlock(); diff --git a/net/procfs/net_mld.c b/net/procfs/net_mld.c index 9d2a1b5404..aaec339c3b 100644 --- a/net/procfs/net_mld.c +++ b/net/procfs/net_mld.c @@ -123,9 +123,9 @@ static int netprocfs_joinleave(FAR struct netprocfs_file_s *netfile) int len; len = snprintf(netfile->line, NET_LINELEN, "Joins: %04x ", - g_netstats.mld.joins); + g_netstats.mld.njoins); len += snprintf(&netfile->line[len], NET_LINELEN - len, "Leaves: %04x\n", - g_netstats.mld.leaves); + g_netstats.mld.nleaves); return len; }