nuttx/net/mld/mld_query.c
Xiang Xiao c47b1e2c5b !sys/types.h: change time_t and clock_t to int64_t to align with other OSes
POSIX leaves the signedness of time_t and clock_t unspecified, but
mainstream implementations (Linux glibc/musl, the BSDs, macOS, RTEMS,
Zephyr's POSIX layer, Windows _time64) expose time_t as signed 64-bit.
NuttX has historically used uint64_t only because it was tied to the
CONFIG_SYSTEM_TIME64 knob; with that gone, switch:

  time_t   : uint64_t  -> int64_t
  clock_t  : uint64_t  -> int64_t
  CLOCK_MAX: UINT64_MAX -> INT64_MAX

This lets (time_t)-1 sentinels, negative tick deltas, and host-side
headers behave as on every other POSIX system without source churn.

Headers updated:
  - include/sys/types.h, include/limits.h, include/nuttx/clock.h
  - include/nuttx/fs/hostfs.h (nuttx_time_t alias)
  - include/nuttx/{mqueue.h,wdog.h,wqueue.h,timers/clkcnt.h}

Because clock_t is now signed 64-bit, the NuttX-internal sclock_t
alias becomes redundant: every sclock_t/SCLOCK_MAX use is folded
back to clock_t/CLOCK_MAX (notably in sched/wdog, sched/mqueue,
sched/sched, sched/clock, sched/timer, libs/libc/time, fs/vfs and
the drivers/arch consumers below).

Tick/period constants (NSEC_PER_SEC, USEC_PER_SEC, MSEC_PER_SEC,
SEC_PER_MIN, ...) in include/nuttx/clock.h are retyped from "long"
literals to INT64_C(...) so that 64-bit arithmetic no longer
depends on the host's long width.

Strip now-redundant (time_t)/(clock_t)/(unsigned long) casts and
unsigned-only branches across the tree:
  - arch RTC / oneshot / tickless lowerhalfs:
      arm: cxd56xx, efm32, imxrt, lc823450, max326xx, sam34, sama5,
           samd5e5, samv7, stm32, stm32f7, stm32h7, stm32l4, stm32wb,
           xmc4
      mips: pic32mz       sparc: bm3803       x86_64: intel64
      risc-v/xtensa: espressif (esp_i2c[_slave], esp_rtc,
           esp32c3{_i2c,_rtc,_wifi_adapter}, esp32{,s2,s3}_*),
           mpfs_perf
  - drivers: audio/tone, input/aw86225, power/pm/{activity,
           stability}_governor, rpmsg/rpmsg_ping,
           timers/{ds3231,mcp794xx,pcf85263,rx8010},
           wireless/ieee80211/bcm43xxx, wireless/spirit/spirit_spi
  - core: fs/vfs/{fs_poll,fs_timerfd}, mm/iob/iob_alloc,
          libs/libc/{netdb/lib_dnscache,time/{lib_calendar2utc,
          lib_time}}, net/icmp/icmp_pmtu, net/icmpv6/icmpv6_pmtu,
          net/ipfrag, net/tcp/{tcp.h,tcp_timer},
          net/utils/net_snoop, net/mld/mld_query (drop the now-dead
          mld_mrc2mrd helper since signed math handles it directly),
          sched/clock/{clock,clock_initialize},
          sched/sched/{sched_profil,sched_setparam,sched_setscheduler},
          sched/pthread/pthread_create,
          sched/wdog/{wd_gettime,wd_start,wdog.h},
          sched/timer/timer_gettime, sched/mqueue/*

Flip the few in-tree printf format strings that assumed an
unsigned 64-bit tv_sec:
  * drivers/rpmsg/rpmsg_ping.c                       PRIu64 -> PRId64
  * arch/xtensa/src/esp32{,s2,s3}/esp32*_oneshot_lowerhalf.c
                                          PRIu32 (already wrong) -> PRId64

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-05-19 16:21:28 +08:00

443 lines
14 KiB
C

/****************************************************************************
* net/mld/mld_query.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <nuttx/debug.h>
#include <nuttx/wdog.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netstats.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/mld.h>
#include "devif/devif.h"
#include "inet/inet.h"
#include "mld/mld.h"
#include "utils/utils.h"
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: mld_check_v1compat
*
* Description:
* If this is for MLDv1 query, then select MLDv1 compatibility mode and
* start (or re-start) the compatibility timer. We need to make this
* check BEFORE sending the report.
*
****************************************************************************/
static inline void mld_check_v1compat(FAR struct net_driver_s *dev,
bool mldv1)
{
if (mldv1)
{
/* REVISIT: I am confused. Per RFC 3810:
* "The Older Version Querier Present Timeout is the time-out for
* transitioning a host back to MLDv2 Host Compatibility Mode. When
* an MLDv1 query is received, MLDv2 hosts set their Older Version
* Querier Present Timer to [Older Version Querier Present Timeout].
*
* "This value MUST be ([Robustness Variable] times (the [Query
* Interval] in the last Query received)) plus ([Query Response
* Interval])."
*
* I am not sure how to do that since the MLDv1 version has no QQI
* field. That is an MLDv2 extension.
*/
/* Select MLDv1 compatibility mode (might already be selected) */
SET_MLD_V1COMPAT(dev->d_mld.flags);
/* REVISIT: Whenever a host changes its compatibility mode, it cancels
* all its pending responses and retransmission timers. Logic Missing.
*/
/* And start the MLDv1 compatibility timer. If the timer is already
* running, this will reset the timer.
*/
mld_start_v1timer(dev, MSEC2TICK(MLD_V1PRESENT_MSEC(MLD_QUERY_MSEC)));
}
}
/****************************************************************************
* 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)
{
FAR const uint16_t *lladdr = netdev_ipv6_lladdr(dev);
int i;
if (lladdr == NULL)
{
/* If no link-local address presents, regard address as ::, then nobody
* can be less than it.
*/
return false;
}
for (i = 0; i < 8; i++)
{
if (srcaddr[i] < lladdr[i])
{
return true;
}
}
return false;
}
/****************************************************************************
* Name: mld_check_querier
*
* Description:
* Check if we are still the querier (assuming that we are currently the
* querier). This compares the IPv6 Source Address of the query against
* the IPv6 address of the link. If the source address is numerically
* less than the link address, when we are no longer the querier.
*
****************************************************************************/
static void mld_check_querier(FAR struct net_driver_s *dev,
FAR struct ipv6_hdr_s *ipv6)
{
/* Check if this member is a Querier */
if (IS_MLD_QUERIER(dev->d_mld.flags))
{
/* This is a querier, check if the IPv6 source address is numerically
* less than the IPv6 address assigned to this link.
*/
if (mld_cmpaddr(dev, ipv6->srcipaddr))
{
/* Switch to non-Querier mode */
CLR_MLD_QUERIER(dev->d_mld.flags);
}
}
/* Check if the member is a Non-Querier. */
if (!IS_MLD_QUERIER(dev->d_mld.flags))
{
/* Yes.. [re-]start the 'Other Querier Present' Timeout. */
mld_start_gentimer(dev, MSEC2TICK(MLD_OQUERY_MSEC));
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mld_query
*
* Description:
* 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.
*
****************************************************************************/
int mld_query(FAR struct net_driver_s *dev,
FAR const struct mld_mcast_listen_query_s *query)
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
FAR struct mld_group_s *group;
unsigned int mldsize;
bool mldv1 = false;
mldinfo("Multicast Listener Query\n");
#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.
*/
mrc = NTOHS(query->mrc);
#endif
/* The MLD version of a Multicast Listener Query message is determined
* as follows:
*
* MLDv1 Query: length = 24 octets
* MLDv2 Query: length >= 28 octets
*
* Query messages that do not match any of the above conditions (e.g., a
* Query of length 26 octets) MUST be silently ignored.
*/
mldsize = (unsigned int)ipv6->len[0] << 8 | ipv6->len[1];
if (mldsize == sizeof(struct mld_mcast_listen_report_v1_s))
{
mldv1 = true;
}
else if (mldsize < SIZEOF_MLD_MCAST_LISTEN_QUERY_S(0))
{
mldinfo("WARNING: Invalid size for MLD query: %u\n", mldsize);
dev->d_len = 0;
return -EINVAL;
}
/* Warn if we received a MLDv2 query in MLDv1 compatibility mode. */
if (!mldv1 && IS_MLD_V1COMPAT(dev->d_mld.flags))
{
mldwarn("WARNING: MLDv2 query received in MLDv1 compatibility mode\n");
}
/* There are three variants of the Query message (RFC 3810):
*
* 1. A "General Query" is sent by the Querier to learn which
* 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)
{
FAR struct mld_group_s *member;
bool rptsent = false;
/* This is the general query */
mldinfo("General multicast query\n");
MLD_STATINCR(g_netstats.mld.gm_query_received);
/* Check if we are still the querier for this sub-net */
mld_check_querier(dev, ipv6);
#ifdef CONFIG_NET_MLD_ROUTER
/* Update accumulated membership at the beginning of each new poll
* cycle
*/
mld_new_pollcycle(dev);
#endif
/* Check MLDv1 compatibility mode */
mld_check_v1compat(dev, mldv1);
/* Send the Report in response to the query. This has to be done
* multiple times because because there is only a single packet buffer
* that is used for both incoming and outgoing packets. When the
* report is sent, it will clobber the incoming* query. Any attempt
* to send an additional Report would also clobber a preceding report
*/
for (member = (FAR struct mld_group_s *)dev->d_mld.grplist.head;
member != NULL;
member = member->next)
{
/* Skip over the all systems group entry */
if (!net_ipv6addr_cmp(member->grpaddr, g_ipv6_allnodes))
{
/* Have we already sent a report from this loop? */
if (rptsent)
{
/* Yes.. Just mark that a report as pending. The pending
* flag will checked on the next driver poll.
*/
SET_MLD_RPTPEND(member->flags);
}
else
{
/* No.. Send one report now. */
mld_send(dev, member, mld_report_msgtype(dev));
rptsent = true;
CLR_MLD_RPTPEND(member->flags);
}
}
}
/* Need to set d_len to zero if nothing is being sent */
if (!rptsent)
{
dev->d_len = 0;
}
return OK;
}
/* All of other Queries are sent with the group address set. Find the
* group instance 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)
{
mldinfo("We are not a member of this group\n");
dev->d_len = 0;
return -ENOENT;
}
/* Check if we are still the querier for this group */
mld_check_querier(dev, ipv6);
#ifdef CONFIG_NET_MLD_ROUTER
/* Save the number of members that reported in the previous query cycle;
* reset the number of members that have reported in the new query cycle.
*/
group->lstmbrs = group->members;
group->members = 0;
#endif
/* Warn if we received a MLDv2 query in MLDv1 compatibility mode. */
if (!mldv1 && IS_MLD_V1COMPAT(dev->d_mld.flags))
{
mldinfo("WARNING: MLDv2 query received in MLDv1 compatibility mode\n");
}
/* Check for Multicast Address Specific (MAS) Query or a Multicast Address
* and Source Specific (MASS) Query. MAS and MASS queries are sent with
* an IP destination address equal to the multicast address of interest.
*/
if (net_ipv6addr_cmp(ipv6->destipaddr, group->grpaddr))
{
if (query->nsources == 0)
{
mldinfo("Multicast Address Specific Query\n");
MLD_STATINCR(g_netstats.mld.mas_query_received);
}
else
{
mldinfo("Multicast Address and Source Specific Query\n");
MLD_STATINCR(g_netstats.mld.mass_query_received);
}
/* Check MLDv1 compatibility mode */
mld_check_v1compat(dev, mldv1);
/* Send the report */
mld_send(dev, group, mld_report_msgtype(dev));
CLR_MLD_RPTPEND(group->flags);
}
/* Not sent to all systems. Check for Unicast General Query */
else if (NETDEV_IS_MY_V6ADDR(dev, ipv6->destipaddr))
{
mldinfo("Unicast query\n");
MLD_STATINCR(g_netstats.mld.ucast_query_received);
/* Check MLDv1 compatibility mode */
mld_check_v1compat(dev, mldv1);
/* Send the report */
mld_send(dev, group, mld_report_msgtype(dev));
CLR_MLD_RPTPEND(group->flags);
}
else
{
mldinfo("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;
}