net/mld: Implement MLDv1 compatibility mode. This completes coding of MLD. MLD is, howeer, still completely untested.

This commit is contained in:
Gregory Nutt 2018-11-04 14:07:27 -06:00
parent b1a61834d9
commit 9a75a14800
12 changed files with 484 additions and 151 deletions

View File

@ -271,12 +271,17 @@
struct mld_mcast_listen_query_s
{
/* The initial fields are common for MLDv1 and MLDv2 (24-bytes) */
uint8_t type; /* Message Type: ICMPV6_MCAST_LISTEN_QUERY */
uint8_t reserved1; /* Reserved, must be zero on transmission */
uint16_t chksum; /* Checksum of ICMP header and data */
uint16_t mrc; /* Maximum response code */
uint16_t reserved2; /* Reserved, must be zero on transmission */
net_ipv6addr_t grpaddr; /* 128-bit IPv6 multicast group address */
/* These fields apply only to the MLDv2 query */
uint8_t flags; /* See S and QRV flag definitions */
uint8_t qqic; /* Querier's Query Interval Code */
uint16_t nsources; /* Number of sources that follow */
@ -329,7 +334,7 @@ struct mld_mcast_addrec_v2_s
#define SIZEOF_MLD_MCAST_ADDREC_V2_S(nsources, auxdatlen) \
(sizeof(struct mld_mcast_addrec_v2_s) + \
sizeof(net_ipv6addr_t) * ((nsources) - 1) + \
(auxdatlen)
(auxdatlen))
struct mld_mcast_listen_report_v2_s
{
@ -348,10 +353,10 @@ struct mld_mcast_listen_report_v2_s
* size of each variable length address record (addreclen).
*/
#define SIZEOF_MLD_MCAST_ADDREC_V2_S(addreclen) \
#define SIZEOF_MLD_MCAST_LISTEN_REPORT_V2_S(addreclen) \
(sizeof(struct mld_mcast_addrec_v2_s) - \
sizeof(net_ipv6addr_t) + \
(addreclen)
(addreclen))
/* Version 1 Multicast Listener Done (RFC 2710) */

View File

@ -91,18 +91,20 @@ EXTERN uint16_t g_ipid;
/* Well-known IPv6 addresses */
#ifdef CONFIG_NET_IPv6
EXTERN const net_ipv6addr_t g_ipv6_unspecaddr; /* An address of all zeroes */
EXTERN const net_ipv6addr_t g_ipv6_allnodes; /* All link local nodes */
EXTERN const net_ipv6addr_t g_ipv6_unspecaddr; /* An address of all zeroes */
EXTERN const net_ipv6addr_t g_ipv6_allnodes; /* All link local nodes */
#if defined(CONFIG_NET_ICMPv6_AUTOCONF) || defined(CONFIG_NET_ICMPv6_ROUTER) || \
defined(CONFIG_NET_MLD)
/* IPv6 Multi-cast IP addresses. See RFC 2375 */
EXTERN const net_ipv6addr_t g_ipv6_allrouters; /* All link local routers */
EXTERN const net_ipv6addr_t g_ipv6_allrouters; /* All link local routers */
#ifdef CONFIG_NET_ICMPv6_AUTOCONF
EXTERN const net_ipv6addr_t g_ipv6_llnetmask; /* Netmask for local link address */
EXTERN const net_ipv6addr_t g_ipv6_llnetmask; /* Netmask for local link address */
#endif
#ifdef CONFIG_NET_MLD
EXTERN const net_ipv6addr_t g_ipv6_allmldv2routers; /* All MLDv2 link local routers */
#endif
#ifdef CONFIG_NET_ETHERNET
/* IPv6 Multi-cast Ethernet addresses. Formed from the 16-bit prefix:
*
@ -111,8 +113,8 @@ EXTERN const net_ipv6addr_t g_ipv6_llnetmask; /* Netmask for local link addres
* and the last 32-bits of the IPv6 IP address
*/
EXTERN const struct ether_addr g_ipv6_ethallnodes; /* All link local nodes */
EXTERN const struct ether_addr g_ipv6_ethallrouters; /* All link local routers */
EXTERN const struct ether_addr g_ipv6_ethallnodes; /* All link local nodes */
EXTERN const struct ether_addr g_ipv6_ethallrouters; /* All link local routers */
#endif /* CONFIG_NET_ETHERNET */
#endif /* CONFIG_NET_ICMPv6_AUTOCONF || CONFIG_NET_ICMPv6_ROUTER || CONFIG_NET_MLD */

View File

@ -98,6 +98,20 @@ const net_ipv6addr_t g_ipv6_llnetmask = /* Netmask for local link address */
};
#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
#ifdef CONFIG_NET_MLD
/* Version 2 Multicast Listener Reports are sent with an IP destination
* address of FF02:0:0:0:0:0:0:16, to which all MLDv2-capable multicast
* routers listen (RFC 3810)
*/
const net_ipv6addr_t g_ipv6_allmldv2routers = /* All MLDv2 link local routers */
{
HTONS(0xff02),
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
HTONS(0x0016)
};
#endif
#ifdef CONFIG_NET_ETHERNET
/* IPv6 Multi-cast Ethernet addresses. Formed from the 16-bit prefix:

View File

@ -164,8 +164,9 @@ enum mld_msgtype_e
{
MLD_SEND_NONE = 0, /* Nothing to send */
MLD_SEND_GENQUERY, /* Send General Query */
MLD_SEND_REPORT, /* Send Unsolicited report */
MLD_SEND_DONE /* Send Done message */
MLD_SEND_V1REPORT, /* Send MLDv1 Report message */
MLD_SEND_V2REPORT, /* Send MLDv2 Report message */
MLD_SEND_V1DONE /* Send MLDv1 Done message */
};
/* This structure represents one group member. There is a list of groups
@ -182,7 +183,8 @@ struct mld_group_s
struct mld_group_s *next; /* Implements a singly-linked list */
net_ipv6addr_t grpaddr; /* Group IPv6 address */
struct work_s work; /* For deferred timeout operations */
WDOG_ID wdog; /* WDOG used to detect timeouts */
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 */
uint8_t ifindex; /* Interface index */
uint8_t flags; /* See MLD_ flags definitions */
@ -427,29 +429,24 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec);
int mld_leavegroup(FAR const struct ipv6_mreq *mrec);
/****************************************************************************
* Name: mld_starttimer
* Name: mld_start_polltimer
*
* Description:
* Start the MLD timer with differing time units (ticks or deciseconds).
* Start the MLD poll timer.
*
****************************************************************************/
void mld_starttimer(FAR struct mld_group_s *group, clock_t ticks);
void mld_start_polltimer(FAR struct mld_group_s *group, clock_t ticks);
/****************************************************************************
* Name: mld_cmptimer
* Name: mld_start_v1timer
*
* Description:
* Compare the timer remaining on the watching timer to the deci-second
* value. If maxticks > ticks-remaining, then (1) cancel the timer (to
* avoid race conditions) and return true.
*
* If true is returned then the caller must call mld_starttimer() to
* restart the timer
* Start the MLDv1 compatibility timer.
*
****************************************************************************/
bool mld_cmptimer(FAR struct mld_group_s *group, int maxticks);
void mld_start_v1timer(FAR struct mld_group_s *group, clock_t ticks);
/****************************************************************************
* Name: mld_addmcastmac

View File

@ -130,10 +130,13 @@ FAR struct mld_group_s *mld_grpalloc(FAR struct net_driver_s *dev,
nxsem_init(&group->sem, 0, 0);
nxsem_setprotocol(&group->sem, SEM_PRIO_NONE);
/* Initialize the group timer (but don't start it yet) */
/* Initialize the group timers */
group->wdog = wd_create();
DEBUGASSERT(group->wdog);
group->polldog = wd_create();
DEBUGASSERT(group->polldog);
group->v1dog = wd_create();
DEBUGASSERT(group->v1dog);
/* Save the interface index */
@ -231,9 +234,10 @@ void mld_grpfree(FAR struct net_driver_s *dev, FAR struct mld_group_s *group)
{
grpinfo("Free: %p flags: %02x\n", group, group->flags);
/* Cancel the wdog */
/* Cancel the timers */
wd_cancel(group->wdog);
wd_cancel(group->polldog);
wd_cancel(group->v1dog);
/* Remove the group structure from the group list in the device structure */
@ -243,9 +247,10 @@ void mld_grpfree(FAR struct net_driver_s *dev, FAR struct mld_group_s *group)
(void)nxsem_destroy(&group->sem);
/* Destroy the wdog */
/* Destroy the timers */
wd_delete(group->wdog);
wd_delete(group->polldog);
wd_delete(group->v1dog);
/* Then release the group structure resources. */

View File

@ -85,6 +85,7 @@ void mld_devinit(struct net_driver_s *dev)
/* Allow the MLD messages at the MAC level */
mld_addmcastmac(dev, g_ipv6_allrouters);
mld_addmcastmac(dev, g_ipv6_allnodes);
mld_addmcastmac(dev, g_ipv6_allrouters);
mld_addmcastmac(dev, g_ipv6_allmldv2routers);
}

View File

@ -132,13 +132,15 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec)
MLD_STATINCR(g_netstats.mld.joins);
/* Send the Version 1 Multicast Listener Report */
/* Send the Version 2 Multicast Listener Report (Assumes MLDv2 mode
* initially?).
*/
MLD_STATINCR(g_netstats.mld.report_sched);
ret = mld_waitmsg(group, MLD_SEND_REPORT);
ret = mld_waitmsg(group, MLD_SEND_V2REPORT);
if (ret < 0)
{
nerr("ERROR: Failed to schedule message: %d\n", ret);
nerr("ERROR: Failed to schedule Report: %d\n", ret);
mld_grpfree(dev, group);
return ret;
}
@ -147,7 +149,7 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec)
SET_MLD_STARTUP(group->flags);
group->count = MLD_UNSOLREPORT_COUNT - 1;
mld_starttimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC));
mld_start_polltimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC));
/* Add the group (MAC) address to the Ethernet drivers MAC filter list */

View File

@ -122,13 +122,14 @@ int mld_leavegroup(FAR const struct ipv6_mreq *mrec)
{
ninfo("Leaving group: %p\n", group);
/* Cancel the timer and discard any queued Reports. Canceling the
/* 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.
*/
wd_cancel(group->wdog);
wd_cancel(group->polldog);
wd_cancel(group->v1dog);
CLR_MLD_SCHEDMSG(group->flags);
CLR_MLD_WAITMSG(group->flags);
@ -139,9 +140,12 @@ int mld_leavegroup(FAR const struct ipv6_mreq *mrec)
* 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 router responds to the Done message with a multicast-address-s
* pecific (MAS) Query. If any other node responds to the Query with a
* 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))
@ -150,7 +154,7 @@ int mld_leavegroup(FAR const struct ipv6_mreq *mrec)
MLD_STATINCR(g_netstats.mld.done_sched);
ret = mld_waitmsg(group, MLD_SEND_DONE);
ret = mld_waitmsg(group, MLD_SEND_V1DONE);
if (ret < 0)
{
nerr("ERROR: Failed to schedule message: %d\n", ret);

View File

@ -37,6 +37,7 @@
#include <nuttx/config.h>
#include <stdbool.h>
#include <assert.h>
#include <debug.h>
@ -61,6 +62,97 @@
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: mld_setup_v1compat
*
* Description:
* If this is for MLDv1 query, then select MLDv1 compatibility mode and
* start (or re-start) the compatibility timer. We need to make this
* check BEFORE sending the report.
*
****************************************************************************/
static void mld_setup_v1compat(FAR struct mld_group_s *group,
FAR const struct mld_mcast_listen_query_s *query,
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
* an MLDv1 query is received, MLDv2 hosts set their Older Version
* Querier Present Timer to [Older Version Querier Present Timeout].
*
* "This value MUST be ([Robustness Variable] times (the [Query
* Interval] in the last Query received)) plus ([Query Response
* Interval])."
*
* I am not sure how to do that since the MLDv1 version has no QQI
* field. That is an MLDv2 extension.
*/
respmsec = MLD_QUERY_MSEC;
#endif
/* Select MLDv1 compatibility mode (might already be selected) */
SET_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);
}
/* 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)));
}
}
/****************************************************************************
* Name: mld_report_msgtype
*
* Description:
* Determine which type of Report to send, MLDv1 or MLDv2, depending on
* current state of compatibility mode flag.
*
****************************************************************************/
static inline uint8_t mld_report_msgtype(FAR struct mld_group_s *group)
{
if (IS_MLD_V1COMPAT(group->flags))
{
return MLD_SEND_V1REPORT;
}
else
{
return MLD_SEND_V2REPORT;
}
}
/****************************************************************************
* Name: mld_mrc2mrd
*
@ -125,7 +217,7 @@ static bool mld_cmpaddr(FAR struct net_driver_s *dev,
*
* Description:
* Check if we are still the querier for this group (assuming that we are
* currently the querier). This comparies the IPv6 Source Address of the
* 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.
@ -152,9 +244,9 @@ static void mld_check_querier(FAR struct net_driver_s *dev,
if (!IS_MLD_STARTUP(group->flags))
{
/* Yes.. cancel the timer */
/* Yes.. cancel the poll timer */
wd_cancel(group->wdog);
wd_cancel(group->polldog);
}
/* Switch to non-Querier mode */
@ -196,6 +288,8 @@ int mld_query(FAR struct net_driver_s *dev,
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
FAR struct mld_group_s *group;
unsigned int mldsize;
bool mldv1 = false;
ninfo("Multicast Listener Query\n");
@ -207,6 +301,29 @@ int mld_query(FAR struct net_driver_s *dev,
mrc = NTOHS(query->mrc);
#endif
/* The MLD version of a Multicast Listener Query message is determined
* as follows:
*
* MLDv1 Query: length = 24 octets
* MLDv2 Query: length >= 28 octets
*
* Query messages that do not match any of the above conditions (e.g., a
* Query of length 26 octets) MUST be silently ignored.
*/
mldsize = ipv6->len[0] << 8 | ipv6->len[1];
if (mldsize == sizeof(struct mld_mcast_listen_report_v1_s))
{
mldv1 = true;
}
else if (mldsize < SIZEOF_MLD_MCAST_LISTEN_QUERY_S(0))
{
nwarn("WARNING: Invalid size for MLD query: %u\n", mldsize);
dev->d_len = 0;
return -EINVAL;
}
/* There are three variants of the Query message (RFC 3810):
*
* 1. A "General Query" is sent by the Querier to learn which
@ -267,6 +384,16 @@ int mld_query(FAR struct net_driver_s *dev,
/* 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))
{
nwarn("WARNING: MLDv2 query received in MLDv1 "
"compatibility mode\n");
}
}
}
@ -290,9 +417,14 @@ int mld_query(FAR struct net_driver_s *dev,
if (!net_ipv6addr_cmp(member->grpaddr, g_ipv6_allnodes))
{
/* Check MLDv1 compatibility mode */
mld_setup_v1compat(member, query, mldv1);
/* Send one report and break out of the loop */
mld_send(dev, member, MLD_SEND_REPORT);
mld_send(dev, member, mld_report_msgtype(member));
rptsent = true;
break;
}
@ -326,6 +458,13 @@ int mld_query(FAR struct net_driver_s *dev,
mld_check_querier(dev, ipv6, group);
/* Warn if we received a MLDv2 query in MLDv1 compatibility mode. */
if (!mldv1 && IS_MLD_V1COMPAT(group->flags))
{
nwarn("WARNING: MLDv2 query received in MLDv1 compatibility mode\n");
}
/* Check for Multicast Address Specific Query */
if (net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_allrouters))
@ -341,9 +480,13 @@ int mld_query(FAR struct net_driver_s *dev,
MLD_STATINCR(g_netstats.mld.massq_query_received);
}
/* Check MLDv1 compatibility mode */
mld_setup_v1compat(group, query, mldv1);
/* Send the report */
mld_send(dev, group, MLD_SEND_REPORT);
mld_send(dev, group, mld_report_msgtype(group));
}
/* Not sent to all systems. Check for Unicast General Query */
@ -353,9 +496,13 @@ int mld_query(FAR struct net_driver_s *dev,
ninfo("Unicast query\n");
MLD_STATINCR(g_netstats.mld.ucast_query_received);
/* Check MLDv1 compatibility mode */
mld_setup_v1compat(group, query, mldv1);
/* Send the report */
mld_send(dev, group, MLD_SEND_REPORT);
mld_send(dev, group, mld_report_msgtype(group));
}
else
{

View File

@ -48,12 +48,6 @@
#include "devif/devif.h"
#include "mld/mld.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define IPv6BUF ((FAR struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)])
/****************************************************************************
* Private Functions
****************************************************************************/
@ -67,9 +61,8 @@
*
****************************************************************************/
int mld_report(FAR struct net_driver_s *dev)
int mld_report(FAR struct net_driver_s *dev, FAR const net_ipv6addr_t grpaddr)
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
FAR struct mld_group_s *group;
/* Reports are send to the group multicast address. Hence, the IPv6
@ -77,9 +70,8 @@ int mld_report(FAR struct net_driver_s *dev)
*/
ninfo("grpaddr: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
ipv6->destipaddr[0], ipv6->destipaddr[1], ipv6->destipaddr[2],
ipv6->destipaddr[3], ipv6->destipaddr[4], ipv6->destipaddr[5],
ipv6->destipaddr[6], ipv6->destipaddr[7]);
grpaddr[0], grpaddr[1], grpaddr[2], grpaddr[3],
grpaddr[4], grpaddr[5], grpaddr[6], grpaddr[7]);
/* Find the group (or create a new one) using the incoming IP address.
* If we are not a router (and I assume we are not), then can ignore
@ -89,7 +81,7 @@ int mld_report(FAR struct net_driver_s *dev)
*/
#ifdef CONFIG_MLD_ROUTER
group = mld_grpallocfind(dev, ipv6->destipaddr);
group = mld_grpallocfind(dev, grpaddr);
if (group == NULL)
{
nerr("ERROR: Failed to allocate group\n");
@ -97,7 +89,7 @@ int mld_report(FAR struct net_driver_s *dev)
}
#else
group = mld_grpfind(dev, ipv6->destipaddr);
group = mld_grpfind(dev, grpaddr);
if (group == NULL)
{
nwarn("WARNING: Ignoring group. We are not a member\n");
@ -144,15 +136,20 @@ int mld_report(FAR struct net_driver_s *dev)
* so it is deleted from the list and its disappearance is made known to
* the multicast routing component.
*
* A node MUST accept and process any version 1 Report whose IP
* Destination Address field contains *any* of the IPv6 addresses (unicast
* or multicast) assigned to the interface on which the Report arrives.
*
****************************************************************************/
int mld_report_v1(FAR struct net_driver_s *dev,
FAR const struct mld_mcast_listen_report_v1_s *report)
{
ninfo("Version 1 Multicast Listener Report\n");
DEBUGASSERT(dev != NULL && report != NULL);
MLD_STATINCR(g_netstats.mld.v1report_received);
return mld_report(dev);
return mld_report(dev, report->mcastaddr);
}
/****************************************************************************
@ -169,13 +166,42 @@ int mld_report_v1(FAR struct net_driver_s *dev,
* any of these checks fails, the packet is dropped. If the validity of
* the MLD message is verified, the router starts to process the Report.
*
* Version 2 Multicast Listener Reports are sent with an IP destination
* address of FF02:0:0:0:0:0:0:16, to which all MLDv2-capable multicast
* routers listen
*
****************************************************************************/
int mld_report_v2(FAR struct net_driver_s *dev,
FAR const struct mld_mcast_listen_report_v2_s *report)
{
int ret = -ENOENT;
int i;
ninfo("Version 2 Multicast Listener Report\n");
DEBUGASSERT(dev != NULL && report != NULL);
MLD_STATINCR(g_netstats.mld.v2report_received);
return mld_report(dev);
for (i = 0; i < report->naddrec; i++)
{
/* Handle this mcast address in the list */
int status = mld_report(dev, report->addrec[i].mcast);
if (status == OK)
{
/* Return success if any address in the listed was processed */
ret = OK;
}
else if (status != -ENOENT)
{
/* Any result other than -ENOENT would be a problem */
ret = status;
break;
}
}
return ret;
}

View File

@ -76,16 +76,18 @@
/* Buffer layout */
#define IPv6BUF ((FAR struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)])
#define RABUF ((FAR struct ipv6_router_alert_s *) \
&dev->d_buf[NET_LL_HDRLEN(dev)] + IPv6_HDRLEN)
#define RASIZE sizeof(struct ipv6_router_alert_s)
#define QUERYBUF ((FAR struct mld_mcast_listen_query_s *) \
&dev->d_buf[NET_LL_HDRLEN(dev)] + IPv6_HDRLEN + RASIZE)
#define REPORTBUF ((FAR struct mld_mcast_listen_report_v1_s *) \
&dev->d_buf[NET_LL_HDRLEN(dev)] + IPv6_HDRLEN + RASIZE)
#define DONEBUF ((FAR struct mld_mcast_listen_done_v1_s *) \
&dev->d_buf[NET_LL_HDRLEN(dev)] + IPv6_HDRLEN + RASIZE)
#define IPv6BUF ((FAR struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)])
#define RABUF ((FAR struct ipv6_router_alert_s *) \
&dev->d_buf[NET_LL_HDRLEN(dev)] + IPv6_HDRLEN)
#define RASIZE sizeof(struct ipv6_router_alert_s)
#define QUERYBUF ((FAR struct mld_mcast_listen_query_s *) \
&dev->d_buf[NET_LL_HDRLEN(dev)] + IPv6_HDRLEN + RASIZE)
#define V1REPORTBUF ((FAR struct mld_mcast_listen_report_v1_s *) \
&dev->d_buf[NET_LL_HDRLEN(dev)] + IPv6_HDRLEN + RASIZE)
#define V2REPORTBUF ((FAR struct mld_mcast_listen_report_v2_s *) \
&dev->d_buf[NET_LL_HDRLEN(dev)] + IPv6_HDRLEN + RASIZE)
#define DONEBUF ((FAR struct mld_mcast_listen_done_v1_s *) \
&dev->d_buf[NET_LL_HDRLEN(dev)] + IPv6_HDRLEN + RASIZE)
/****************************************************************************
* Public Functions
@ -124,31 +126,46 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
IFF_SET_IPv6(dev->d_flags);
/* What is the size of the ICMPv6 payload? Currently only Version 1
* REPORT and DONE packets are sent (these are actually the same size).
* This will change.
*/
/* What is the size of the ICMPv6 payload? */
switch (msgtype)
{
case MLD_SEND_GENQUERY: /* Send General Query */
ninfo("Send General Query, flags=%02x\n", group->flags);
mldsize = SIZEOF_MLD_MCAST_LISTEN_QUERY_S(0);
{
ninfo("Send General Query, flags=%02x\n", group->flags);
mldsize = SIZEOF_MLD_MCAST_LISTEN_QUERY_S(0);
}
break;
case MLD_SEND_REPORT: /* Send Unsolicited report */
ninfo("Send Report, flags=%02x\n", group->flags);
mldsize = sizeof(struct mld_mcast_listen_report_v1_s);
case MLD_SEND_V1REPORT: /* Send MLDv1 Report message */
{
ninfo("Send MLDv1 Report, flags=%02x\n", group->flags);
mldsize = sizeof(struct mld_mcast_listen_report_v1_s);
}
break;
case MLD_SEND_DONE: /* Send Done message */
ninfo("Send Done message, flags=%02x\n", group->flags);
mldsize = sizeof(struct mld_mcast_listen_done_v1_s);
case MLD_SEND_V2REPORT: /* Send MLDv2 Report message */
{
unsigned int addreclen;
ninfo("Send MLDv2 Report, flags=%02x\n", group->flags);
addreclen = SIZEOF_MLD_MCAST_ADDREC_V2_S(0, 0);
mldsize = SIZEOF_MLD_MCAST_LISTEN_REPORT_V2_S(addreclen);
}
break;
case MLD_SEND_V1DONE: /* Send MLDv1 Done message */
{
ninfo("Send Done message, flags=%02x\n", group->flags);
mldsize = sizeof(struct mld_mcast_listen_done_v1_s);
}
break;
default:
nerr("Bad msgtype: %02x \n", msgtype);
DEBUGPANIC();
{
nerr("Bad msgtype: %02x \n", msgtype);
DEBUGPANIC();
}
return;
}
@ -196,11 +213,15 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
destipaddr = g_ipv6_allnodes;
break;
case MLD_SEND_REPORT: /* Send Unsolicited report */
case MLD_SEND_V1REPORT: /* Send MLDv1 Report message */
destipaddr = group->grpaddr;
break;
case MLD_SEND_DONE: /* Send Done message */
case MLD_SEND_V2REPORT: /* Send MLDv2 Report message */
destipaddr = g_ipv6_allmldv2routers;
break;
case MLD_SEND_V1DONE: /* Send MLDv1 Done message */
destipaddr = g_ipv6_allrouters;
break;
}
@ -234,17 +255,26 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
{
FAR struct mld_mcast_listen_query_s *query = QUERYBUF;
/* Initializer the Query payload. In a General Query, both the
/* Initialize the Query payload. In a General Query, both the
* Multicast Address field and the Number of Sources (N)
* field are zero.
*
* Careful here. In MLDv1 compatibility mode, the MRC is not
* encoded and must follow the rules for MLDv1.
*/
memset(query, 0, sizeof(struct mld_mcast_listen_report_v1_s));
net_ipv6addr_hdrcopy(query->grpaddr, &group->grpaddr);
query->type = ICMPV6_MCAST_LISTEN_QUERY;
query->mrc = MLD_QRESP_MSEC;
query->flags = MLD_ROBUSTNESS;
query->qqic = MLD_QRESP_SEC;
/* Fields unique to the extended MLDv2 query */
if (!IS_MLD_V1COMPAT(group->flags))
{
query->flags = MLD_ROBUSTNESS;
query->qqic = MLD_QRESP_SEC;
}
/* Calculate the ICMPv6 checksum. */
@ -255,11 +285,11 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
}
break;
case MLD_SEND_REPORT:
case MLD_SEND_V1REPORT: /* Send MLDv1 Report message */
{
FAR struct mld_mcast_listen_report_v1_s *report = REPORTBUF;
FAR struct mld_mcast_listen_report_v1_s *report = V1REPORTBUF;
/* Initializer the Report payload */
/* Initialize the Report payload */
memset(report, 0, sizeof(struct mld_mcast_listen_report_v1_s));
net_ipv6addr_hdrcopy(report->mcastaddr, &group->grpaddr);
@ -275,11 +305,36 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
}
break;
case MLD_SEND_DONE:
case MLD_SEND_V2REPORT: /* Send MLDv2 Report message */
{
FAR struct mld_mcast_listen_report_v2_s *report = V2REPORTBUF;
FAR struct mld_mcast_addrec_v2_s *addrec;
/* Initialize the Report payload */
memset(report, 0, mldsize);
report->type = ICMPV6_MCAST_LISTEN_REPORT_V2;
report->naddrec = 1;
addrec = report->addrec;
addrec->rectype = MODE_IS_INCLUDE;
net_ipv6addr_hdrcopy(addrec->mcast, &group->grpaddr);
/* Calculate the ICMPv6 checksum. */
report->chksum = 0;
report->chksum = ~icmpv6_chksum(dev);
SET_MLD_LASTREPORT(group->flags); /* Remember we were the last to report */
MLD_STATINCR(g_netstats.mld.report_sent);
}
break;
case MLD_SEND_V1DONE: /* Send MLDv1 Done message */
{
FAR struct mld_mcast_listen_done_v1_s *done = DONEBUF;
/* Initializer the Done payload */
/* Initialize the Done payload */
memset(done, 0, sizeof(struct mld_mcast_listen_done_v1_s));
done->type = ICMPV6_MCAST_LISTEN_DONE_V1;

View File

@ -90,7 +90,28 @@
****************************************************************************/
/****************************************************************************
* Name: mld_timeout_work
* Name: mld_report_msgtype
*
* Description:
* Determine which type of Report to send, MLDv1 or MLDv2, depending on
* current state of compatibility mode flag.
*
****************************************************************************/
static inline uint8_t mld_report_msgtype(FAR struct mld_group_s *group)
{
if (IS_MLD_V1COMPAT(group->flags))
{
return MLD_SEND_V1REPORT;
}
else
{
return MLD_SEND_V2REPORT;
}
}
/****************************************************************************
* Name: mld_polldog_work
*
* Description:
* Timeout watchdog work
@ -100,7 +121,7 @@
*
****************************************************************************/
static void mld_timeout_work(FAR void *arg)
static void mld_polldog_work(FAR void *arg)
{
FAR struct mld_group_s *group;
int ret;
@ -118,7 +139,7 @@ static void mld_timeout_work(FAR void *arg)
/* Schedule (and forget) the Report. */
MLD_STATINCR(g_netstats.mld.report_sched);
ret = mld_schedmsg(group, MLD_SEND_REPORT);
ret = mld_schedmsg(group, mld_report_msgtype(group));
if (ret < 0)
{
nerr("ERROR: Failed to schedule message: %d\n", ret);
@ -133,7 +154,7 @@ static void mld_timeout_work(FAR void *arg)
/* Decrement the count and restart the timer */
group->count--;
mld_starttimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC));
mld_start_polltimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC));
}
else
{
@ -145,7 +166,7 @@ static void mld_timeout_work(FAR void *arg)
if (IS_MLD_QUERIER(group->flags))
{
mld_starttimer(group, MSEC2TICK(MLD_QUERY_MSEC));
mld_start_polltimer(group, MSEC2TICK(MLD_QUERY_MSEC));
}
}
}
@ -165,25 +186,25 @@ static void mld_timeout_work(FAR void *arg)
/* Restart the Querier timer */
mld_starttimer(group, MSEC2TICK(MLD_QUERY_MSEC));
mld_start_polltimer(group, MSEC2TICK(MLD_QUERY_MSEC));
}
net_unlock();
}
/****************************************************************************
* Name: mld_timeout
* Name: mld_polldog_timout
*
* Description:
* Timeout watchdog handler.
*
* Assumptions:
* This function is called from the wdog timer handler which runs in the
* This function is called from the polldog timer handler which runs in the
* context of the timer interrupt handler.
*
****************************************************************************/
static void mld_timeout(int argc, uint32_t arg, ...)
static void mld_polldog_timout(int argc, uint32_t arg, ...)
{
FAR struct mld_group_s *group;
int ret;
@ -199,7 +220,92 @@ static void mld_timeout(int argc, uint32_t arg, ...)
* work queue.
*/
ret = work_queue(LPWORK, &group->work, mld_timeout_work, group, 0);
ret = work_queue(LPWORK, &group->work, mld_polldog_work, group, 0);
if (ret < 0)
{
nerr("ERROR: Failed to queue timeout 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 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;
ninfo("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)
{
nerr("ERROR: Failed to queue timeout work: %d\n", ret);
@ -211,78 +317,47 @@ static void mld_timeout(int argc, uint32_t arg, ...)
****************************************************************************/
/****************************************************************************
* Name: mld_starttimer
* Name: mld_start_polltimer
*
* Description:
* Start the MLD timer.
*
* Assumptions:
* This function may be called from most any context.
* Start the MLD poll timer.
*
****************************************************************************/
void mld_starttimer(FAR struct mld_group_s *group, clock_t ticks)
void mld_start_polltimer(FAR struct mld_group_s *group, clock_t ticks)
{
int ret;
/* Start the timer */
mtmrinfo("ticks: %ld\n", (unsigned long)ticks);
mtmrinfo("ticks: %lu\n", (unsigned long)ticks);
ret = wd_start(group->wdog, ticks, mld_timeout, 1, (uint32_t)group);
ret = wd_start(group->polldog, ticks, mld_polldog_timout, 1, (uint32_t)group);
DEBUGASSERT(ret == OK);
UNUSED(ret);
}
/****************************************************************************
* Name: mld_cmptimer
* Name: mld_start_v1timer
*
* Description:
* Compare the timer remaining on the watching timer to the deci-second
* value. If maxticks > ticks-remaining, then (1) cancel the timer (to
* avoid race conditions) and return true.
*
* Assumptions:
* This function may be called from most any context. If true is returned
* then the caller must call mld_starttimer() to restart the timer
* Start the MLDv1 compatibility timer.
*
****************************************************************************/
bool mld_cmptimer(FAR struct mld_group_s *group, int maxticks)
void mld_start_v1timer(FAR struct mld_group_s *group, clock_t ticks)
{
irqstate_t flags;
int remaining;
int ret;
/* Disable interrupts so that there is no race condition with the actual
* timer expiration.
*/
/* Start the timer */
flags = enter_critical_section();
mtmrinfo("ticks: %lu\n", (unsigned long)ticks);
/* Get the timer remaining on the watchdog. A time of <= zero means that
* the watchdog was never started.
*/
ret = wd_start(group->v1dog, ticks, mld_v1dog_timout, 1, (uint32_t)group);
remaining = wd_gettime(group->wdog);
/* A remaining time of zero means that the watchdog was never started
* or has already expired. That case should be covered in the following
* test as well.
*/
mtmrinfo("maxticks: %d remaining: %d\n", maxticks, remaining);
if (maxticks > remaining)
{
/* Cancel the watchdog timer and return true */
wd_cancel(group->wdog);
leave_critical_section(flags);
return true;
}
leave_critical_section(flags);
return false;
DEBUGASSERT(ret == OK);
UNUSED(ret);
}
#endif /* CONFIG_NET_MLD */