diff --git a/include/nuttx/net/igmp.h b/include/nuttx/net/igmp.h index 5d03f2f0724..4f513ffd9ff 100644 --- a/include/nuttx/net/igmp.h +++ b/include/nuttx/net/igmp.h @@ -81,32 +81,6 @@ #define IGMP_HDRLEN 8 #define IPIGMP_HDRLEN (IGMP_HDRLEN + IPv4_HDRLEN + 4) -/* Group flags */ - -#define IGMP_PREALLOCATED (1 << 0) -#define IGMP_LASTREPORT (1 << 1) -#define IGMP_IDLEMEMBER (1 << 2) -#define IGMP_SCHEDMSG (1 << 3) -#define IGMP_WAITMSG (1 << 4) - -#define SET_PREALLOCATED(f) do { (f) |= IGMP_PREALLOCATED; } while (0) -#define SET_LASTREPORT(f) do { (f) |= IGMP_LASTREPORT; } while (0) -#define SET_IDLEMEMBER(f) do { (f) |= IGMP_IDLEMEMBER; } while (0) -#define SET_SCHEDMSG(f) do { (f) |= IGMP_SCHEDMSG; } while (0) -#define SET_WAITMSG(f) do { (f) |= IGMP_WAITMSG; } while (0) - -#define CLR_PREALLOCATED(f) do { (f) &= ~IGMP_PREALLOCATED; } while (0) -#define CLR_LASTREPORT(f) do { (f) &= ~IGMP_LASTREPORT; } while (0) -#define CLR_IDLEMEMBER(f) do { (f) &= ~IGMP_IDLEMEMBER; } while (0) -#define CLR_SCHEDMSG(f) do { (f) &= ~IGMP_SCHEDMSG; } while (0) -#define CLR_WAITMSG(f) do { (f) &= ~IGMP_WAITMSG; } while (0) - -#define IS_PREALLOCATED(f) (((f) & IGMP_PREALLOCATED) != 0) -#define IS_LASTREPORT(f) (((f) & IGMP_LASTREPORT) != 0) -#define IS_IDLEMEMBER(f) (((f) & IGMP_IDLEMEMBER) != 0) -#define IS_SCHEDMSG(f) (((f) & IGMP_SCHEDMSG) != 0) -#define IS_WAITMSG(f) (((f) & IGMP_WAITMSG) != 0) - /* Time-to-Live must be one */ #define IGMP_TTL 1 diff --git a/include/nuttx/net/mld.h b/include/nuttx/net/mld.h index 7266329b91f..a7f2a91661a 100644 --- a/include/nuttx/net/mld.h +++ b/include/nuttx/net/mld.h @@ -90,7 +90,7 @@ #define MLD_MRC_MANT_MASK (0xfff << MLD_MRC_MANT_SHIFT) # define MLD_MRC_MANT(n) ((uint16_t)(n) << MLD_MRC_MANT_SHIFT) -/* MRD conversion (for the case of MRC >= 32768) */ +/* Conversion of MRC to MRD in milliseconds (for the case of MRC >= 32768) */ #define MLD_MRC_GETEXP(mrc) (((mrc) & MLD_MRC_EXP_MASK) >> MLD_MRC_EXP_SHIFT) #define MLD_MRC_GETMANT(mrc) (((mrc) & MLD_MRC_MANT_MASK) >> MLD_MRC_MANT_SHIFT) @@ -113,7 +113,7 @@ #define MLD_QQIC_MANT_MASK (15 << MLD_QQIC_MANT_SHIFT) # define MLD_QQIC_MANT(n) ((uint8_t)(n) << MLD_QQIC_MANT_SHIFT) -/* QQI conversion (for the case of QQIC >= 128) */ +/* Conversion of QQIC to QQI in seconds (for the case of MRC >= 128) */ #define MLD_QQIC_GETEXP(qqic) (((qqic) & MLD_QQIC_EXP_MASK) >> MLD_QQIC_EXP_SHIFT) #define MLD_QQIC_GETMANT(qqic) (((qqic) & MLD_QQIC_MANT_MASK) >> MLD_QQIC_MANT_SHIFT) @@ -165,6 +165,81 @@ * the node no longer wishes to listen * to. */ +/* MLD default values (RFC 3810) of tunable parameters. + * + * MLD_ROBUSTNESS Robustness Variable. The Robustness Variable + * allows tuning for the expected packet loss on + * a link. + * MLD_QUERY_MSEC Query Interval. The Query Interval variable + * denotes the interval between General Queries + * sent by the Querier. + * MLD_QRESP_MSEC Query Response Interval. The Maximum Response + * Delay used to calculate the Maximum Response + * Code inserted into the periodic General Queries. + * MLD_MCASTLISTEN_MSEC Multicast Address Listening Interval. The + * amount of time that must pass before a multicast + * router decides there are no more listeners of a + * multicast address or a particular source on a + * link. + * MLD_OQUERY_MSEC Other Querier Present Timeout. The length of + * time that must pass before a multicast router + * decides that there is no longer another multicast + * router which should be the Querier. + * MLD_STARTUP_MSEC Startup Query Interval. The interval between + * General Queries sent by a Querier on startup. + * MLD_STARTUP_COUNT Startup Query Count. The number of Queries sent + * out on startup separated by the Startup Query + * Interval. + * MLD_LASTLISTEN_MSEC Last Listener Query Interval. The Maximum + * Response Delay used to calculate the Maximum + * Response Code inserted into Multicast Address + * Specific Queries sent in response to Version 1 + * Multicast Listener Done messages. It is also + * the Maximum Response Delay used to calculate + * the Maximum Response Code inserted into Multicast + * Address and Source Specific Query messages. + * MLD_LASTLISTEN_COUNT Last Listener Query Count. The number of + * Multicast Address Specific Queries sent before + * the router assumes there are no local listeners. + * The Last Listener Query Count is also the number + * of Multicast Address and Source Specific Queries + * sent before the router assumes there are no + * listeners for a particular source. + * MLD_LLQUERY_MSEC Last Listener Query Time. The time value + * represented by the Last Listener Query Interval + * multiplied by Last Listener Query Count. + * MLD_UNSOLREPORT_MSEC Unsolicited Report Interval. The time between + * repetitions of a node's initial report of + * interest in a multicast address. + * MLD_V1PRESENT_MSEC(m) Older Version Querier Present Timeout. The time- + * out for transitioning a host back to MLDv2 Host + * Compatibility Mode. 'm' is the Query Interval + * in the last Query received in units of msec. + * MLD_V1HOST_MSEC Older Version Host Present Timeout. The time-out + * for transitioning a router back to MLDv2 Multicast + * Address Compatibility Mode for a specific multicast + * address. When an MLDv1 report is received for that + * multicast address, routers set their Older Version + * Host Present Timer to the Older Version Host Present + * Timeout. + */ + +#define MLD_ROBUSTNESS (2) +#define MLD_QUERY_MSEC (125 * 1000) +#define MLD_QRESP_SEC (10) +#define MLD_QRESP_MSEC (MLD_QRESP_SEC * 1000) +#define MLD_MCASTLISTEN_MSEC (MLD_ROBUSTNESS * MLD_QUERY_MSEC + MLD_QRESP_MSEC) +#define MLD_OQUERY_MSEC (MLD_ROBUSTNESS * MLD_QUERY_MSEC + (MLD_QRESP_MSEC / 2)) +#define MLD_STARTUP_MSEC (MLD_QUERY_MSEC / 4) +#define MLD_STARTUP_COUNT MLD_ROBUSTNESS +#define MLD_LASTLISTEN_MSEC (1000) +#define MLD_LASTLISTEN_COUNT MLD_ROBUSTNESS +#define MLD_LLQUERY_MSEC (MLD_LASTLISTEN_MSEC * MLD_LASTLISTEN_COUNT) +#define MLD_UNSOLREPORT_MSEC (1000) +#define MLD_UNSOLREPORT_COUNT MLD_ROBUSTNESS +#define MLD_V1PRESENT_MSEC(m) (MLD_ROBUSTNESS * (m) + MLD_QRESP_MSEC) +#define MLD_V1HOST_MSEC (MLD_ROBUSTNESS * MLD_QUERY_MSEC + MLD_QRESP_MSEC) + /**************************************************************************** * Public Type Definitions ****************************************************************************/ @@ -203,7 +278,7 @@ struct mld_mcast_listen_query_s uint16_t reserved2; /* Reserved, must be zero on transmission */ net_ipv6addr_t grpaddr; /* 128-bit IPv6 multicast group address */ uint8_t flags; /* See S and QRV flag definitions */ - uint8_t qqic; /* Querier's Query Interval Cod */ + uint8_t qqic; /* Querier's Query Interval Code */ uint16_t nsources; /* Number of sources that follow */ net_ipv6addr_t srcaddr[1]; /* Array of source IPv6 address (actual size is * nsources) */ @@ -212,8 +287,8 @@ struct mld_mcast_listen_query_s /* The actual size of the query structure depends on the number of sources */ #define SIZEOF_MLD_MCAST_LISTEN_QUERY_S(nsources) \ - (sizeof(struct mld_multicast_listener_query_s) + \ - sizeof(net_ipv6addr_t) * ((nsources) - 1) + (sizeof(struct mld_mcast_listen_query_s) + \ + sizeof(net_ipv6addr_t) * ((nsources) - 1)) /* Multicast Listener Reports are sent by IP nodes to report (to neighboring * routers) the current multicast listening state, or changes in the @@ -297,10 +372,12 @@ struct mld_stats_s { net_stats_t joins; /* Requests to join a group */ net_stats_t leaves; /* Requests to leave a group */ - net_stats_t report_sched; /* Version 1 REPORT packets scheduled */ - net_stats_t done_sched; /* Version 1 DONE packets scheduled */ - net_stats_t report_sent; /* Version 1 REPORT packets sent */ - net_stats_t done_sent; /* Version 1 DONE packets sent */ + net_stats_t query_sched; /* General QUERY packets scheduled */ + net_stats_t report_sched; /* Unsolicited REPORT packets scheduled */ + net_stats_t done_sched; /* DONE packets scheduled */ + net_stats_t query_sent; /* General QUERY packets sent */ + net_stats_t report_sent; /* Unsolicited REPORT packets sent */ + net_stats_t done_sent; /* DONE packets sent */ net_stats_t gmq_query_received; /* General multicast QUERY received */ net_stats_t mas_query_received; /* Multicast Address Specific QUERY received */ net_stats_t massq_query_received; /* Multicast Address and Source Specific QUERY received */ diff --git a/net/igmp/igmp.h b/net/igmp/igmp.h index 1fbb197f1ec..69eae36ac77 100644 --- a/net/igmp/igmp.h +++ b/net/igmp/igmp.h @@ -81,6 +81,32 @@ #ifdef CONFIG_NET_IGMP +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Group flags */ + +#define IGMP_IDLEMEMBER (1 << 0) +#define IGMP_LASTREPORT (1 << 1) +#define IGMP_SCHEDMSG (1 << 2) +#define IGMP_WAITMSG (1 << 3) + +#define SET_IDLEMEMBER(f) do { (f) |= IGMP_IDLEMEMBER; } while (0) +#define SET_LASTREPORT(f) do { (f) |= IGMP_LASTREPORT; } while (0) +#define SET_SCHEDMSG(f) do { (f) |= IGMP_SCHEDMSG; } while (0) +#define SET_WAITMSG(f) do { (f) |= IGMP_WAITMSG; } while (0) + +#define CLR_IDLEMEMBER(f) do { (f) &= ~IGMP_IDLEMEMBER; } while (0) +#define CLR_LASTREPORT(f) do { (f) &= ~IGMP_LASTREPORT; } while (0) +#define CLR_SCHEDMSG(f) do { (f) &= ~IGMP_SCHEDMSG; } while (0) +#define CLR_WAITMSG(f) do { (f) &= ~IGMP_WAITMSG; } while (0) + +#define IS_IDLEMEMBER(f) (((f) & IGMP_IDLEMEMBER) != 0) +#define IS_LASTREPORT(f) (((f) & IGMP_LASTREPORT) != 0) +#define IS_SCHEDMSG(f) (((f) & IGMP_SCHEDMSG) != 0) +#define IS_WAITMSG(f) (((f) & IGMP_WAITMSG) != 0) + /**************************************************************************** * Public Type Definitions ****************************************************************************/ diff --git a/net/mld/mld.h b/net/mld/mld.h index 3cfe7171700..53d1071b86d 100644 --- a/net/mld/mld.h +++ b/net/mld/mld.h @@ -123,27 +123,31 @@ /* Group flags */ -#define MLD_PREALLOCATED (1 << 0) -#define MLD_LASTREPORT (1 << 1) -#define MLD_IDLEMEMBER (1 << 2) -#define MLD_SCHEDMSG (1 << 3) -#define MLD_WAITMSG (1 << 4) +#define MLD_QUERIER (1 << 0) /* Querier */ +#define MLD_STARTUP (1 << 2) /* Startup unsolicited Reports */ +#define MLD_V1COMPAT (1 << 3) /* Version 1 compatibility mode */ +#define MLD_LASTREPORT (1 << 3) /* We were the last to report */ +#define MLD_SCHEDMSG (1 << 4) /* Outgoing message scheduled */ +#define MLD_WAITMSG (1 << 5) /* Block until message sent */ -#define SET_MLD_PREALLOCATED(f) do { (f) |= MLD_PREALLOCATED; } while (0) +#define SET_MLD_QUERIER(f) do { (f) |= MLD_QUERIER; } while (0) +#define SET_MLD_STARTUP(f) do { (f) |= MLD_STARTUP; } while (0) +#define SET_MLD_V1COMPAT(f) do { (f) |= MLD_V1COMPAT; } while (0) #define SET_MLD_LASTREPORT(f) do { (f) |= MLD_LASTREPORT; } while (0) -#define SET_MLD_IDLEMEMBER(f) do { (f) |= MLD_IDLEMEMBER; } while (0) #define SET_MLD_SCHEDMSG(f) do { (f) |= MLD_SCHEDMSG; } while (0) #define SET_MLD_WAITMSG(f) do { (f) |= MLD_WAITMSG; } while (0) -#define CLR_MLD_PREALLOCATED(f) do { (f) &= ~MLD_PREALLOCATED; } while (0) +#define CLR_MLD_QUERIER(f) do { (f) &= ~MLD_QUERIER; } while (0) +#define CLR_MLD_STARTUP(f) do { (f) &= ~MLD_STARTUP; } while (0) +#define CLR_MLD_V1COMPAT(f) do { (f) &= ~MLD_V1COMPAT; } while (0) #define CLR_MLD_LASTREPORT(f) do { (f) &= ~MLD_LASTREPORT; } while (0) -#define CLR_MLD_IDLEMEMBER(f) do { (f) &= ~MLD_IDLEMEMBER; } while (0) #define CLR_MLD_SCHEDMSG(f) do { (f) &= ~MLD_SCHEDMSG; } while (0) #define CLR_MLD_WAITMSG(f) do { (f) &= ~MLD_WAITMSG; } while (0) -#define IS_MLD_PREALLOCATED(f) (((f) & MLD_PREALLOCATED) != 0) +#define IS_MLD_QUERIER(f) (((f) & MLD_QUERIER) != 0) +#define IS_MLD_STARTUP(f) (((f) & MLD_STARTUP) != 0) +#define IS_MLD_V1COMPAT(f) (((f) & MLD_V1COMPAT) != 0) #define IS_MLD_LASTREPORT(f) (((f) & MLD_LASTREPORT) != 0) -#define IS_MLD_IDLEMEMBER(f) (((f) & MLD_IDLEMEMBER) != 0) #define IS_MLD_SCHEDMSG(f) (((f) & MLD_SCHEDMSG) != 0) #define IS_MLD_WAITMSG(f) (((f) & MLD_WAITMSG) != 0) @@ -151,6 +155,18 @@ * Public Type Definitions ****************************************************************************/ +/* These are the types of messages that may be sent in response to a device + * poll. + */ + +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 */ +}; + /* This structure represents one group member. There is a list of groups * for each device interface structure. * @@ -167,7 +183,8 @@ struct mld_group_s WDOG_ID wdog; /* WDOG used to detect timeouts */ sem_t sem; /* Used to wait for message transmission */ volatile uint8_t flags; /* See MLD_ flags definitions */ - uint8_t msgid; /* Pending message ID (if non-zero) */ + uint8_t msgtype; /* Pending message type to send (if non-zero) */ + uint8_t count; /* Report repetition count */ }; /**************************************************************************** @@ -315,7 +332,7 @@ void mld_grpfree(FAR struct net_driver_s *dev, * ****************************************************************************/ -void mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgid); +void mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgtype); /**************************************************************************** * Name: mld_waitmsg @@ -326,7 +343,7 @@ void mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgid); * ****************************************************************************/ -void mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgid); +void mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgtype); /**************************************************************************** * Name: mld_poll @@ -407,15 +424,14 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec); int mld_leavegroup(FAR const struct ipv6_mreq *mrec); /**************************************************************************** - * Name: mld_startticks and mld_starttimer + * Name: mld_starttimer * * Description: * Start the MLD timer with differing time units (ticks or deciseconds). * ****************************************************************************/ -void mld_startticks(FAR struct mld_group_s *group, unsigned int ticks); -void mld_starttimer(FAR struct mld_group_s *group, uint8_t decisecs); +void mld_starttimer(FAR struct mld_group_s *group, clock_t ticks); /**************************************************************************** * Name: mld_cmptimer @@ -425,7 +441,7 @@ void mld_starttimer(FAR struct mld_group_s *group, uint8_t decisecs); * 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_startticks() to + * If true is returned then the caller must call mld_starttimer() to * restart the timer * ****************************************************************************/ diff --git a/net/mld/mld_done.c b/net/mld/mld_done.c index 7443c270cd9..232e8d3763e 100644 --- a/net/mld/mld_done.c +++ b/net/mld/mld_done.c @@ -46,6 +46,12 @@ #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)]) + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -54,15 +60,52 @@ * Name: mld_done_v1 * * Description: - * Called from icmpv6_input() when a Version 1 Multicast Listener Done is - * received. + * Called from icmpv6_input() when a Version 1 Multicast Listener Done is + * received. + * + * When a router in Querier state receives a Done message from a link, + * if the Multicast Address identified in the message is present in the + * Querier's list of addresses having listeners on that link, the Querier + * periodically sends multiple Multicast-Address-Specific Queries to that + * multicast address. If no Reports for the address are received from the + * link after the maximum response delay in the Multicast-Address-Specific + * Queries of the last query has passed, the routers on the link assume + * that the address no longer has any listeners there; the address is + * therefore deleted from the list and its disappearance is made known to + * the multicast routing component. + * + * Routers in Non-Querier state MUST ignore Done messages. * ****************************************************************************/ int mld_done_v1(FAR struct net_driver_s *dev, FAR const struct mld_mcast_listen_done_v1_s *done) { + FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; + FAR struct mld_group_s *group; + MLD_STATINCR(g_netstats.mld.done_received); -#warning Missing logic - return -ENOSYS; + + /* Find the group (or create a new one) using the incoming IP address */ + + group = mld_grpfind(dev, ipv6->destipaddr); + if (group == NULL) + { + return -ENOENT; /* REVISIT: Or should it return OK? */ + } + + /* Ignore the Done message is this is not a Querier */ + + if (IS_MLD_QUERIER(group->flags)) + { + /* REVISIT: Here we just remove the group from this list immediately. + * The RFC requires that we send Multicast-Address-Specific Queries + * repeatedly before doing this to assure that the listener is not + * present. + */ + + mld_grpfree(dev, group); + } + + return OK; } diff --git a/net/mld/mld_group.c b/net/mld/mld_group.c index 8583cc7ed50..4225aaf855c 100644 --- a/net/mld/mld_group.c +++ b/net/mld/mld_group.c @@ -132,7 +132,11 @@ FAR struct mld_group_s *mld_grpalloc(FAR struct net_driver_s *dev, group->wdog = wd_create(); DEBUGASSERT(group->wdog); - /* Interrupts must be disabled in order to modify the group list */ + /* All routers start up as a Querier on each of their attached links. */ + + SET_MLD_QUERIER(group->flags); + + /* The network must be locked in order to modify the group list */ net_lock(); diff --git a/net/mld/mld_join.c b/net/mld/mld_join.c index c6c50213733..bf7d04942a7 100644 --- a/net/mld/mld_join.c +++ b/net/mld/mld_join.c @@ -105,34 +105,7 @@ * * State transition diagram for a router in Non-Querier state is * similar, but non-Queriers do not send any messages and are only - * driven by message reception. - * - * ________________ - * | | - * | | - * timer expired| |timer expired - * (notify routing -)| No Listeners |(notify routing -) - * --------->| Present |<--------- - * | | | | - * | | | | - * | | | | - * | |________________| | - * | | | - * | |report received | - * | |(notify routing +,| - * | | start timer) | - * ________|________ | ________|________ - * | |<--------- | | - * | | report received | | - * | | (start timer) | | - * | Listeners |<-------------------| Checking | - * | Present | m-a-s query rec'd | Listeners | - * | | (start timer*) | | - * ---->| |------------------->| | - * | |_________________| |_________________| - * | report received | - * | (start timer) | - * ----------------- + * driven by message reception (See RFC 2710/3810 or net/mld.h). * ****************************************************************************/ @@ -193,9 +166,11 @@ int mld_joingroup(FAR const struct ipv6_mreq *mrec) MLD_STATINCR(g_netstats.mld.report_sched); mld_waitmsg(group, ICMPV6_MCAST_LISTEN_REPORT_V1); - /* And start the timer at 10*100 msec */ + /* And start the timer at 1 second */ - mld_starttimer(group, 10); + SET_MLD_STARTUP(group->flags); + group->count = MLD_UNSOLREPORT_COUNT - 1; + mld_starttimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC)); /* Add the group (MAC) address to the Ethernet drivers MAC filter list */ diff --git a/net/mld/mld_leave.c b/net/mld/mld_leave.c index cb96044c095..166ee31d8b9 100644 --- a/net/mld/mld_leave.c +++ b/net/mld/mld_leave.c @@ -107,34 +107,7 @@ * * State transition diagram for a router in Non-Querier state is * similar, but non-Queriers do not send any messages and are only - * driven by message reception. - * - * ________________ - * | | - * | | - * timer expired| |timer expired - * (notify routing -)| No Listeners |(notify routing -) - * --------->| Present |<--------- - * | | | | - * | | | | - * | | | | - * | |________________| | - * | | | - * | |report received | - * | |(notify routing +,| - * | | start timer) | - * ________|________ | ________|________ - * | |<--------- | | - * | | report received | | - * | | (start timer) | | - * | Listeners |<-------------------| Checking | - * | Present | m-a-s query rec'd | Listeners | - * | | (start timer*) | | - * ---->| |------------------->| | - * | |_________________| |_________________| - * | report received | - * | (start timer) | - * ----------------- + * driven by message reception (See RFC 2710/3810 or net/mld.h). * ****************************************************************************/ diff --git a/net/mld/mld_msg.c b/net/mld/mld_msg.c index 1183ac2a72d..ad22706347b 100644 --- a/net/mld/mld_msg.c +++ b/net/mld/mld_msg.c @@ -64,13 +64,13 @@ * ****************************************************************************/ -void mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgid) +void mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgtype) { /* The following should be atomic */ net_lock(); DEBUGASSERT(!IS_MLD_SCHEDMSG(group->flags)); - group->msgid = msgid; + group->msgtype = msgtype; SET_MLD_SCHEDMSG(group->flags); net_unlock(); } @@ -84,7 +84,7 @@ void mld_schedmsg(FAR struct mld_group_s *group, uint8_t msgid) * ****************************************************************************/ -void mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgid) +void mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgtype) { int ret; @@ -93,7 +93,7 @@ void mld_waitmsg(FAR struct mld_group_s *group, uint8_t msgid) net_lock(); DEBUGASSERT(!IS_MLD_WAITMSG(group->flags)); SET_MLD_WAITMSG(group->flags); - mld_schedmsg(group, msgid); + mld_schedmsg(group, msgtype); /* Then wait for the message to be sent */ diff --git a/net/mld/mld_poll.c b/net/mld/mld_poll.c index 50be5d2fecb..d1fc23a1912 100644 --- a/net/mld/mld_poll.c +++ b/net/mld/mld_poll.c @@ -69,40 +69,47 @@ ****************************************************************************/ static inline void mld_sched_send(FAR struct net_driver_s *dev, - FAR struct mld_group_s *group) + FAR struct mld_group_s *group) { const net_ipv6addr_t *dest; - /* Check what kind of message we need to send. There are only two + /* Check what kind of message we need to send. There are only three * possibilities: */ - if (group->msgid == ICMPV6_MCAST_LISTEN_REPORT_V1) + 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 ICMPV6_MCAST_LISTEN_REPORT_V1, flags=%02x\n", group->flags); + 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]); - MLD_STATINCR(g_netstats.mld.report_sched); SET_MLD_LASTREPORT(group->flags); /* Remember we were the last to report */ } else { - DEBUGASSERT(group->msgid == ICMPV6_MCAST_LISTEN_DONE_V1); + DEBUGASSERT(group->msgtype == MLD_SEND_DONE); dest = &g_ipv6_allrouters; - ninfo("Send ICMPV6_MCAST_LISTEN_DONE_V1, flags=%02x\n", group->flags); + 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]); - - MLD_STATINCR(g_netstats.mld.done_sched); } /* Send the message */ @@ -112,7 +119,7 @@ static inline void mld_sched_send(FAR struct net_driver_s *dev, /* Indicate that the message has been sent */ CLR_MLD_SCHEDMSG(group->flags); - group->msgid = 0; + group->msgtype = 0; /* If there is a thread waiting fore the message to be sent, wake it up */ diff --git a/net/mld/mld_query.c b/net/mld/mld_query.c index 9c0a96de943..9f5a693a9ab 100644 --- a/net/mld/mld_query.c +++ b/net/mld/mld_query.c @@ -57,6 +57,109 @@ #define IPv6BUF ((FAR struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mld_mrc2mrd + * + * Description: + * Convert the MLD Maximum Response Code (MRC) to the Maximum Response + * Delay (MRD) in units of system clock ticks. + * + ****************************************************************************/ + +static clock_t mld_mrc2mrd(uint16_t mrc) +{ + uint32_t mrd; /* Units of milliseconds */ + + /* If bit 15 is not set (i.e., mrc < 32768), then no conversion is required. */ + + if (mrc < 32768) + { + mrd = mrc; + } + else + { + /* Conversion required */ + + mrd = MLD_MRD_VALUE(mrc); + } + + /* Return the MRD in units of clock ticks */ + + return MSEC2TICK((clock_t)mrd); +} + +/**************************************************************************** + * Name: mld_cmpaddr + * + * 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. + * + ****************************************************************************/ + +static bool mld_cmpaddr(FAR struct net_driver_s *dev, + const net_ipv6addr_t srcaddr) +{ + int i; + + for (i = 0; i < 8; i++) + { + if (srcaddr[i] < dev->d_ipv6addr[i]) + { + return true; + } + } + + return false; +} + +/**************************************************************************** + * 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. + * + ****************************************************************************/ + +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) +{ + clock_t ticks; + + /* Check if this member is a Querier */ + + if (IS_MLD_QUERIER(member->flags)) + { + /* This is a querier, check if the IPv6 source address is numerically + * less than the IPv6 address assigned to this link. + */ + + 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. + */ + + ticks = mld_mrc2mrd(mrc); + if (mld_cmptimer(member, ticks)) + { + mld_starttimer(member, ticks); + CLR_MLD_QUERIER(member->flags); + } + } + } +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -65,7 +168,22 @@ * Name: mld_query * * Description: - * Called from icmpv6_input() when a Multicast Listener Query is received. + * Called from icmpv6_input() when a Multicast Listener Query is received. + * + * A router may assume one of two roles: Querier or Non-Querier. There is + * normally only one Querier per link. All routers start up as a Querier + * on each of their attached links. If a router hears a Query message + * whose IPv6 Source Address is numerically less than its own selected + * address for that link, it MUST become a Non-Querier on that link. If a + * delay passes without receiving, from a particular attached link, any + * Queries from a router with an address less than its own, a router + * resumes the role of Querier on that link. + * + * A Querier for a link periodically sends a General Query on that link, + * to solicit reports of all multicast addresses of interest on that link. + * On startup, a router SHOULD send multiple General Queries spaced closely + * together Interval] on all attached links in order to quickly and + * reliably discover the presence of multicast listeners on those links. * ****************************************************************************/ @@ -74,7 +192,7 @@ int mld_query(FAR struct net_driver_s *dev, { FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; FAR struct mld_group_s *group; - unsigned int ticks; + uint16_t mrc; bool unspec; ninfo("Multicast Listener Query\n"); @@ -101,6 +219,7 @@ int mld_query(FAR struct net_driver_s *dev, /* Check if the query was sent to all systems */ unspec = net_ipv6addr_cmp(query->grpaddr, g_ipv6_unspecaddr); + mrc = NTOHS(query->mrc); if (net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_allnodes)) { @@ -141,40 +260,41 @@ int mld_query(FAR struct net_driver_s *dev, if (!net_ipv6addr_cmp(member->grpaddr, g_ipv6_allnodes)) { - ticks = net_dsec2tick((int)query->mrc); - if (IS_MLD_IDLEMEMBER(member->flags) || - mld_cmptimer(member, ticks)) - { - mld_startticks(member, ticks); - CLR_MLD_IDLEMEMBER(member->flags); - } + /* REVISIT: Missing logic. No action is taken on the query. */ + + mld_check_querier(dev, ipv6, member, mrc); } } } else if (!unspec && query->nsources == 0) { ninfo("Multicast Address Specific Query\n"); - - /* We first need to re-lookup the group since we used dest last time. - * Use the incoming IPaddress! - */ - MLD_STATINCR(g_netstats.mld.mas_query_received); - group = mld_grpallocfind(dev, query->grpaddr); - ticks = net_dsec2tick((int)query->mrc); + /* We first need to re-lookup the group since we used the incoming + * dest last time. Use the group address in the query. + */ - if (IS_MLD_IDLEMEMBER(group->flags) || mld_cmptimer(group, ticks)) - { - mld_startticks(group, ticks); - CLR_MLD_IDLEMEMBER(group->flags); - } + 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); -#warning Missing logic + + /* 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); } } @@ -185,14 +305,7 @@ int mld_query(FAR struct net_driver_s *dev, ninfo("Unicast query\n"); MLD_STATINCR(g_netstats.mld.ucast_query_received); - ninfo("Query to a specific group with the group address as destination\n"); - - ticks = net_dsec2tick((int)query->mrc); - if (IS_MLD_IDLEMEMBER(group->flags) || mld_cmptimer(group, ticks)) - { - mld_startticks(group, ticks); - CLR_MLD_IDLEMEMBER(group->flags); - } + mld_check_querier(dev, ipv6, group, mrc); } return OK; diff --git a/net/mld/mld_report.c b/net/mld/mld_report.c index 0c48a00400f..610cc6aca2d 100644 --- a/net/mld/mld_report.c +++ b/net/mld/mld_report.c @@ -65,6 +65,22 @@ * Called from icmpv6_input() when a Version 1 or Version 2 Multicast * Listener Report is received. * + * If a node receives another node's Report from an interface for a + * multicast address while it has a timer running for that same address + * on that interface, it stops its timer and does not send a Report for + * that address, thus suppressing duplicate reports on the link. + * + * When a router receives a Report from a link, if the reported address + * is not already present in the router's list of multicast address + * having listeners on that link, the reported address is added to the + * list, its timer is set, and its appearance is made known to the router's + * multicast routing component. If a Report is received for a multicast + * address that is already present in the router's list, the timer for that + * address is reset. If an address's timer expires, it is assumed that + * there are no longer any listeners for that address present on the link, + * so it is deleted from the list and its disappearance is made known to + * the multicast routing component. + * ****************************************************************************/ int mld_report_v1(FAR struct net_driver_s *dev, @@ -90,12 +106,11 @@ int mld_report_v1(FAR struct net_driver_s *dev, return -ENOENT; } - if (!IS_MLD_IDLEMEMBER(group->flags)) - { - /* This is on a specific group we have already looked up */ + /* If we are a Querier, then reset the timer for that group. */ - wd_cancel(group->wdog); - SET_MLD_IDLEMEMBER(group->flags); + if (IS_MLD_QUERIER(group->flags)) + { + mld_starttimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC)); CLR_MLD_LASTREPORT(group->flags); } @@ -106,9 +121,16 @@ int mld_report_v1(FAR struct net_driver_s *dev, * Name: mld_report_v2 * * Description: - * Called from icmpv6_input() when a Version 2 Multicast Listener Report is + * Called from icmpv6_input() when a Version 2 Multicast Listener Report is * received. * + * Upon reception of an MLD message that contains a Report, the router + * checks if the source address of the message is a valid link-local + * address, if the Hop Limit is set to 1, and if the Router Alert option + * is present in the Hop-By-Hop Options header of the IPv6 packet. If + * 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. + * ****************************************************************************/ int mld_report_v2(FAR struct net_driver_s *dev, @@ -125,7 +147,25 @@ int mld_report_v2(FAR struct net_driver_s *dev, MLD_STATINCR(g_netstats.mld.v2report_received); - /* Find the group (or create a new one) using the incoming IP address */ + /* 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) @@ -134,12 +174,11 @@ int mld_report_v2(FAR struct net_driver_s *dev, return -ENOENT; } - if (!IS_MLD_IDLEMEMBER(group->flags)) - { - /* This is on a specific group we have already looked up */ + /* If we are a Querier, then reset the timer for that group. */ - wd_cancel(group->wdog); - SET_MLD_IDLEMEMBER(group->flags); + if (IS_MLD_QUERIER(group->flags)) + { + mld_starttimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC)); CLR_MLD_LASTREPORT(group->flags); } diff --git a/net/mld/mld_send.c b/net/mld/mld_send.c index 444bb0b2684..7149d4e49ee 100644 --- a/net/mld/mld_send.c +++ b/net/mld/mld_send.c @@ -80,6 +80,8 @@ #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 *) \ @@ -117,7 +119,7 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, FAR struct ipv6_router_alert_s *ra; unsigned int mldsize; - ninfo("msgid: %02x \n", group->msgid); + 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]); @@ -131,20 +133,20 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, * This will change. */ - switch (group->msgid) + switch (group->msgtype) { - case ICMPV6_MCAST_LISTEN_REPORT_V1: + case MLD_SEND_GENQUERY: /* Send General Query */ + mldsize = SIZEOF_MLD_MCAST_LISTEN_QUERY_S(0); + break; + + case MLD_SEND_REPORT: /* Send Unsolicited report */ mldsize = sizeof(struct mld_mcast_listen_report_v1_s); break; - case ICMPV6_MCAST_LISTEN_DONE_V1: + case MLD_SEND_DONE: /* Send Done message */ mldsize = sizeof(struct mld_mcast_listen_done_v1_s); break; - /* Not yet supported */ - - case ICMPV6_MCAST_LISTEN_QUERY: - case ICMPV6_MCAST_LISTEN_REPORT_V2: default: DEBUGPANIC(); return; @@ -192,17 +194,42 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, * router alert) */ - switch (group->msgid) + switch (group->msgtype) { - case ICMPV6_MCAST_LISTEN_REPORT_V1: + case MLD_SEND_GENQUERY: + { + FAR struct mld_mcast_listen_query_s *query = QUERYBUF; + + /* Initializer the Query payload. In a General Query, both the + * Multicast Address field and the Number of Sources (N) + * field are zero. + */ + + 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; + + /* Calculate the ICMPv6 checksum. */ + + query->chksum = 0; + query->chksum = ~icmpv6_chksum(dev); + + MLD_STATINCR(g_netstats.mld.query_sent); + } + break; + + case MLD_SEND_REPORT: { FAR struct mld_mcast_listen_report_v1_s *report = REPORTBUF; /* Initializer the Report payload */ memset(report, 0, sizeof(struct mld_mcast_listen_report_v1_s)); - report->type = ICMPV6_MCAST_LISTEN_REPORT_V1; net_ipv6addr_hdrcopy(report->mcastaddr, &group->grpaddr); + report->type = ICMPV6_MCAST_LISTEN_REPORT_V1; /* Calculate the ICMPv6 checksum. */ @@ -213,7 +240,7 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct mld_group_s *group, } break; - case ICMPV6_MCAST_LISTEN_DONE_V1: + case MLD_SEND_DONE: { FAR struct mld_mcast_listen_done_v1_s *done = DONEBUF; diff --git a/net/mld/mld_timer.c b/net/mld/mld_timer.c index c5406cc951b..e3714775fa9 100644 --- a/net/mld/mld_timer.c +++ b/net/mld/mld_timer.c @@ -60,27 +60,27 @@ /* Debug ********************************************************************/ -#undef MLD_GTMRDEBUG /* Define to enable detailed MLD group debug */ +#undef MLD_MTMRDEBUG /* Define to enable detailed MLD group debug */ #ifndef CONFIG_NET_MLD -# undef MLD_GTMRDEBUG +# undef MLD_MTMRDEBUG #endif #ifdef CONFIG_CPP_HAVE_VARARGS -# ifdef MLD_GTMRDEBUG -# define gtmrerr(format, ...) nerr(format, ##__VA_ARGS__) -# define gtmrinfo(format, ...) ninfo(format, ##__VA_ARGS__) +# ifdef MLD_MTMRDEBUG +# define mtmrerr(format, ...) nerr(format, ##__VA_ARGS__) +# define mtmrinfo(format, ...) ninfo(format, ##__VA_ARGS__) # else -# define gtmrerr(x...) -# define gtmrinfo(x...) +# define mtmrerr(x...) +# define mtmrinfo(x...) # endif #else -# ifdef MLD_GTMRDEBUG -# define gtmrerr nerr -# define gtmrinfo ninfo +# ifdef MLD_MTMRDEBUG +# define mtmrerr nerr +# define mtmrinfo ninfo # else -# define gtmrerr (void) -# define gtmrinfo (void) +# define mtmrerr (void) +# define mtmrinfo (void) # endif #endif @@ -110,30 +110,53 @@ static void mld_timeout(int argc, uint32_t arg, ...) group = (FAR struct mld_group_s *)arg; DEBUGASSERT(argc == 1 && group); - /* If the group exists and is no an IDLE MEMBER, then it must be a DELAYING - * member. Race conditions are avoided because (1) the timer is not started - * until after the first MLDv2_MEMBERSHIP_REPORT during the join, and (2) - * the timer is canceled before sending the ICMPV6_MCAST_LISTEN_DONE_V1 - * during a leave. - */ + /* Check if this a new join to the multicast group. */ - if (!IS_MLD_IDLEMEMBER(group->flags)) + if (IS_MLD_STARTUP(group->flags)) { - /* Schedule (and forget) the Report. NOTE: Since we are executing - * from a timer interrupt, we cannot wait for the message to be sent. - */ + /* Schedule (and forget) the Report. */ MLD_STATINCR(g_netstats.mld.report_sched); - mld_schedmsg(group, ICMPV6_MCAST_LISTEN_REPORT_V1); + mld_schedmsg(group, MLD_SEND_REPORT); - /* Also note: The Report is sent at most two times because the timer - * is not reset here. Hmm.. does this mean that the group is stranded - * if both reports were lost? This is consistent with the - * RFC that states: "To cover the possibility of the initial Report - * being lost or damaged, it is recommended that it be repeated once - * or twice after short delays [Unsolicited Report Interval]...." - * (RFC 2710). + /* Send the report until the unsolicited report count goes to zero + * then terminate the start-up sequence. */ + + if (group->count > 1) + { + /* Decrement the count and restart the timer */ + + group->count--; + mld_starttimer(group, MSEC2TICK(MLD_UNSOLREPORT_MSEC)); + } + else + { + /* Terminate the start-up sequence */ + + CLR_MLD_STARTUP(group->flags); + + /* If in Querier mode, start the Querier timer */ + + if (IS_MLD_QUERIER(group->flags)) + { + mld_starttimer(group, MSEC2TICK(MLD_QUERY_MSEC)); + } + } + } + + /* Check if this is querier */ + + else if (IS_MLD_QUERIER(group->flags)) + { + /* Schedule (and forget) the general query. */ + + MLD_STATINCR(g_netstats.mld.query_sched); + mld_schedmsg(group, MLD_SEND_GENQUERY); + + /* Restart the Querier timer */ + + mld_starttimer(group, MSEC2TICK(MLD_QUERY_MSEC)); } } @@ -142,23 +165,23 @@ static void mld_timeout(int argc, uint32_t arg, ...) ****************************************************************************/ /**************************************************************************** - * Name: mld_startticks and mld_starttimer + * Name: mld_starttimer * * Description: - * Start the MLD timer with differing time units (ticks or deciseconds). + * Start the MLD timer. * * Assumptions: * This function may be called from most any context. * ****************************************************************************/ -void mld_startticks(FAR struct mld_group_s *group, unsigned int ticks) +void mld_starttimer(FAR struct mld_group_s *group, clock_t ticks) { int ret; /* Start the timer */ - gtmrinfo("ticks: %d\n", ticks); + mtmrinfo("ticks: %ld\n", (unsigned long)ticks); ret = wd_start(group->wdog, ticks, mld_timeout, 1, (uint32_t)group); @@ -166,16 +189,6 @@ void mld_startticks(FAR struct mld_group_s *group, unsigned int ticks) UNUSED(ret); } -void mld_starttimer(FAR struct mld_group_s *group, uint8_t decisecs) -{ - /* Convert the decisec value to system clock ticks and start the timer. - * Important!! this should be a random timer from 0 to decisecs - */ - - gtmrinfo("decisecs: %d\n", decisecs); - mld_startticks(group, net_dsec2tick(decisecs)); -} - /**************************************************************************** * Name: mld_cmptimer * @@ -186,7 +199,7 @@ void mld_starttimer(FAR struct mld_group_s *group, uint8_t decisecs) * * Assumptions: * This function may be called from most any context. If true is returned - * then the caller must call mld_startticks() to restart the timer + * then the caller must call mld_starttimer() to restart the timer * ****************************************************************************/ @@ -212,7 +225,7 @@ bool mld_cmptimer(FAR struct mld_group_s *group, int maxticks) * test as well. */ - gtmrinfo("maxticks: %d remaining: %d\n", maxticks, remaining); + mtmrinfo("maxticks: %d remaining: %d\n", maxticks, remaining); if (maxticks > remaining) { /* Cancel the watchdog timer and return true */