Squashed commit of the following:
The MLD implementation did not follow the RFC correctly 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 sub-nset since all groups are queried at once. Instead, the design used 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, not a per-group timer. net/mld: Group may be NULL when sending a general query
This commit is contained in:
parent
f14cf966c9
commit
8a3fc26b74
16
TODO
16
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)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -51,7 +51,9 @@
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <stdint.h>
|
||||
#include <queue.h>
|
||||
|
||||
#include <nuttx/wdog.h>
|
||||
#include <nuttx/net/ip.h>
|
||||
#include <nuttx/net/icmpv6.h>
|
||||
|
||||
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -38,9 +38,11 @@
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/wdog.h>
|
||||
#include <nuttx/net/netdev.h>
|
||||
#include <nuttx/net/ip.h>
|
||||
#include <nuttx/net/mld.h>
|
||||
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -46,19 +46,276 @@
|
||||
#include <nuttx/wqueue.h>
|
||||
#include <nuttx/net/netconfig.h>
|
||||
#include <nuttx/net/net.h>
|
||||
#include <nuttx/net/netdev.h>
|
||||
#include <nuttx/net/netstats.h>
|
||||
#include <nuttx/net/mld.h>
|
||||
|
||||
#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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user