From b1a61834d914bd826ee6b2f620f19aeddccbf9c7 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 4 Nov 2018 10:57:21 -0600 Subject: [PATCH] net/mld: Review and update all MLD message receipt logic. Also ripple changs that are, hopefully, improvements back to IGMP. --- include/nuttx/net/mld.h | 1 + net/icmpv6/icmpv6_input.c | 22 ++-- net/igmp/igmp.h | 3 +- net/igmp/igmp_input.c | 104 +++++++++++++-- net/igmp/igmp_poll.c | 8 +- net/igmp/igmp_send.c | 7 +- net/mld/mld.h | 10 +- net/mld/mld_done.c | 21 ++- net/mld/mld_leave.c | 10 +- net/mld/mld_msg.c | 4 +- net/mld/mld_poll.c | 80 +++--------- net/mld/mld_query.c | 261 +++++++++++++++++++++++--------------- net/mld/mld_report.c | 135 ++++++++++---------- net/mld/mld_send.c | 67 +++++++--- net/mld/mld_timer.c | 5 +- 15 files changed, 440 insertions(+), 298 deletions(-) diff --git a/include/nuttx/net/mld.h b/include/nuttx/net/mld.h index a7f2a91661..3e16d197b4 100644 --- a/include/nuttx/net/mld.h +++ b/include/nuttx/net/mld.h @@ -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 */ diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c index 74916769fe..6ad1663bc2 100644 --- a/net/icmpv6/icmpv6_input.c +++ b/net/icmpv6/icmpv6_input.c @@ -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: diff --git a/net/igmp/igmp.h b/net/igmp/igmp.h index 2ecb5b640d..df30196483 100644 --- a/net/igmp/igmp.h +++ b/net/igmp/igmp.h @@ -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 diff --git a/net/igmp/igmp_input.c b/net/igmp/igmp_input.c index 789b806925..8e19f578a0 100644 --- a/net/igmp/igmp_input.c +++ b/net/igmp/igmp_input.c @@ -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 */ diff --git a/net/igmp/igmp_poll.c b/net/igmp/igmp_poll.c index 0cf23a8849..fe34a6b82b 100644 --- a/net/igmp/igmp_poll.c +++ b/net/igmp/igmp_poll.c @@ -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; } } diff --git a/net/igmp/igmp_send.c b/net/igmp/igmp_send.c index 8230f1e3f9..e67f8ee2d1 100644 --- a/net/igmp/igmp_send.c +++ b/net/igmp/igmp_send.c @@ -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); diff --git a/net/mld/mld.h b/net/mld/mld.h index 10e370ec12..56f715dc3a 100644 --- a/net/mld/mld.h +++ b/net/mld/mld.h @@ -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 diff --git a/net/mld/mld_done.c b/net/mld/mld_done.c index 232e8d3763..97c054a053 100644 --- a/net/mld/mld_done.c +++ b/net/mld/mld_done.c @@ -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; } diff --git a/net/mld/mld_leave.c b/net/mld/mld_leave.c index 274731f33e..633740759d 100644 --- a/net/mld/mld_leave.c +++ b/net/mld/mld_leave.c @@ -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)) { diff --git a/net/mld/mld_msg.c b/net/mld/mld_msg.c index 176f2df4e4..65e205e254 100644 --- a/net/mld/mld_msg.c +++ b/net/mld/mld_msg.c @@ -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; } diff --git a/net/mld/mld_poll.c b/net/mld/mld_poll.c index d1fc23a191..cd038f7a36 100644 --- a/net/mld/mld_poll.c +++ b/net/mld/mld_poll.c @@ -47,7 +47,6 @@ #include #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; } } diff --git a/net/mld/mld_query.c b/net/mld/mld_query.c index 9f5a693a9a..0be7992ea4 100644 --- a/net/mld/mld_query.c +++ b/net/mld/mld_query.c @@ -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; diff --git a/net/mld/mld_report.c b/net/mld/mld_report.c index 610cc6aca2..b54a1e0814 100644 --- a/net/mld/mld_report.c +++ b/net/mld/mld_report.c @@ -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); } diff --git a/net/mld/mld_send.c b/net/mld/mld_send.c index 7149d4e49e..4b2693571e 100644 --- a/net/mld/mld_send.c +++ b/net/mld/mld_send.c @@ -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); diff --git a/net/mld/mld_timer.c b/net/mld/mld_timer.c index aa2bd13353..ac36041521 100644 --- a/net/mld/mld_timer.c +++ b/net/mld/mld_timer.c @@ -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. */