net/mld: Review and update all MLD message receipt logic. Also ripple changs that are, hopefully, improvements back to IGMP.

This commit is contained in:
Gregory Nutt 2018-11-04 10:57:21 -06:00
parent 3eed2e01bc
commit b1a61834d9
15 changed files with 440 additions and 298 deletions

View file

@ -382,6 +382,7 @@ struct mld_stats_s
net_stats_t mas_query_received; /* Multicast Address Specific QUERY received */
net_stats_t massq_query_received; /* Multicast Address and Source Specific QUERY received */
net_stats_t ucast_query_received; /* Unicast query received */
net_stats_t bad_query_received; /* Unhandled query received */
net_stats_t v1report_received; /* Version 1 REPORT packets received */
net_stats_t v2report_received; /* Version 2 REPORT packets received */
net_stats_t done_received; /* DONE packets received */

View file

@ -492,8 +492,6 @@ void icmpv6_input(FAR struct net_driver_s *dev,
{
goto icmpv6_drop_packet;
}
goto icmpv6_send_nothing; /* REVISIT */
}
break;
@ -507,8 +505,6 @@ void icmpv6_input(FAR struct net_driver_s *dev,
{
goto icmpv6_drop_packet;
}
goto icmpv6_send_nothing; /* REVISIT */
}
break;
@ -523,8 +519,6 @@ void icmpv6_input(FAR struct net_driver_s *dev,
{
goto icmpv6_drop_packet;
}
goto icmpv6_send_nothing; /* REVISIT */
}
break;
@ -538,8 +532,6 @@ void icmpv6_input(FAR struct net_driver_s *dev,
{
goto icmpv6_drop_packet;
}
goto icmpv6_send_nothing; /* REVISIT */
}
break;
#endif
@ -551,13 +543,17 @@ void icmpv6_input(FAR struct net_driver_s *dev,
}
}
ninfo("Outgoing ICMPv6 packet length: %d (%d)\n",
dev->d_len, (ipv6->len[0] << 8) | ipv6->len[1]);
#ifdef CONFIG_NET_STATISTICS
g_netstats.icmpv6.sent++;
g_netstats.ipv6.sent++;
if (dev->d_len > 0)
{
ninfo("Outgoing ICMPv6 packet length: %d (%d)\n",
dev->d_len, (ipv6->len[0] << 8) | ipv6->len[1]);
g_netstats.icmpv6.sent++;
g_netstats.ipv6.sent++;
}
#endif
return;
icmpv6_type_error:

View file

@ -275,6 +275,7 @@ void igmp_poll(FAR struct net_driver_s *dev);
* group - Describes the multicast group member and identifies the
* message to be sent.
* destipaddr - The IP address of the recipient of the message
* msgid - ID of message to send
*
* Returned Value:
* None
@ -285,7 +286,7 @@ void igmp_poll(FAR struct net_driver_s *dev);
****************************************************************************/
void igmp_send(FAR struct net_driver_s *dev, FAR struct igmp_group_s *group,
FAR in_addr_t *dest);
FAR in_addr_t *destipaddr, uint8_t msgid);
/****************************************************************************
* Name: igmp_joingroup

View file

@ -137,16 +137,33 @@ void igmp_input(struct net_driver_s *dev)
return;
}
/* Find the group (or create a new one) using the incoming IP address */
/* 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
* querys for and reports from groups that we are not a member of.
*
* REVISIT: Router support is not yet implemented.
*/
destipaddr = net_ip4addr_conv32(IGMPBUF->destipaddr);
#ifdef CONFIG_IGMP_ROUTER
group = igmp_grpallocfind(dev, &destipaddr);
if (!group)
if (group == NULL)
{
nerr("ERROR: Failed to allocate/find group: %08x\n", destipaddr);
nerr("ERROR: Failed to allocate group: %08x\n", destipaddr);
return;
}
#else
group = igmp_grpfind(dev, &destipaddr);
if (group == NULL)
{
nwarn("WARNING: Ignoring group. We are not a member: %08x\n",
destipaddr);
return;
}
#endif
/* Now handle the message based on the IGMP message type */
switch (IGMPBUF->type)
@ -183,6 +200,7 @@ void igmp_input(struct net_driver_s *dev)
if (IGMPBUF->grpaddr == 0)
{
FAR struct igmp_group_s *member;
bool rptsent = false;
/* This is the general query */
@ -196,6 +214,11 @@ void igmp_input(struct net_driver_s *dev)
}
IGMP_STATINCR(g_netstats.igmp.query_received);
/* Two passes through the member list. On the first, just
* perform IDLE member checks.
*/
for (member = (FAR struct igmp_group_s *)dev->d_igmp_grplist.head;
member;
member = member->next)
@ -213,6 +236,42 @@ void igmp_input(struct net_driver_s *dev)
}
}
}
/* 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
*
* REVISIT: This is a design flaw: Only a single report can
* be sent in this context because there is no mechanism to
* preserve the incoming request nor to queue multiple
* outgoing reports.
*/
for (member = (FAR struct igmp_group_s *)dev->d_igmp_grplist.head;
member;
member = member->next)
{
/* Skip over the all systems group entry */
if (!net_ipv4addr_cmp(member->grpaddr, g_ipv4_allsystems))
{
/* Send one REPORT and break out of the loop. */
igmp_send(dev, member, &member->grpaddr,
IGMPv2_MEMBERSHIP_REPORT);
rptsent = true;
break;
}
}
if (!rptsent)
{
goto noresponse;
}
}
else /* if (IGMPBUF->grpaddr != 0) */
{
@ -223,13 +282,28 @@ void igmp_input(struct net_driver_s *dev)
*/
IGMP_STATINCR(g_netstats.igmp.ucast_query);
grpaddr = net_ip4addr_conv32(IGMPBUF->grpaddr);
group = igmp_grpallocfind(dev, &grpaddr);
ticks = net_dsec2tick((int)IGMPBUF->maxresp);
if (IS_IDLEMEMBER(group->flags) || igmp_cmptimer(group, ticks))
group = igmp_grpfind(dev, &grpaddr);
if (group != NULL)
{
igmp_startticks(group, ticks);
CLR_IDLEMEMBER(group->flags);
ticks = net_dsec2tick((int)IGMPBUF->maxresp);
if (IS_IDLEMEMBER(group->flags) || igmp_cmptimer(group, ticks))
{
igmp_startticks(group, ticks);
CLR_IDLEMEMBER(group->flags);
}
/* Send the REPORT */
igmp_send(dev, group, &group->grpaddr,
IGMPv2_MEMBERSHIP_REPORT);
}
else
{
goto noresponse;
}
}
}
@ -249,6 +323,11 @@ void igmp_input(struct net_driver_s *dev)
igmp_startticks(group, ticks);
CLR_IDLEMEMBER(group->flags);
}
/* Send the REPORT */
igmp_send(dev, group, &group->grpaddr,
IGMPv2_MEMBERSHIP_REPORT);
}
break;
@ -265,15 +344,24 @@ void igmp_input(struct net_driver_s *dev)
SET_IDLEMEMBER(group->flags);
CLR_LASTREPORT(group->flags);
}
goto noresponse;
}
break;
default:
{
nwarn("WARNING: Unexpected msg %02x\n", IGMPBUF->type);
goto noresponse;
}
break;
}
return;
noresponse:
dev->d_len = 0;
return;
}
#endif /* CONFIG_NET_IGMP */

View file

@ -90,7 +90,6 @@ static inline void igmp_sched_send(FAR struct net_driver_s *dev,
dest = &group->grpaddr;
ninfo("Send IGMPv2_MEMBERSHIP_REPORT, dest=%08x flags=%02x\n",
*dest, group->flags);
IGMP_STATINCR(g_netstats.igmp.report_sched);
SET_LASTREPORT(group->flags); /* Remember we were the last to report */
}
else
@ -99,12 +98,11 @@ static inline void igmp_sched_send(FAR struct net_driver_s *dev,
dest = &g_ipv4_allrouters;
ninfo("Send IGMP_LEAVE_GROUP, dest=%08x flags=%02x\n",
*dest, group->flags);
IGMP_STATINCR(g_netstats.igmp.leave_sched);
}
/* Send the message */
igmp_send(dev, group, dest);
igmp_send(dev, group, dest, group->msgid);
/* Indicate that the message has been sent */
@ -164,10 +162,6 @@ void igmp_poll(FAR struct net_driver_s *dev)
/* Yes, create the IGMP message in the driver buffer */
igmp_sched_send(dev, group);
/* Mark the message as sent and break out */
CLR_SCHEDMSG(group->flags);
break;
}
}

View file

@ -105,6 +105,7 @@ static uint16_t igmp_chksum(FAR uint8_t *buffer, int buflen)
* group - Describes the multicast group member and identifies the
* message to be sent.
* destipaddr - The IP address of the recipient of the message
* msgid - ID of message to send
*
* Returned Value:
* None
@ -115,9 +116,9 @@ static uint16_t igmp_chksum(FAR uint8_t *buffer, int buflen)
****************************************************************************/
void igmp_send(FAR struct net_driver_s *dev, FAR struct igmp_group_s *group,
FAR in_addr_t *destipaddr)
FAR in_addr_t *destipaddr, uint8_t msgid)
{
ninfo("msgid: %02x destipaddr: %08x\n", group->msgid, (int)*destipaddr);
ninfo("msgid: %02x destipaddr: %08x\n", msgid, (int)*destipaddr);
/* The total length to send is the size of the IP and IGMP headers and 4
* bytes for the ROUTER ALERT (and, eventually, the Ethernet header)
@ -158,7 +159,7 @@ void igmp_send(FAR struct net_driver_s *dev, FAR struct igmp_group_s *group,
/* Set up the IGMP message */
IGMPBUF->type = group->msgid;
IGMPBUF->type = msgid;
IGMPBUF->maxresp = 0;
net_ipv4addr_hdrcopy(IGMPBUF->grpaddr, &group->grpaddr);

View file

@ -370,10 +370,10 @@ void mld_poll(FAR struct net_driver_s *dev);
* the IP header and calculates the IP header checksum.
*
* Input Parameters:
* dev - The device driver structure to use in the send operation.
* group - Describes the multicast group member and identifies the
* message to be sent.
* destipaddr - The IP address of the recipient of the message
* dev - The device driver structure to use in the send operation.
* group - Describes the multicast group member and identifies the
* message to be sent.
* msgtype - The type of the message to be sent (see enum mld_msgtype_e)
*
* Returned Value:
* None
@ -384,7 +384,7 @@ void mld_poll(FAR struct net_driver_s *dev);
****************************************************************************/
void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
FAR const net_ipv6addr_t dest);
uint8_t msgtype);
/****************************************************************************
* Name: mld_joingroup

View file

@ -81,17 +81,23 @@
int mld_done_v1(FAR struct net_driver_s *dev,
FAR const struct mld_mcast_listen_done_v1_s *done)
{
#ifdef CONFIG_MLD_ROUTER
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
FAR struct mld_group_s *group;
ninfo("Version 1 Multicast Listener Done\n");
MLD_STATINCR(g_netstats.mld.done_received);
/* Find the group (or create a new one) using the incoming IP address */
/* The done message is sent to the link-local, all routers multicast
* address. Find the group using the group address in the Done message.
*/
group = mld_grpfind(dev, ipv6->destipaddr);
group = mld_grpfind(dev, done->grpaddr);
if (group == NULL)
{
return -ENOENT; /* REVISIT: Or should it return OK? */
/* We know nothing of this group */
return -ENOENT;
}
/* Ignore the Done message is this is not a Querier */
@ -106,6 +112,15 @@ int mld_done_v1(FAR struct net_driver_s *dev,
mld_grpfree(dev, group);
}
#else
/* We are not a router so we can just ignore Done messages */
ninfo("Version 1 Multicast Listener Done\n");
MLD_STATINCR(g_netstats.mld.done_received);
#endif
/* Need to set d_len to zero to indication that nothing is being sent */
dev->d_len = 0;
return OK;
}

View file

@ -134,7 +134,15 @@ int mld_leavegroup(FAR const struct ipv6_mreq *mrec)
MLD_STATINCR(g_netstats.mld.leaves);
/* Send a leave if the flag is set according to the state diagram */
/* Send a leave if the LASTREPORT flag is set for the group. If there
* are other members of the group, then their reports will clear the
* LAST REPORT flag. In this case we know that there are other
* members of the group and we do not have to send the Done message.
*
* The router responds to the Done message with a multicast-address-s
* pecific (MAS) Query. If any other node responds to the Query with a
* Report message the there are still listeners present.
*/
if (IS_MLD_LASTREPORT(group->flags))
{

View file

@ -82,6 +82,8 @@ int mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgtype)
return -ENODEV;
}
/* Schedule the message */
group->msgtype = msgtype;
SET_MLD_SCHEDMSG(group->flags);
@ -132,7 +134,7 @@ int mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgtype)
*/
DEBUGASSERT(ret == -EINTR || ret == -ECANCELED);
if (ret != -EINTR && ret != -ECANCELED)
if (ret != -EINTR)
{
break;
}

View file

@ -47,7 +47,6 @@
#include <nuttx/net/ip.h>
#include "devif/devif.h"
#include "inet/inet.h"
#include "mld/mld.h"
/****************************************************************************
@ -71,63 +70,6 @@
static inline void mld_sched_send(FAR struct net_driver_s *dev,
FAR struct mld_group_s *group)
{
const net_ipv6addr_t *dest;
/* Check what kind of message we need to send. There are only three
* possibilities:
*/
if (group->msgtype == MLD_SEND_GENQUERY)
{
dest = &g_ipv6_allrouters;
ninfo("Send General Query, flags=%02x\n", group->flags);
ninfo("destipaddr: %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]);
}
else if (group->msgtype == MLD_SEND_REPORT)
{
dest = &group->grpaddr;
ninfo("Send Report, flags=%02x\n", group->flags);
ninfo("destipaddr: %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]);
SET_MLD_LASTREPORT(group->flags); /* Remember we were the last to report */
}
else
{
DEBUGASSERT(group->msgtype == MLD_SEND_DONE);
dest = &g_ipv6_allrouters;
ninfo("Send Done message, flags=%02x\n", group->flags);
ninfo("destipaddr: %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]);
}
/* Send the message */
mld_send(dev, group, *dest);
/* Indicate that the message has been sent */
CLR_MLD_SCHEDMSG(group->flags);
group->msgtype = 0;
/* If there is a thread waiting fore the message to be sent, wake it up */
if (IS_MLD_WAITMSG(group->flags))
{
ninfo("Awakening waiter\n");
nxsem_post(&group->sem);
}
}
/****************************************************************************
@ -171,13 +113,27 @@ void mld_poll(FAR struct net_driver_s *dev)
if (IS_MLD_SCHEDMSG(group->flags))
{
/* Yes, create the MLD message in the driver buffer */
/* Yes.. create the MLD message in the driver buffer */
mld_sched_send(dev, group);
mld_send(dev, group, group->msgtype);
/* Mark the message as sent and break out */
/* Indicate that the message has been sent */
CLR_MLD_SCHEDMSG(group->flags);
group->msgtype = MLD_SEND_NONE;
/* If there is a thread waiting fore the message to be sent, wake
* it up.
*/
if (IS_MLD_WAITMSG(group->flags))
{
ninfo("Awakening waiter\n");
nxsem_post(&group->sem);
}
/* And break out of the loop */
CLR_MLD_SCHEDMSG(group->flags);
break;
}
}

View file

@ -70,6 +70,7 @@
*
****************************************************************************/
#if 0 /* Not used */
static clock_t mld_mrc2mrd(uint16_t mrc)
{
uint32_t mrd; /* Units of milliseconds */
@ -91,6 +92,7 @@ static clock_t mld_mrc2mrd(uint16_t mrc)
return MSEC2TICK((clock_t)mrd);
}
#endif
/****************************************************************************
* Name: mld_cmpaddr
@ -122,22 +124,21 @@ static bool mld_cmpaddr(FAR struct net_driver_s *dev,
* Name: mld_check_querier
*
* Description:
* Perform a numerical comparison of the IPv6 Source Address and the IPv6
* address of the link. Return true if the source address is less than
* the link address.
* 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
* 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.
*
****************************************************************************/
static void mld_check_querier(FAR struct net_driver_s *dev,
FAR struct ipv6_hdr_s *ipv6,
FAR struct mld_group_s *member,
uint16_t mrc)
FAR struct mld_group_s *group)
{
clock_t ticks;
/* Check if this member is a Querier */
if (IS_MLD_QUERIER(member->flags))
if (IS_MLD_QUERIER(group->flags))
{
/* This is a querier, check if the IPv6 source address is numerically
* less than the IPv6 address assigned to this link.
@ -145,17 +146,20 @@ static void mld_check_querier(FAR struct net_driver_s *dev,
if (mld_cmpaddr(dev, ipv6->srcipaddr))
{
/* This is a querier, then switch to non-querier and set a timeout.
* If additional queries are received within this timeout period,
* then we need to revert to Querier.
/* Are we past the start up phase (where the timer is used for a
* different purpose)?
*/
ticks = mld_mrc2mrd(mrc);
if (mld_cmptimer(member, ticks))
if (!IS_MLD_STARTUP(group->flags))
{
mld_starttimer(member, ticks);
CLR_MLD_QUERIER(member->flags);
/* Yes.. cancel the timer */
wd_cancel(group->wdog);
}
/* Switch to non-Querier mode */
CLR_MLD_QUERIER(group->flags);
}
}
}
@ -192,120 +196,175 @@ int mld_query(FAR struct net_driver_s *dev,
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
FAR struct mld_group_s *group;
uint16_t mrc;
bool unspec;
ninfo("Multicast Listener Query\n");
ninfo("destipaddr: %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]);
/* Find the group (or create a new one) using the incoming IP address */
#if 0 /* Not used */
/* Max Response Delay. The Max Response Code field specifies the maximum
* allowed time before sending a responding report in units of 1/10 second.
*/
group = mld_grpallocfind(dev, ipv6->destipaddr);
if (group == NULL)
mrc = NTOHS(query->mrc);
#endif
/* There are three variants of the Query message (RFC 3810):
*
* 1. A "General Query" is sent by the Querier to learn which
* multicast addresses have listeners on an attached link. In a
* General Query, both the Multicast Address field and the Number
* of Sources (N) field are zero.
* 2. A "Multicast Address Specific Query" is sent by the Querier to
* learn if a particular multicast address has any listeners on an
* attached link. In a Multicast Address Specific Query, the
* Multicast Address field contains the multicast address of
* interest, while the Number of Sources (N) field is set to zero.
* 3. A "Multicast Address and Source Specific Query" is sent by the
* Querier to learn if any of the sources from the specified list for
* the particular multicast address has any listeners on an attached
* link or not. In a Multicast Address and Source Specific Query the
* Multicast Address field contains the multicast address of
* interest, while the Source Address [i] field(s) contain(s) the
* source address(es) of interest.
*
* Another possibility is a Unicast query that is sent specifically
* to our local IP address.
*/
/* Check the destination address. This varies with the type of message
* being sent:
*
* MESSAGE DESTINATION ADDRESS
* General Query Message: The link-local, all nodes multicast address
* MAS Query Messages: The group multicast address
*/
/* Check for a General Query */
if (net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_allnodes) &&
net_ipv6addr_cmp(query->grpaddr, g_ipv6_unspecaddr) &&
query->nsources == 0)
{
nerr("ERROR: Failed to allocate/find group\n");
FAR struct mld_group_s *member;
bool rptsent = false;
/* This is the general query */
ninfo("General multicast query\n");
MLD_STATINCR(g_netstats.mld.gmq_query_received);
/* Two passes through the member list. On the first, just check if we
* are still the querier for the qroup.
*/
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);
}
}
/* 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
*
* REVISIT: This is a design flaw: Only a single report can be sent
* in this context because there is no mechanism to preserve the
* incoming request nor to queue multiple outgoing reports.
*/
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))
{
/* Send one report and break out of the loop */
mld_send(dev, member, MLD_SEND_REPORT);
rptsent = true;
break;
}
}
/* Need to set d_len to zero if nothing is being sent */
if (!rptsent)
{
dev->d_len = 0;
}
return OK;
}
/* Find the group using associated with this group address. For the purpose
* of sending reports, we only care about the query if we are a member of
* the group.
*/
group = mld_grpfind(dev, query->grpaddr);
if (group != NULL)
{
ninfo("We are not a member of this group\n");
dev->d_len = 0;
return -ENOENT;
}
/* Max Response Time. The Max Response Time field is meaningful only in
* Query messages, and specifies the maximum allowed time before sending
* a responding report in units of 1/10 second. In all other messages,
* it is set to zero by the sender and ignored by receivers.
*/
/* Check if we are still the querier for this group */
/* Check if the query was sent to all systems */
mld_check_querier(dev, ipv6, group);
unspec = net_ipv6addr_cmp(query->grpaddr, g_ipv6_unspecaddr);
mrc = NTOHS(query->mrc);
/* Check for Multicast Address Specific Query */
if (net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_allnodes))
if (net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_allrouters))
{
/* There are three variants of the Query message (RFC 3810):
*
* 1. A "General Query" is sent by the Querier to learn which
* multicast addresses have listeners on an attached link. In a
* General Query, both the Multicast Address field and the Number
* of Sources (N) field are zero.
* 2. A "Multicast Address Specific Query" is sent by the Querier to
* learn if a particular multicast address has any listeners on an
* attached link. In a Multicast Address Specific Query, the
* Multicast Address field contains the multicast address of
* interest, while the Number of Sources (N) field is set to zero.
* 3. A "Multicast Address and Source Specific Query" is sent by the
* Querier to learn if any of the sources from the specified list for
* the particular multicast address has any listeners on an attached
* link or not. In a Multicast Address and Source Specific Query the
* Multicast Address field contains the multicast address of
* interest, while the Source Address [i] field(s) contain(s) the
* source address(es) of interest.
*/
if (unspec && query->nsources == 0)
{
FAR struct mld_group_s *member;
/* This is the general query */
ninfo("General multicast query\n");
MLD_STATINCR(g_netstats.mld.gmq_query_received);
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))
{
/* REVISIT: Missing logic. No action is taken on the query. */
mld_check_querier(dev, ipv6, member, mrc);
}
}
}
else if (!unspec && query->nsources == 0)
if (query->nsources == 0)
{
ninfo("Multicast Address Specific Query\n");
MLD_STATINCR(g_netstats.mld.mas_query_received);
/* We first need to re-lookup the group since we used the incoming
* dest last time. Use the group address in the query.
*/
group = mld_grpallocfind(dev, query->grpaddr);
/* REVISIT: Missing logic. No action is taken on the query. */
mld_check_querier(dev, ipv6, group, mrc);
}
else
{
ninfo("Multicast Address and Source Specific Query\n");
MLD_STATINCR(g_netstats.mld.massq_query_received);
/* We first need to re-lookup the group since we used the incoming
* dest last time. Use the group address in the query.
*/
group = mld_grpallocfind(dev, query->grpaddr);
/* REVISIT: Missing logic. No action is taken on the query. */
mld_check_querier(dev, ipv6, group, mrc);
}
/* Send the report */
mld_send(dev, group, MLD_SEND_REPORT);
}
/* Not sent to all systems -- Unicast query */
/* Not sent to all systems. Check for Unicast General Query */
else if (!unspec)
else if (net_ipv6addr_cmp(ipv6->destipaddr, dev->d_ipv6addr))
{
ninfo("Unicast query\n");
MLD_STATINCR(g_netstats.mld.ucast_query_received);
mld_check_querier(dev, ipv6, group, mrc);
/* Send the report */
mld_send(dev, group, MLD_SEND_REPORT);
}
else
{
nwarn("WARNING: Unhandled query\n");
MLD_STATINCR(g_netstats.mld.bad_query_received);
/* Need to set d_len to zero to indication that nothing is being sent */
dev->d_len = 0;
}
return OK;

View file

@ -54,6 +54,69 @@
#define IPv6BUF ((FAR struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)])
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: mld_report
*
* Description:
* Common report handling. Since we are not a router, we do very little
* on the receipt of a report from another member of the group.
*
****************************************************************************/
int mld_report(FAR struct net_driver_s *dev)
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
FAR struct mld_group_s *group;
/* Reports are send to the group multicast address. Hence, the IPv6
* destipaddr idenfies the group.
*/
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]);
/* 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
* reports from groups that we are not a member of.
*
* REVISIT: Router support is not yet implemented.
*/
#ifdef CONFIG_MLD_ROUTER
group = mld_grpallocfind(dev, ipv6->destipaddr);
if (group == NULL)
{
nerr("ERROR: Failed to allocate group\n");
return -ENOMEM;
}
#else
group = mld_grpfind(dev, ipv6->destipaddr);
if (group == NULL)
{
nwarn("WARNING: Ignoring group. We are not a member\n");
return -ENOENT;
}
#endif
/* There are certainly other members of this group, we can clear the
* LASTREPORT flag.
*/
CLR_MLD_LASTREPORT(group->flags);
/* Need to set d_len to zero to indication that nothing is being sent */
dev->d_len = 0;
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -86,35 +149,10 @@
int mld_report_v1(FAR struct net_driver_s *dev,
FAR const struct mld_mcast_listen_report_v1_s *report)
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
FAR struct mld_group_s *group;
ninfo("Version 1 Multicast Listener Report\n");
ninfo("destipaddr: %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]);
MLD_STATINCR(g_netstats.mld.v1report_received);
/* Find the group (or create a new one) using the incoming IP address */
group = mld_grpallocfind(dev, ipv6->destipaddr);
if (group == NULL)
{
nerr("ERROR: Failed to allocate/find group\n");
return -ENOENT;
}
/* If we are a Querier, then reset the timer for that group. */
if (IS_MLD_QUERIER(group->flags))
{
mld_starttimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC));
CLR_MLD_LASTREPORT(group->flags);
}
return OK;
return mld_report(dev);
}
/****************************************************************************
@ -136,51 +174,8 @@ int mld_report_v1(FAR struct net_driver_s *dev,
int mld_report_v2(FAR struct net_driver_s *dev,
FAR const struct mld_mcast_listen_report_v2_s *report)
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
FAR struct mld_group_s *group;
ninfo("Version 2 Multicast Listener Report\n");
ninfo("destipaddr: %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]);
MLD_STATINCR(g_netstats.mld.v2report_received);
/* Check for a valid report
*
* REVISIT: Missing required test for Router Alert option. That has
* already been handled in ipv6_input() but is not available here
* unless we re-parse the extension options.
*/
if (!net_is_addr_linklocal(ipv6->srcipaddr) || ipv6->ttl != 1)
{
nwarn("WARNING: Bad Report, ttl=%u\n", ipv6->ttl);
nwarn(" srcipaddr: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
ipv6->srcipaddr[0], ipv6->srcipaddr[1], ipv6->srcipaddr[2],
ipv6->srcipaddr[3], ipv6->srcipaddr[4], ipv6->srcipaddr[5],
ipv6->srcipaddr[6], ipv6->srcipaddr[7]);
return -EINVAL;
}
/* Find the group (or create a new one) using the incoming IP address */
group = mld_grpallocfind(dev, ipv6->destipaddr);
if (group == NULL)
{
nerr("ERROR: Failed to allocate/find group\n");
return -ENOENT;
}
/* If we are a Querier, then reset the timer for that group. */
if (IS_MLD_QUERIER(group->flags))
{
mld_starttimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC));
CLR_MLD_LASTREPORT(group->flags);
}
return OK;
return mld_report(dev);
}

View file

@ -99,10 +99,10 @@
* the IP header and calculates the IP header checksum.
*
* Input Parameters:
* dev - The device driver structure to use in the send operation.
* group - Describes the multicast group member and identifies the
* message to be sent.
* destipaddr - The IP address of the recipient of the message
* dev - The device driver structure to use in the send operation.
* group - Describes the multicast group member and identifies the
* message to be sent.
* msgtype - The type of the message to be sent (see enum mld_msgtype_e)
*
* Returned Value:
* None
@ -113,17 +113,13 @@
****************************************************************************/
void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
FAR const net_ipv6addr_t destipaddr)
uint8_t msgtype)
{
FAR struct ipv6_hdr_s *ipv6;
FAR struct ipv6_router_alert_s *ra;
FAR const uint16_t *destipaddr;
unsigned int mldsize;
ninfo("msgtype: %02x \n", group->msgtype);
ninfo("destipaddr: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
destipaddr[0], destipaddr[1], destipaddr[2], destipaddr[3],
destipaddr[4], destipaddr[5], destipaddr[6], destipaddr[7]);
/* Select IPv6 */
IFF_SET_IPv6(dev->d_flags);
@ -133,21 +129,25 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
* This will change.
*/
switch (group->msgtype)
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);
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);
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);
break;
default:
nerr("Bad msgtype: %02x \n", msgtype);
DEBUGPANIC();
return;
}
@ -174,7 +174,41 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
ipv6->proto = NEXT_HOPBYBOT_EH; /* Hop-to-hop extension header */
ipv6->ttl = MLD_TTL; /* MLD Time-to-live */
/* Select the IPv6 source address (the local interface assigned to the
* network device).
*/
net_ipv6addr_hdrcopy(ipv6->srcipaddr, dev->d_ipv6addr);
/* Select the IPv6 destination address. This varies with the type of message
* being sent:
*
* MESSAGE DESTINATION ADDRESS
* General Query Message: The link-local, all nodes multicast address
* MAS Query Messages: The group multicast address
* Report Message: The group multicast address
* Done Message: The link-local, all routers multicast address.
*/
switch (msgtype)
{
case MLD_SEND_GENQUERY: /* Send General Query */
destipaddr = g_ipv6_allnodes;
break;
case MLD_SEND_REPORT: /* Send Unsolicited report */
destipaddr = group->grpaddr;
break;
case MLD_SEND_DONE: /* Send Done message */
destipaddr = g_ipv6_allrouters;
break;
}
ninfo("destipaddr: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
destipaddr[0], destipaddr[1], destipaddr[2], destipaddr[3],
destipaddr[4], destipaddr[5], destipaddr[6], destipaddr[7]);
net_ipv6addr_hdrcopy(ipv6->destipaddr, destipaddr);
/* Add the router alert IP header option.
@ -194,7 +228,7 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
* router alert)
*/
switch (group->msgtype)
switch (msgtype)
{
case MLD_SEND_GENQUERY:
{
@ -236,6 +270,7 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
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;
@ -258,14 +293,6 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group,
MLD_STATINCR(g_netstats.mld.done_sent);
}
break;
/* Not yet supported */
case ICMPV6_MCAST_LISTEN_QUERY:
case ICMPV6_MCAST_LISTEN_REPORT_V2:
default:
DEBUGPANIC();
return;
}
MLD_STATINCR(g_netstats.icmpv6.sent);

View file

@ -96,8 +96,7 @@
* Timeout watchdog work
*
* Assumptions:
* This function is called from the wdog timer handler which runs in the
* context of the timer interrupt handler.
* This function is called from a work queue thread.
*
****************************************************************************/
@ -196,7 +195,7 @@ static void mld_timeout(int argc, uint32_t arg, ...)
group = (FAR struct mld_group_s *)arg;
DEBUGASSERT(argc == 1 && group != NULL);
/* Perform the timeout-related operations on (preferably) the low prioirity
/* Perform the timeout-related operations on (preferably) the low priority
* work queue.
*/