From 59fa8a06f39cee0345645d4659427f77cfd316bb Mon Sep 17 00:00:00 2001 From: wenquan1 Date: Thu, 17 Sep 2026 09:23:24 +0800 Subject: [PATCH 1/2] netutils/ptpd: move multicast join after interface address query The IGMP multicast join (ipmsfilter) was previously called before the interface address (interface_addr) was populated via SIOCGIFADDR. This meant the IGMP join had to locate the network device without a valid local address, which could fail or join on the wrong interface. Move the multicast group subscription to after the interface address is queried, and guard it with an AF_INET check since IGMP only applies to IPv4. This ensures the IGMP join can always locate the correct network device. Signed-off-by: wenquan1 --- netutils/ptpd/ptpd.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/netutils/ptpd/ptpd.c b/netutils/ptpd/ptpd.c index d75acca976e..8bb897a3429 100644 --- a/netutils/ptpd/ptpd.c +++ b/netutils/ptpd/ptpd.c @@ -578,21 +578,10 @@ static int ptp_initialize_state(FAR struct ptp_state_s *state) goto errout; } - /* Subscribe to PTP multicast address */ + /* Bind socket for events to PTP multicast address */ bind_addr.sin_family = AF_INET; bind_addr.sin_addr.s_addr = HTONL(PTP_MULTICAST_ADDR); - - ret = ipmsfilter(&state->interface_addr.sin_addr, - &bind_addr.sin_addr, MCAST_INCLUDE); - if (ret < 0) - { - ptperr("Failed to bind multicast address: %d\n", errno); - goto errout; - } - - /* Bind socket for events */ - bind_addr.sin_port = HTONS(PTP_UDP_PORT_EVENT); ret = bind(state->event_socket, (FAR struct sockaddr *)&bind_addr, sizeof(bind_addr)); @@ -655,6 +644,25 @@ static int ptp_initialize_state(FAR struct ptp_state_s *state) state->interface_addr = *(FAR struct sockaddr_in *)&req.ifr_ifru.ifru_addr; + /* Subscribe to PTP multicast address (AF_INET only). + * Must be done after interface_addr is populated so the IGMP join + * can locate the correct network device. + */ + + if (state->config->af == AF_INET) + { + struct in_addr mcast_addr; + + mcast_addr.s_addr = HTONL(PTP_MULTICAST_ADDR); + ret = ipmsfilter(&state->interface_addr.sin_addr, + &mcast_addr, MCAST_INCLUDE); + if (ret < 0) + { + ptperr("Failed to join multicast group: %d\n", errno); + goto errout; + } + } + /* Get hardware address to initialize the identity field in header. * Clock identity is EUI-64, which we make from EUI-48. */ From 0dd9ae94838f74e72be2fbb9415ff81c8b080ff0 Mon Sep 17 00:00:00 2001 From: wenquan1 Date: Thu, 17 Sep 2026 09:25:23 +0800 Subject: [PATCH 2/2] netutils/ptpd: use status file for ptpd_status IPC Replace the sigqueue + shared-memory IPC mechanism in ptpd_status() with a file-based approach: - Daemon side: on SIGUSR1, write a binary ptpd_status_s struct to a temp file and atomically rename it to the status path. - Client side: send kill(SIGUSR1), poll for the file to appear, then read the struct back. This removes the CONFIG_BUILD_FLAT restriction (the old code returned -ENOTSUP for Protected and Kernel builds) and avoids passing pointers across address spaces via sigqueue. The status file path is configurable via NETUTILS_PTPD_STATUSFILE (default /tmp/ptpd.status). The atomic temp + rename pattern ensures readers never see a partial write. Signed-off-by: wenquan1 --- netutils/ptpd/Kconfig | 10 +++ netutils/ptpd/ptpd.c | 174 ++++++++++++++++++++++-------------------- 2 files changed, 101 insertions(+), 83 deletions(-) diff --git a/netutils/ptpd/Kconfig b/netutils/ptpd/Kconfig index 63d756ad9c0..b27143ba2ce 100644 --- a/netutils/ptpd/Kconfig +++ b/netutils/ptpd/Kconfig @@ -189,4 +189,14 @@ config NETUTILS_PTPD_DELAYREQ_AVGCOUNT ---help--- Measured path delay is averaged over this many samples. +config NETUTILS_PTPD_STATUSFILE + string "PTP daemon status file path" + default "/tmp/ptpd.status" + ---help--- + Path where the PTP daemon writes its binary status file + when signaled with SIGUSR1. The ptpd_status() API reads + this file to retrieve daemon state without requiring shared + memory, making it work across all build modes (Flat, + Protected, Kernel). Written atomically via temp + rename. + endif # NETUTILS_PTPD diff --git a/netutils/ptpd/ptpd.c b/netutils/ptpd/ptpd.c index 8bb897a3429..e528da8a977 100644 --- a/netutils/ptpd/ptpd.c +++ b/netutils/ptpd/ptpd.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -64,22 +65,14 @@ * Private Types ****************************************************************************/ -/* Carrier structure for querying PTPD status */ - -struct ptpd_statusreq_s -{ - FAR sem_t *done; - FAR struct ptpd_status_s *dest; -}; - /* Main PTPD state storage */ struct ptp_state_s { - /* Request for PTPD task to stop or report status */ + /* Request for PTPD task to stop or dump status */ bool stop; - struct ptpd_statusreq_s status_req; + bool dump; /* Set by SIGUSR1, checked in main loop */ /* Address of network interface we are operating on */ @@ -1551,10 +1544,9 @@ static void ptp_signal_handler(int signo, FAR siginfo_t *siginfo, { state->stop = true; } - else if (signo == SIGUSR1 && siginfo->si_value.sival_ptr) + else if (signo == SIGUSR1) { - state->status_req = - *(FAR struct ptpd_statusreq_s *)siginfo->si_value.sival_ptr; + state->dump = true; } } @@ -1571,75 +1563,87 @@ static void ptp_setup_sighandlers(FAR struct ptp_state_s *state) sigaction(SIGUSR1, &act, NULL); } -/* Process status information request */ +/* Dump status to file when requested via signal. + * Write atomically: temp file + rename. + */ -static void ptp_process_statusreq(FAR struct ptp_state_s *state) +static void ptp_dump_status_file(FAR struct ptp_state_s *state) { - FAR struct ptpd_status_s *status; + struct ptpd_status_s status; + char tmppath[64]; + int fd; + int ret; - if (!state->status_req.dest) + if (!state->dump) { - return; /* No active request */ + return; } - status = state->status_req.dest; - status->clock_source_valid = state->selected_source_valid; + state->dump = false; - if (status->clock_source_valid) - { - /* Copy relevant parts of announce info to status struct */ + memset(&status, 0, sizeof(status)); + status.clock_source_valid = state->selected_source_valid; + if (status.clock_source_valid) + { FAR struct ptp_announce_s *s = &state->selected_source; - memcpy(status->clock_source_info.id, + memcpy(status.clock_source_info.id, s->header.sourceidentity, - sizeof(status->clock_source_info.id)); + sizeof(status.clock_source_info.id)); - status->clock_source_info.utcoffset = + status.clock_source_info.utcoffset = (int16_t)(((uint16_t)s->utcoffset[0] << 8) | s->utcoffset[1]); - status->clock_source_info.priority1 = s->gm_priority1; - status->clock_source_info.clockclass = s->gm_quality[0]; - status->clock_source_info.accuracy = s->gm_quality[1]; - status->clock_source_info.priority2 = s->gm_priority2; - status->clock_source_info.variance = + status.clock_source_info.priority1 = s->gm_priority1; + status.clock_source_info.clockclass = s->gm_quality[0]; + status.clock_source_info.accuracy = s->gm_quality[1]; + status.clock_source_info.priority2 = s->gm_priority2; + status.clock_source_info.variance = ((uint16_t)s->gm_quality[2] << 8) | s->gm_quality[3]; - memcpy(status->clock_source_info.gm_id, + memcpy(status.clock_source_info.gm_id, s->gm_identity, - sizeof(status->clock_source_info.gm_id)); + sizeof(status.clock_source_info.gm_id)); - status->clock_source_info.stepsremoved = + status.clock_source_info.stepsremoved = ((uint16_t)s->stepsremoved[0] << 8) | s->stepsremoved[1]; - status->clock_source_info.timesource = s->timesource; + status.clock_source_info.timesource = s->timesource; } - /* Copy latest adjustment info */ + status.last_clock_update = state->last_delta_timestamp; + status.last_delta_ns = state->last_delta_ns; + status.last_adjtime_ns = state->last_adjtime_ns; + status.drift_ppb = state->drift_ppb; + status.path_delay_ns = state->path_delay_ns; - status->last_clock_update = state->last_delta_timestamp; - status->last_delta_ns = state->last_delta_ns; - status->last_adjtime_ns = state->last_adjtime_ns; - status->drift_ppb = state->drift_ppb; - status->path_delay_ns = state->path_delay_ns; + status.last_received_multicast = state->last_received_multicast; + status.last_received_announce = state->last_received_announce; + status.last_received_sync = state->last_received_sync; + status.last_transmitted_sync = state->last_transmitted_sync; + status.last_transmitted_announce = state->last_transmitted_announce; + status.last_transmitted_delayresp = state->last_transmitted_delayresp; + status.last_transmitted_delayreq = state->last_transmitted_delayreq; - /* Copy timestamps */ + snprintf(tmppath, sizeof(tmppath), "%s.tmp", + CONFIG_NETUTILS_PTPD_STATUSFILE); - status->last_received_multicast = state->last_received_multicast; - status->last_received_announce = state->last_received_announce; - status->last_received_sync = state->last_received_sync; - status->last_transmitted_sync = state->last_transmitted_sync; - status->last_transmitted_announce = state->last_transmitted_announce; - status->last_transmitted_delayresp = state->last_transmitted_delayresp; - status->last_transmitted_delayreq = state->last_transmitted_delayreq; + fd = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + if (fd < 0) + { + return; + } - /* Post semaphore to inform that we are done */ + ret = write(fd, &status, sizeof(status)); + close(fd); - if (state->status_req.done) + if (ret == sizeof(status)) { - sem_post(state->status_req.done); + rename(tmppath, CONFIG_NETUTILS_PTPD_STATUSFILE); + } + else + { + unlink(tmppath); } - - state->status_req.done = NULL; - state->status_req.dest = NULL; } /**************************************************************************** @@ -1763,7 +1767,7 @@ int ptpd_start(FAR const struct ptpd_config_s *config) ptp_periodic_send(state); state->selected_source_valid = is_selected_source_valid(state); - ptp_process_statusreq(state); + ptp_dump_status_file(state); } errout: @@ -1796,48 +1800,52 @@ int ptpd_start(FAR const struct ptpd_config_s *config) int ptpd_status(int pid, FAR struct ptpd_status_s *status) { -#ifndef CONFIG_BUILD_FLAT + int fd; + int ret; + int elapsed; - /* TODO: Use SHM memory to pass the status information if processes - * do not share the same memory space. - */ + memset(status, 0, sizeof(struct ptpd_status_s)); - return -ENOTSUP; + /* Signal daemon to dump fresh status */ -#else + unlink(CONFIG_NETUTILS_PTPD_STATUSFILE); - int ret = OK; - sem_t donesem; - struct ptpd_statusreq_s req; - union sigval val; - struct timespec timeout; + if (kill(pid, SIGUSR1) != OK) + { + return -errno; + } - /* Fill in the status request */ + /* Wait for status file to appear (up to 3s) */ - memset(status, 0, sizeof(struct ptpd_status_s)); - sem_init(&donesem, 0, 0); - req.done = &donesem; - req.dest = status; - val.sival_ptr = &req; + for (elapsed = 0; elapsed < 30; elapsed++) + { + usleep(100000); + if (access(CONFIG_NETUTILS_PTPD_STATUSFILE, F_OK) == 0) + { + break; + } + } - if (sigqueue(pid, SIGUSR1, val) != OK) + if (elapsed >= 30) + { + return -ETIMEDOUT; + } + + fd = open(CONFIG_NETUTILS_PTPD_STATUSFILE, O_RDONLY | O_CLOEXEC); + if (fd < 0) { return -errno; } - /* Wait for status request to be handled */ + ret = read(fd, status, sizeof(*status)); + close(fd); - clock_gettime(CLOCK_MONOTONIC, &timeout); - timeout.tv_sec += 1; - if (sem_clockwait(&donesem, CLOCK_MONOTONIC, &timeout) != 0) + if (ret != sizeof(*status)) { - ret = -errno; + return ret < 0 ? -errno : -EIO; } - sem_destroy(&donesem); - return ret; - -#endif /* CONFIG_BUILD_FLAT */ + return OK; } /****************************************************************************