diff --git a/TODO b/TODO index 5b130de185..047c193680 100644 --- a/TODO +++ b/TODO @@ -19,7 +19,7 @@ nuttx/: (9) Kernel/Protected Build (3) C++ Support (5) Binary loaders (binfmt/) - (19) Network (net/, drivers/net) + (18) Network (net/, drivers/net) (4) USB (drivers/usbdev, drivers/usbhost) (2) Other drivers (drivers/) (11) Libraries (libs/libc/, libs/libm/) @@ -1489,20 +1489,6 @@ o Network (net/, drivers/net) anything but a well-known point-to-point configuration impossible. - Title: MLD QUERIER DESIGN - Description: The MLD implementation does not follow the RFC correct when it is - the Querier. The Querier should use a general query and get - query messages from all members of all groups. This would be - driven by a single timer per network device since all groups on - the sub-net are queried at once. Instead, the design currently - uses a Multicast Address Specific Query with one timer per group - and ignores groups that we are not members of. - - Similary, the MLDv1 compatibility timer should be a single, - separate timer per network device, not a per-group timer. - Status: Open - Priority: Low. There are no customers of MLD as far as I know. - o USB (drivers/usbdev, drivers/usbhost) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/include/nuttx/net/mld.h b/include/nuttx/net/mld.h index 44bcf15ada..472c3bd914 100644 --- a/include/nuttx/net/mld.h +++ b/include/nuttx/net/mld.h @@ -51,7 +51,9 @@ #include #include +#include +#include #include #include @@ -370,6 +372,22 @@ struct mld_mcast_listen_done_s net_ipv6addr_t mcastaddr; /* Multicast address */ }; +/* This structure represents the overall MLD state for a single network. + * This structure in included withing the net_driver_s structure. + * + * There will be a group for the all systems group address but this + * will not run the state machine as it is used to kick off reports + * from all the other groups + */ + +struct mld_netdev_s +{ + sq_queue_t grplist; /* MLD group list */ + WDOG_ID gendog; /* General query timer */ + WDOG_ID v1dog; /* MLDv1 compatibility timer */ + uint8_t flags; /* See MLD_ flags definitions */ +}; + #ifdef CONFIG_NET_STATISTICS /* MLD statistic counters */ diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h index fc3728daf9..9af9151c36 100644 --- a/include/nuttx/net/netdev.h +++ b/include/nuttx/net/netdev.h @@ -346,7 +346,7 @@ struct net_driver_s sq_queue_t d_igmp_grplist; /* IGMP group list */ #endif #ifdef CONFIG_NET_MLD - sq_queue_t d_mld_grplist; /* MLD group list */ + struct mld_netdev_s d_mld; /* MLD state information */ #endif #ifdef CONFIG_NETDEV_STATISTICS diff --git a/net/mld/mld.h b/net/mld/mld.h index fddd1f6b23..e43f1245ad 100644 --- a/net/mld/mld.h +++ b/net/mld/mld.h @@ -123,35 +123,45 @@ * Pre-processor Definitions ****************************************************************************/ -/* Group flags */ +/* Global flags */ #define MLD_QUERIER (1 << 0) /* Querier */ +#define MLD_V1COMPAT (1 << 1) /* MLDv1 compatibility mode */ +#define MLD_GENPEND (1 << 2) /* General query pending */ + +#define SET_MLD_QUERIER(f) do { (f) |= MLD_QUERIER; } while (0) +#define SET_MLD_V1COMPAT(f) do { (f) |= MLD_V1COMPAT; } while (0) +#define SET_MLD_GENPEND(f) do { (f) |= MLD_GENPEND; } while (0) + +#define CLR_MLD_QUERIER(f) do { (f) &= ~MLD_QUERIER; } while (0) +#define CLR_MLD_V1COMPAT(f) do { (f) &= ~MLD_V1COMPAT; } while (0) +#define CLR_MLD_GENPEND(f) do { (f) &= ~MLD_GENPEND; } while (0) + +#define IS_MLD_QUERIER(f) (((f) & MLD_QUERIER) != 0) +#define IS_MLD_V1COMPAT(f) (((f) & MLD_V1COMPAT) != 0) +#define IS_MLD_GENPEND(f) (((f) & MLD_GENPEND) != 0) + +/* Group flags */ + #define MLD_STARTUP (1 << 1) /* Startup unsolicited Reports */ -#define MLD_V1COMPAT (1 << 2) /* Version 1 compatibility mode */ #define MLD_LASTREPORT (1 << 3) /* We were the last to report */ #define MLD_SCHEDMSG (1 << 4) /* Outgoing message scheduled */ #define MLD_WAITMSG (1 << 5) /* Block until message sent */ #define MLD_RPTPEND (1 << 6) /* Report pending */ -#define SET_MLD_QUERIER(f) do { (f) |= MLD_QUERIER; } while (0) #define SET_MLD_STARTUP(f) do { (f) |= MLD_STARTUP; } while (0) -#define SET_MLD_V1COMPAT(f) do { (f) |= MLD_V1COMPAT; } while (0) #define SET_MLD_LASTREPORT(f) do { (f) |= MLD_LASTREPORT; } while (0) #define SET_MLD_SCHEDMSG(f) do { (f) |= MLD_SCHEDMSG; } while (0) #define SET_MLD_WAITMSG(f) do { (f) |= MLD_WAITMSG; } while (0) #define SET_MLD_RPTPEND(f) do { (f) |= MLD_RPTPEND; } while (0) -#define CLR_MLD_QUERIER(f) do { (f) &= ~MLD_QUERIER; } while (0) #define CLR_MLD_STARTUP(f) do { (f) &= ~MLD_STARTUP; } while (0) -#define CLR_MLD_V1COMPAT(f) do { (f) &= ~MLD_V1COMPAT; } while (0) #define CLR_MLD_LASTREPORT(f) do { (f) &= ~MLD_LASTREPORT; } while (0) #define CLR_MLD_SCHEDMSG(f) do { (f) &= ~MLD_SCHEDMSG; } while (0) #define CLR_MLD_WAITMSG(f) do { (f) &= ~MLD_WAITMSG; } while (0) #define CLR_MLD_RPTPEND(f) do { (f) &= ~MLD_RPTPEND; } while (0) -#define IS_MLD_QUERIER(f) (((f) & MLD_QUERIER) != 0) #define IS_MLD_STARTUP(f) (((f) & MLD_STARTUP) != 0) -#define IS_MLD_V1COMPAT(f) (((f) & MLD_V1COMPAT) != 0) #define IS_MLD_LASTREPORT(f) (((f) & MLD_LASTREPORT) != 0) #define IS_MLD_SCHEDMSG(f) (((f) & MLD_SCHEDMSG) != 0) #define IS_MLD_WAITMSG(f) (((f) & MLD_WAITMSG) != 0) @@ -201,10 +211,6 @@ enum mld_msgtype_e /* This structure represents one group member. There is a list of groups * for each device interface structure. - * - * There will be a group for the all systems group address but this - * will not run the state machine as it is used to kick off reports - * from all the other groups */ typedef FAR struct wdog_s *WDOG_ID; @@ -214,7 +220,6 @@ struct mld_group_s net_ipv6addr_t grpaddr; /* Group IPv6 address */ struct work_s work; /* For deferred timeout operations */ 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) */ @@ -287,7 +292,7 @@ int mld_query(FAR struct net_driver_s *dev, * Name: mld_report_v1 * * Description: - * Called from icmpv6_input() when a Version 1 Multicast Listener Report is + * Called from icmpv6_input() when a MLDv1 Multicast Listener Report is * received. * ****************************************************************************/ @@ -363,6 +368,18 @@ FAR struct mld_group_s *mld_grpallocfind(FAR struct net_driver_s *dev, void mld_grpfree(FAR struct net_driver_s *dev, FAR struct mld_group_s *group); +/**************************************************************************** + * Name: mld_new_pollcycle + * + * Description: + * Update accumulated membership at the beginning of each new poll cycle + * + ****************************************************************************/ + +#ifdef CONFIG_NET_MLD_ROUTER +void mld_new_pollcycle(FAR struct net_driver_s *dev); +#endif + /**************************************************************************** * Name: mld_schedmsg * @@ -431,7 +448,7 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, * ****************************************************************************/ -uint8_t mld_report_msgtype(FAR struct mld_group_s *group); +uint8_t mld_report_msgtype(FAR struct net_driver_s *dev); /**************************************************************************** * Name: mld_joingroup @@ -474,14 +491,14 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec); int mld_leavegroup(FAR const struct ipv6_mreq *mrec); /**************************************************************************** - * Name: mld_start_polltimer + * Name: mld_start_gentimer * * Description: - * Start the MLD poll timer. + * Start/Re-start the general query timer. * ****************************************************************************/ -void mld_start_polltimer(FAR struct mld_group_s *group, clock_t ticks); +void mld_start_gentimer(FAR struct net_driver_s *dev, clock_t ticks); /**************************************************************************** * Name: mld_start_v1timer @@ -491,7 +508,17 @@ void mld_start_polltimer(FAR struct mld_group_s *group, clock_t ticks); * ****************************************************************************/ -void mld_start_v1timer(FAR struct mld_group_s *group, clock_t ticks); +void mld_start_v1timer(FAR struct net_driver_s *dev, clock_t ticks); + +/**************************************************************************** + * Name: mld_start_polltimer + * + * Description: + * Start the MLD poll timer. + * + ****************************************************************************/ + +void mld_start_polltimer(FAR struct mld_group_s *group, clock_t ticks); /**************************************************************************** * Name: mld_addmcastmac diff --git a/net/mld/mld_group.c b/net/mld/mld_group.c index 5d2d319cd8..80fce37b8e 100644 --- a/net/mld/mld_group.c +++ b/net/mld/mld_group.c @@ -109,31 +109,28 @@ FAR struct mld_group_s *mld_grpalloc(FAR struct net_driver_s *dev, goto errout_with_sem; } - group->v1dog = wd_create(); - DEBUGASSERT(group->v1dog != NULL); - if (group->v1dog == NULL) - { - goto errout_with_polldog; - } - /* Save the interface index */ group->ifindex = dev->d_ifindex; - /* All routers start up as a Querier on each of their attached links. */ +#ifndef CONFIG_CONFIG_NET_MLD_ROUTER + /* Start the query timer if we are the Querier and this is the first + * group member of the group. + */ - SET_MLD_QUERIER(group->flags); + if (dev->d_mld.grplist.head == NULL) + { + mld_start_gentimer(dev, MSEC2TICK(MLD_QUERY_MSEC)); + } +#endif /* Add the group structure to the list in the device structure */ - sq_addfirst((FAR sq_entry_t *)group, &dev->d_mld_grplist); + sq_addfirst((FAR sq_entry_t *)group, &dev->d_mld.grplist); } return group; -errout_with_polldog: - wd_delete(group->polldog); - errout_with_sem: (void)nxsem_destroy(&group->sem); kmm_free(group); @@ -160,7 +157,7 @@ FAR struct mld_group_s *mld_grpfind(FAR struct net_driver_s *dev, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7]); - for (group = (FAR struct mld_group_s *)dev->d_mld_grplist.head; + for (group = (FAR struct mld_group_s *)dev->d_mld.grplist.head; group; group = group->next) { @@ -222,11 +219,10 @@ void mld_grpfree(FAR struct net_driver_s *dev, FAR struct mld_group_s *group) /* Cancel the timers */ wd_cancel(group->polldog); - wd_cancel(group->v1dog); /* Remove the group structure from the group list in the device structure */ - sq_rem((FAR sq_entry_t *)group, &dev->d_mld_grplist); + sq_rem((FAR sq_entry_t *)group, &dev->d_mld.grplist); /* Destroy the wait semaphore */ @@ -235,12 +231,53 @@ void mld_grpfree(FAR struct net_driver_s *dev, FAR struct mld_group_s *group) /* Destroy the timers */ wd_delete(group->polldog); - wd_delete(group->v1dog); /* Then release the group structure resources. */ mldinfo("Call sched_kfree()\n"); kmm_free(group); + +#ifndef CONFIG_CONFIG_NET_MLD_ROUTER + /* If there are no longer any groups, then stop the general query and v1 + * compatibility timers. + */ + + if (dev->d_mld.grplist.head == NULL) + { + wd_cancel(dev->d_mld.gendog); + wd_cancel(dev->d_mld.v1dog); + } +#endif } +/**************************************************************************** + * Name: mld_new_pollcycle + * + * Description: + * Update accumulated membership at the beginning of each new poll cycle + * + ****************************************************************************/ + +#ifdef CONFIG_NET_MLD_ROUTER +void mld_new_pollcycle(FAR struct net_driver_s *dev) +{ + FAR struct mld_group_s *member; + + /* Update member ship in every group */ + + for (member = (FAR struct mld_group_s *)dev->d_mld.grplist.head; + member; + member = member->next) + { + /* 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 + #endif /* CONFIG_NET_MLD */ diff --git a/net/mld/mld_initialize.c b/net/mld/mld_initialize.c index d850e0450c..912bc4f7f0 100644 --- a/net/mld/mld_initialize.c +++ b/net/mld/mld_initialize.c @@ -38,9 +38,11 @@ #include +#include #include #include +#include #include #include #include @@ -77,7 +79,18 @@ void mld_initialize(void) void mld_devinit(struct net_driver_s *dev) { mldinfo("MLD initializing dev %p\n", dev); - DEBUGASSERT(dev->d_mld_grplist.head == NULL); + + /* Initialize the MLD state in the device structure */ + + memset(&dev->d_mld, 0, sizeof(struct mld_netdev_s)); + + dev->d_mld.gendog = wd_create(); + dev->d_mld.v1dog = wd_create(); + DEBUGASSERT(dev->d_mld.gendog != NULL && dev->d_mld.v1dog != NULL); + + /* All routers start up as a Querier on each of their attached links. */ + + SET_MLD_QUERIER(dev->d_mld.flags); /* Add the all nodes address to the group */ @@ -88,4 +101,10 @@ void mld_devinit(struct net_driver_s *dev) mld_addmcastmac(dev, g_ipv6_allnodes); mld_addmcastmac(dev, g_ipv6_allrouters); mld_addmcastmac(dev, g_ipv6_allmldv2routers); + +#ifdef CONFIG_CONFIG_NET_MLD_ROUTER + /* Start the general query timer. */ + + mld_start_gentimer(dev, MSEC2TICK(MLD_QUERY_MSEC)); +#endif } diff --git a/net/mld/mld_leave.c b/net/mld/mld_leave.c index f1c41a519f..3894d010ca 100644 --- a/net/mld/mld_leave.c +++ b/net/mld/mld_leave.c @@ -193,7 +193,6 @@ int mld_leavegroup(FAR const struct ipv6_mreq *mrec) */ wd_cancel(group->polldog); - wd_cancel(group->v1dog); CLR_MLD_SCHEDMSG(group->flags); CLR_MLD_WAITMSG(group->flags); diff --git a/net/mld/mld_poll.c b/net/mld/mld_poll.c index 32e8a753be..e9caf7d2ca 100644 --- a/net/mld/mld_poll.c +++ b/net/mld/mld_poll.c @@ -78,9 +78,28 @@ void mld_poll(FAR struct net_driver_s *dev) dev->d_len = 0; dev->d_sndlen = 0; + /* Check if a general query is pending */ + + if (IS_MLD_GENPEND(dev->d_mld.flags)) + { + /* Clear the pending flag */ + + CLR_MLD_GENPEND(dev->d_mld.flags); + + /* Are we still the querier? */ + + if (IS_MLD_QUERIER(dev->d_mld.flags)) + { + /* Yes, send the general query and return */ + + mld_send(dev, NULL, MLD_SEND_GENQUERY); + return; + } + } + /* Check each member of the group */ - for (group = (FAR struct mld_group_s *)dev->d_mld_grplist.head; + for (group = (FAR struct mld_group_s *)dev->d_mld.grplist.head; group; group = group->next) { @@ -120,7 +139,7 @@ void mld_poll(FAR struct net_driver_s *dev) { /* Yes.. create the MLD message in the driver buffer */ - mld_send(dev, group, mld_report_msgtype(group)); + mld_send(dev, group, mld_report_msgtype(dev)); /* Indicate that the report is no longer pending */ diff --git a/net/mld/mld_query.c b/net/mld/mld_query.c index 91db845e1b..ab5817c7c0 100644 --- a/net/mld/mld_query.c +++ b/net/mld/mld_query.c @@ -65,7 +65,7 @@ ****************************************************************************/ /**************************************************************************** - * Name: mld_setup_v1compat + * Name: mld_check_v1compat * * Description: * If this is for MLDv1 query, then select MLDv1 compatibility mode and @@ -74,21 +74,11 @@ * ****************************************************************************/ -static void mld_setup_v1compat(FAR struct mld_group_s *group, - FAR const struct mld_mcast_listen_query_s *query, - bool mldv1) +static inline void mld_check_v1compat(FAR struct net_driver_s *dev, + bool mldv1) { - unsigned int respmsec; - if (mldv1) { -#if 0 /* REVISIT */ - /* Get the QQI from the query. Since this is MLDv1, we know that - * the value is not encoded. - */ - - respmsec = MSEC_PER_SEC * MLD_QQI_VALUE(query->qqic); -#else /* REVISIT: I am confused. Per RFC 3810: * "The Older Version Querier Present Timeout is the time-out for * transitioning a host back to MLDv2 Host Compatibility Mode. When @@ -103,34 +93,20 @@ static void mld_setup_v1compat(FAR struct mld_group_s *group, * field. That is an MLDv2 extension. */ - respmsec = MLD_QUERY_MSEC; -#endif - /* Select MLDv1 compatibility mode (might already be selected) */ - SET_MLD_V1COMPAT(group->flags); + SET_MLD_V1COMPAT(dev->d_mld.flags); - /* Whenever a host changes its compatibility mode, it cancels all its - * pending responses and retransmission timers. + /* REVISIT: Whenever a host changes its compatibility mode, it cancels + * all its pending responses and retransmission timers. Logic Missing. */ - wd_cancel(group->polldog); - - /* REVISIT: We cannot cancel a pending message if there is a waiter. - * Some additional logic would be required to avoid a hang. - */ - - if (!IS_MLD_WAITMSG(group->flags)) - { - CLR_MLD_SCHEDMSG(group->flags); - } - /* And start the MLDv1 compatibility timer. If the timer is already * running, this will reset the timer. */ - mld_start_v1timer(group, - MSEC2TICK(MLD_V1PRESENT_MSEC((clock_t)respmsec))); + mld_start_v1timer(dev, + MSEC2TICK(MLD_V1PRESENT_MSEC((clock_t)MLD_QUERY_MSEC))); } } @@ -197,21 +173,19 @@ static bool mld_cmpaddr(FAR struct net_driver_s *dev, * Name: mld_check_querier * * Description: - * Check if we are still the querier for this group (assuming that we are - * currently the querier). This compares the IPv6 Source Address of the - * query against and the IPv6 address of the link. Ff the source address - * is numerically less than the link address, when we are no longer the - * querier. + * Check if we are still the querier (assuming that we are currently the + * querier). This compares the IPv6 Source Address of the query against + * the IPv6 address of the link. If the source address is numerically + * less than the link address, when we are no longer the querier. * ****************************************************************************/ static void mld_check_querier(FAR struct net_driver_s *dev, - FAR struct ipv6_hdr_s *ipv6, - FAR struct mld_group_s *group) + FAR struct ipv6_hdr_s *ipv6) { /* Check if this member is a Querier */ - if (IS_MLD_QUERIER(group->flags)) + if (IS_MLD_QUERIER(dev->d_mld.flags)) { /* This is a querier, check if the IPv6 source address is numerically * less than the IPv6 address assigned to this link. @@ -221,21 +195,17 @@ static void mld_check_querier(FAR struct net_driver_s *dev, { /* Switch to non-Querier mode */ - CLR_MLD_QUERIER(group->flags); + CLR_MLD_QUERIER(dev->d_mld.flags); } } - /* Check if the member is a Non-Querier AND that we are past the start up - * phase (where the timer is used for a different purpose)? - */ + /* Check if the member is a Non-Querier. */ - if (!IS_MLD_QUERIER(group->flags) && !IS_MLD_STARTUP(group->flags)) + if (!IS_MLD_QUERIER(dev->d_mld.flags)) { - /* Yes.. cancel the poll timer and [re-]start the 'Other Querier - * Present' Timeout. - */ + /* Yes.. [re-]start the 'Other Querier Present' Timeout. */ - mld_start_polltimer(group, MSEC2TICK(MLD_OQUERY_MSEC)); + mld_start_gentimer(dev, MSEC2TICK(MLD_OQUERY_MSEC)); } } @@ -307,6 +277,13 @@ int mld_query(FAR struct net_driver_s *dev, return -EINVAL; } + /* Warn if we received a MLDv2 query in MLDv1 compatibility mode. */ + + if (!mldv1 && IS_MLD_V1COMPAT(dev->d_mld.flags)) + { + mldwarn("WARNING: MLDv2 query received in MLDv1 compatibility mode\n"); + } + /* There are three variants of the Query message (RFC 3810): * * 1. A "General Query" is sent by the Querier to learn which @@ -352,64 +329,37 @@ int mld_query(FAR struct net_driver_s *dev, mldinfo("General multicast query\n"); MLD_STATINCR(g_netstats.mld.gm_query_received); - /* Two passes through the member list. On the first, just check if we - * are still the querier for the qroup. - */ + /* Check if we are still the querier for this sub-net */ - for (member = (FAR struct mld_group_s *)dev->d_mld_grplist.head; - member; - member = member->next) - { - /* Skip over the all systems group entry */ - - if (!net_ipv6addr_cmp(member->grpaddr, g_ipv6_allnodes)) - { - /* Check if we are still the querier for this group */ - - mld_check_querier(dev, ipv6, member); - - /* Warn if we received a MLDv2 query in MLDv1 compatibility - * mode. - */ - - if (!mldv1 && IS_MLD_V1COMPAT(member->flags)) - { - mldinfo("WARNING: MLDv2 query received in MLDv1 " - "compatibility mode\n"); - } + mld_check_querier(dev, ipv6); #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 - } - } - - /* On the second time through, we send the Report in response to the - * query. This has to be done twice because because there is only - * a single packet buffer that is used for both incoming and outgoing - * packets. When the report is sent, it will clobber the incoming - * query. Any attempt to send an additional Report would also clobber - * a preceding report + /* Update accumulated membership at the beginning of each new poll + * cycle */ - for (member = (FAR struct mld_group_s *)dev->d_mld_grplist.head; - member; + mld_new_pollcycle(dev) +#endif + + /* Check MLDv1 compatibility mode */ + + mld_check_v1compat(dev, mldv1); + + /* Send the Report in response to the query. This has to be done + * multiple times because because there is only a single packet buffer + * that is used for both incoming and outgoing packets. When the + * report is sent, it will clobber the incoming* query. Any attempt + * to send an additional Report would also clobber a preceding report + */ + + for (member = (FAR struct mld_group_s *)dev->d_mld.grplist.head; + member != NULL; member = member->next) { /* Skip over the all systems group entry */ if (!net_ipv6addr_cmp(member->grpaddr, g_ipv6_allnodes)) { - /* Check MLDv1 compatibility mode */ - - mld_setup_v1compat(member, query, mldv1); - /* Have we already sent a report from this loop? */ if (rptsent) @@ -424,7 +374,7 @@ int mld_query(FAR struct net_driver_s *dev, { /* No.. Send one report now. */ - mld_send(dev, member, mld_report_msgtype(member)); + mld_send(dev, member, mld_report_msgtype(dev)); rptsent = true; CLR_MLD_RPTPEND(member->flags); } @@ -458,7 +408,7 @@ int mld_query(FAR struct net_driver_s *dev, /* Check if we are still the querier for this group */ - mld_check_querier(dev, ipv6, group); + mld_check_querier(dev, ipv6); #ifdef CONFIG_NET_MLD_ROUTER /* Save the number of members that reported in the previous query cycle; @@ -471,7 +421,7 @@ int mld_query(FAR struct net_driver_s *dev, /* Warn if we received a MLDv2 query in MLDv1 compatibility mode. */ - if (!mldv1 && IS_MLD_V1COMPAT(group->flags)) + if (!mldv1 && IS_MLD_V1COMPAT(dev->d_mld.flags)) { mldinfo("WARNING: MLDv2 query received in MLDv1 compatibility mode\n"); } @@ -496,11 +446,12 @@ int mld_query(FAR struct net_driver_s *dev, /* Check MLDv1 compatibility mode */ - mld_setup_v1compat(group, query, mldv1); + mld_check_v1compat(dev, mldv1); /* Send the report */ - mld_send(dev, group, mld_report_msgtype(group)); + mld_send(dev, group, mld_report_msgtype(dev)); + CLR_MLD_RPTPEND(group->flags); } /* Not sent to all systems. Check for Unicast General Query */ @@ -512,11 +463,12 @@ int mld_query(FAR struct net_driver_s *dev, /* Check MLDv1 compatibility mode */ - mld_setup_v1compat(group, query, mldv1); + mld_check_v1compat(dev, mldv1); /* Send the report */ - mld_send(dev, group, mld_report_msgtype(group)); + mld_send(dev, group, mld_report_msgtype(dev)); + CLR_MLD_RPTPEND(group->flags); } else { diff --git a/net/mld/mld_report.c b/net/mld/mld_report.c index 67004cc732..8498b86b76 100644 --- a/net/mld/mld_report.c +++ b/net/mld/mld_report.c @@ -157,7 +157,7 @@ int mld_report(FAR struct net_driver_s *dev, FAR const net_ipv6addr_t grpaddr) int mld_report_v1(FAR struct net_driver_s *dev, FAR const struct mld_mcast_listen_report_v1_s *report) { - mldinfo("Version 1 Multicast Listener Report\n"); + mldinfo("MLDv1 Multicast Listener Report\n"); DEBUGASSERT(dev != NULL && report != NULL); MLD_STATINCR(g_netstats.mld.v1report_received); diff --git a/net/mld/mld_send.c b/net/mld/mld_send.c index d0eef7ae2a..12a210b362 100644 --- a/net/mld/mld_send.c +++ b/net/mld/mld_send.c @@ -120,6 +120,11 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, FAR const uint16_t *destipaddr; unsigned int mldsize; + /* Only a general query message can have a NULL group */ + + DEBUGASSERT(dev != NULL); + DEBUGASSERT(msgtype == MLD_SEND_GENQUERY || group != NULL); + /* Select IPv6 */ IFF_SET_IPv6(dev->d_flags); @@ -131,7 +136,8 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, case MLD_SEND_GENQUERY: /* Send General Query */ case MLD_SEND_MASQUERY: /* Send Multicast Address Specific (MAS) Query */ { - mldinfo("Send General Query, flags=%02x\n", group->flags); + mldinfo("Send General/MAS Query, flags=%02x\n", + group != NULL ? group->flags : dev->d_mld.flags); mldsize = SIZEOF_MLD_MCAST_LISTEN_QUERY_S(0); } break; @@ -290,7 +296,7 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, /* Fields unique to the extended MLDv2 query */ - if (!IS_MLD_V1COMPAT(group->flags)) + if (!IS_MLD_V1COMPAT(dev->d_mld.flags)) { query->flags = MLD_ROBUSTNESS; query->qqic = MLD_QRESP_SEC; @@ -307,13 +313,21 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, /* 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. - * - * REVISIT: This would have to be done for all groups, not just - * this one. */ - group->lstmbrs = group->members; - group->members = 0; + if (msgtype == MLD_SEND_GENQUERY) + { + /* Update accumulated membership for all groups. */ + + mld_new_pollcycle(dev) + } + else + { + /* Updated accumulated membership only for this group */ + + group->lstmbrs = group->members; + group->members = 0; + } #endif } break; @@ -404,9 +418,9 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, * ****************************************************************************/ -uint8_t mld_report_msgtype(FAR struct mld_group_s *group) +uint8_t mld_report_msgtype(FAR struct net_driver_s *dev) { - if (IS_MLD_V1COMPAT(group->flags)) + if (IS_MLD_V1COMPAT(dev->d_mld.flags)) { return MLD_SEND_V1REPORT; } diff --git a/net/mld/mld_timer.c b/net/mld/mld_timer.c index 335688293d..5b97c3ca0d 100644 --- a/net/mld/mld_timer.c +++ b/net/mld/mld_timer.c @@ -46,19 +46,276 @@ #include #include #include +#include #include #include #include "devif/devif.h" +#include "netdev/netdev.h" #include "mld/mld.h" #include "utils/utils.h" #ifdef CONFIG_NET_MLD +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MLD_FREE_WORK 4 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is a small pool of work structures that are used for rare, general + * timer and MLDv1 compatibility timers. + * + * REVISIT: A better place would be in the device structure. But then we + * would either have to pass a device structure in the timeout (which could + * become stale) or look up the device using an interface index. But we + * cannot access the network device list from the timer interrupt handler. + */ + +static struct work_s g_mld_work[MLD_FREE_WORK]; + /**************************************************************************** * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: mld_find_work + * + * Description: + * Fin an available work queue structure. + * + * Assumptions: + * Called only from the timer interrupt handler with interrupts disabled. + * + ****************************************************************************/ + +static FAR struct work_s *mld_find_work(void) +{ + int i; + + for (i = 0; i < MLD_FREE_WORK; i++) + { + if (work_available(&g_mld_work[i])) + { + return &g_mld_work[i]; + } + } + + return NULL; +} + +/**************************************************************************** + * Name: mld_gendog_work + * + * Description: + * General query watchdog timeout work. + * + * Assumptions: + * This function is called from a work queue thread. + * + ****************************************************************************/ + +static void mld_gendog_work(FAR void *arg) +{ + FAR struct net_driver_s *dev; + int ifindex; + + /* Recover the interface index and find the device associated with the + * index. + */ + + ifindex = (int)arg; + DEBUGASSERT(ifindex > 0); + + net_lock(); + dev = netdev_findbyindex(ifindex); + if (dev == NULL) + { + /* This could be a normal consequence if the device is unregistered + * while the timer is pending. + */ + + fwarn("WARNING: No device associated with ifindex=%d\n", ifindex); + net_unlock(); + return; + } + + /* Check for an Other Querier Present Timeout. This timer is set in non- + * Querier mode to detect the case where we have lost the Querier. + */ + + if (!IS_MLD_QUERIER(dev->d_mld.flags)) + { + /* We are not the Querier. This is an Other Querier Present Timeout. + * If this timeout expires, it means that there are no Queriers for + * the group. Let's revert to Querier mode. + */ + + SET_MLD_QUERIER(dev->d_mld.flags); + } + + /* Check if this we are the Querier */ + + if (IS_MLD_QUERIER(dev->d_mld.flags)) + { + /* Schedule (and forget) the general query. */ + + MLD_STATINCR(g_netstats.mld.query_sched); + SET_MLD_GENPEND(dev->d_mld.flags); + + /* Notify the device that we have a packet to send */ + + netdev_txnotify_dev(dev); + + /* Restart the Querier timer */ + + mld_start_gentimer(dev, MSEC2TICK(MLD_QUERY_MSEC)); + } + else + { + /* Not the Querier... Restart the Other Querier Present Timeout */ + + mld_start_gentimer(dev, MSEC2TICK(MLD_OQUERY_MSEC)); + } + + net_unlock(); +} + +/**************************************************************************** + * Name: mld_gendog_timout + * + * Description: + * General query watchdog handler. + * + * Assumptions: + * This function is called from the context of the timer interrupt handler. + * + ****************************************************************************/ + +static void mld_gendog_timout(int argc, uint32_t arg, ...) +{ + FAR struct work_s *work; + int ret; + + mldinfo("Timeout!\n"); + DEBUGASSERT(arg != 0); + + /* Find an available work structure. This is awkward. */ + + work = mld_find_work(); + DEBUGASSERT(work != NULL); + + if (work == NULL) + { + mlderr("ERROR: No free work structures.\n"); + } + else + { + /* Perform the general query-related operations on (preferably) the + * low priority work queue. + */ + + ret = work_queue(LPWORK, work, mld_gendog_work, (FAR void *)arg, 0); + if (ret < 0) + { + mlderr("ERROR: Failed to queue general query work: %d\n", ret); + } + } +} + +/**************************************************************************** + * Name: mld_v1dog_work + * + * Description: + * Timeout watchdog work + * + * Assumptions: + * This function is called from a work queue thread. + * + ****************************************************************************/ + +static void mld_v1dog_work(FAR void *arg) +{ + FAR struct net_driver_s *dev; + int ifindex; + + /* Recover the interface index and find the device associated with the + * index. + */ + + ifindex = (int)arg; + DEBUGASSERT(ifindex > 0); + + net_lock(); + dev = netdev_findbyindex(ifindex); + if (dev == NULL) + { + /* This could be a normal consequence if the device is unregistered + * while the timer is pending. + */ + + fwarn("WARNING: No device associated with ifindex=%d\n", ifindex); + } + else + { + /* Exit MLDv1 compatibility mode. */ + + CLR_MLD_V1COMPAT(dev->d_mld.flags); + + /* REVIST: Whenever a host changes its compatibility mode, it cancels + * all of its pending responses and retransmission timers. + */ + } + + net_unlock(); +} + +/**************************************************************************** + * Name: mld_v1dog_timout + * + * Description: + * Timeout watchdog handler. + * + * Assumptions: + * This function is called from the context of the timer interrupt handler. + * + ****************************************************************************/ + +static void mld_v1dog_timout(int argc, uint32_t arg, ...) +{ + FAR struct work_s *work; + int ret; + + mldinfo("Timeout!\n"); + DEBUGASSERT(arg != 0); + + /* Find an available work structure. This is awkward. */ + + work = mld_find_work(); + DEBUGASSERT(work != NULL); + + if (work == NULL) + { + mlderr("ERROR: No free work structures.\n"); + } + else + { + /* Perform the general query-related operations on (preferably) the + * low priority work queue. + */ + + ret = work_queue(LPWORK, work, mld_v1dog_work, (FAR void *)arg, 0); + if (ret < 0) + { + mlderr("ERROR: Failed to queue MLDv1 timeout work: %d\n", ret); + } + } +} + /**************************************************************************** * Name: mld_polldog_work * @@ -73,6 +330,7 @@ static void mld_polldog_work(FAR void *arg) { FAR struct mld_group_s *group; + FAR struct net_driver_s *dev; int ret; /* Recover the reference to the group */ @@ -85,10 +343,25 @@ static void mld_polldog_work(FAR void *arg) net_lock(); if (IS_MLD_STARTUP(group->flags)) { + MLD_STATINCR(g_netstats.mld.report_sched); + + /* Get a reference to the device serving the sub-net */ + + dev = netdev_findbyindex(group->ifindex); + if (dev == NULL) + { + /* This could be a normal consequence if the device is + * unregistered while the timer is pending. + */ + + fwarn("WARNING: No device associated with ifindex=%d\n", + group->ifindex); + return; + } + /* Schedule (and forget) the Report. */ - MLD_STATINCR(g_netstats.mld.report_sched); - ret = mld_schedmsg(group, mld_report_msgtype(group)); + ret = mld_schedmsg(group, mld_report_msgtype(dev)); if (ret < 0) { mlderr("ERROR: Failed to schedule message: %d\n", ret); @@ -110,93 +383,7 @@ static void mld_polldog_work(FAR void *arg) /* Terminate the start-up sequence */ CLR_MLD_STARTUP(group->flags); - - /* If in Querier mode, start the Querier timer */ - - if (IS_MLD_QUERIER(group->flags)) - { - mld_start_polltimer(group, MSEC2TICK(MLD_QUERY_MSEC)); - } } - - net_unlock(); - return; - } - -#ifdef CONFIG_NET_MLD_ROUTER - /* This is a Query-related timeout. Destroy the group if there are - * no members of the group detected in the last two Query cycles. - */ - - if (group->njoins == 0 group->members == 0 && group->lstmbrs == 0) - { - /* Cancel the timers and discard any queued Reports. Canceling the - * timer will prevent any new Reports from being sent; clearing the - * 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); - net_unlock(); - return; - } -#endif - - /* Check for an Other Querier Present Timeout. This timer is set in non- - * Querier mode to detect the case where we have lost the Querier. - */ - - if (!IS_MLD_QUERIER(group->flags)) - { - /* We are not the Querier. This is an Other Querier Present Timeout. - * If this timeout expires, it means that there are no Queriers for - * the group. Let's revert to Querier mode. - */ - - SET_MLD_QUERIER(group->flags); - } - - /* Check if this is Querier */ - - if (IS_MLD_QUERIER(group->flags)) - { - /* Schedule (and forget) the general query or MAS query. */ - - MLD_STATINCR(g_netstats.mld.query_sched); - -#ifdef CONFIG_NET_MLD_ROUTER - /* REVISIT: In order to support the RFC correctly, we would need - * separate, single general query timer that is not part of the - * group structure. The Querier should query across all groups, - * with a single query, not very via multiple MAS queries as is - * done here. - */ - - ret = mld_schedmsg(group, MLD_SEND_GENQUERY); -#else - ret = mld_schedmsg(group, MLD_SEND_MASQUERY); -#endif - if (ret < 0) - { - mlderr("ERROR: Failed to schedule message: %d\n", ret); - } - - /* Restart the Querier timer */ - - mld_start_polltimer(group, MSEC2TICK(MLD_QUERY_MSEC)); - } - else - { - /* Not the Querier... Restart the Other Querier Present Timeout */ - - mld_start_polltimer(group, MSEC2TICK(MLD_OQUERY_MSEC)); } net_unlock(); @@ -209,8 +396,7 @@ static void mld_polldog_work(FAR void *arg) * Timeout watchdog handler. * * Assumptions: - * This function is called from the polldog timer handler which runs in the - * context of the timer interrupt handler. + * This function is called from the context of the timer interrupt handler. * ****************************************************************************/ @@ -237,95 +423,58 @@ static void mld_polldog_timout(int argc, uint32_t arg, ...) } } -/**************************************************************************** - * Name: mld_v1dog_work - * - * Description: - * Timeout watchdog work - * - * Assumptions: - * This function is called from a work queue thread. - * - ****************************************************************************/ - -static void mld_v1dog_work(FAR void *arg) -{ - FAR struct mld_group_s *group; - - /* Recover the reference to the group */ - - group = (FAR struct mld_group_s *)arg; - DEBUGASSERT(group != NULL); - - net_lock(); - if (IS_MLD_V1COMPAT(group->flags)) - { - /* Exit MLDv1 compatibility mode. */ - - CLR_MLD_V1COMPAT(group->flags); - - /* Whenever a host changes its compatibility mode, it cancels all its - * pending responses and retransmission timers. - */ - - wd_cancel(group->polldog); - - /* REVISIT: We cannot cancel a pending message if there is a waiter. - * Some additional logic would be required to avoid a hang. - */ - - if (!IS_MLD_WAITMSG(group->flags)) - { - CLR_MLD_SCHEDMSG(group->flags); - } - } - - net_unlock(); -} - -/**************************************************************************** - * Name: mld_v1dog_timout - * - * Description: - * Timeout watchdog handler. - * - * Assumptions: - * This function is called from the v1dog timer handler which runs in the - * context of the timer interrupt handler. - * - ****************************************************************************/ - -static void mld_v1dog_timout(int argc, uint32_t arg, ...) -{ - FAR struct mld_group_s *group; - int ret; - - mldinfo("Timeout!\n"); - - /* Recover the reference to the group */ - - group = (FAR struct mld_group_s *)arg; - DEBUGASSERT(argc == 1 && group != NULL); - - /* Cancels all its pending responses and retransmission timers */ - - wd_cancel(group->polldog); - - /* Perform the timeout-related operations on (preferably) the low priority - * work queue. - */ - - ret = work_queue(LPWORK, &group->work, mld_v1dog_work, group, 0); - if (ret < 0) - { - mlderr("ERROR: Failed to queue timeout work: %d\n", ret); - } -} - /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: mld_start_gentimer + * + * Description: + * Start/Re-start the general query timer. + * + ****************************************************************************/ + +void mld_start_gentimer(FAR struct net_driver_s *dev, clock_t ticks) +{ + int ret; + + /* Start the timer */ + + mldinfo("ticks: %lu\n", (unsigned long)ticks); + + ret = wd_start(dev->d_mld.gendog, ticks, mld_gendog_timout, 1, + dev->d_ifindex); + + DEBUGASSERT(ret == OK); + UNUSED(ret); +} + +/**************************************************************************** + * Name: mld_start_v1timer + * + * Description: + * Start the MLDv1 compatibility timer. + * + * REVISIT: This should be a single global timer, not a per-group timer. + * + ****************************************************************************/ + +void mld_start_v1timer(FAR struct net_driver_s *dev, clock_t ticks) +{ + int ret; + + /* Start the timer */ + + mldinfo("ticks: %lu\n", (unsigned long)ticks); + + ret = wd_start(dev->d_mld.v1dog, ticks, mld_v1dog_timout, 1, + dev->d_ifindex); + + DEBUGASSERT(ret == OK); + UNUSED(ret); +} + /**************************************************************************** * Name: mld_start_polltimer * @@ -348,28 +497,4 @@ void mld_start_polltimer(FAR struct mld_group_s *group, clock_t ticks) UNUSED(ret); } -/**************************************************************************** - * Name: mld_start_v1timer - * - * Description: - * Start the MLDv1 compatibility timer. - * - * REVISIT: This should be a single global timer, not a per-group timer. - * - ****************************************************************************/ - -void mld_start_v1timer(FAR struct mld_group_s *group, clock_t ticks) -{ - int ret; - - /* Start the timer */ - - mldinfo("ticks: %lu\n", (unsigned long)ticks); - - ret = wd_start(group->v1dog, ticks, mld_v1dog_timout, 1, (uint32_t)group); - - DEBUGASSERT(ret == OK); - UNUSED(ret); -} - #endif /* CONFIG_NET_MLD */