From 61531998dc697d84a191f0d4b35204d312b00fe0 Mon Sep 17 00:00:00 2001 From: Tiago Medicci Date: Thu, 11 Jun 2026 09:34:54 -0300 Subject: [PATCH] examples/mdnsd: Create an event-based starter for mDNS daemon Start the mDNS daemon based on the event of getting a new IP address for any of the system's network interfaces. This allows DHCP client to trigger the mDNS daemon after getting a new IP address, for instance. Signed-off-by: Tiago Medicci --- examples/mdnsd/Makefile | 4 +- examples/mdnsd/mdnsd_event.c | 244 +++++++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 examples/mdnsd/mdnsd_event.c diff --git a/examples/mdnsd/Makefile b/examples/mdnsd/Makefile index 316e6d8172f..5dc84bd13b1 100644 --- a/examples/mdnsd/Makefile +++ b/examples/mdnsd/Makefile @@ -26,11 +26,11 @@ include $(APPDIR)/Make.defs CSRCS = mdnsd_daemon.c -MAINSRC = mdnsd_start.c mdnsd_stop.c +MAINSRC = mdnsd_start.c mdnsd_stop.c mdnsd_event.c # MDNSD built-in application info -PROGNAME = mdnsd_start mdnsd_stop +PROGNAME = mdnsd_start mdnsd_stop mdnsd_event PRIORITY = SCHED_PRIORITY_DEFAULT STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) MODULE = $(CONFIG_EXAMPLES_MDNSD) diff --git a/examples/mdnsd/mdnsd_event.c b/examples/mdnsd/mdnsd_event.c new file mode 100644 index 00000000000..432042372cd --- /dev/null +++ b/examples/mdnsd/mdnsd_event.c @@ -0,0 +1,244 @@ +/**************************************************************************** + * apps/examples/mdnsd/mdnsd_event.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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netutils/mdnsd.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MDNSD_EVENT_BUFSIZE 512 +#define MDNSD_EVENT_SETTLE_MS 500 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mdnsd_event_has_ipv4addr + * + * Description: + * Check whether any network interface already has a non-zero IPv4 + * address. Uses SIOCGIFCONF to enumerate interfaces. + * + * Returned Value: + * true if at least one interface has a non-zero IPv4 address, + * false otherwise. + * + ****************************************************************************/ + +static bool mdnsd_event_has_ipv4addr(void) +{ + struct ifconf ifc; + char buf[sizeof(struct ifreq) * 4]; + int sd; + int i; + + sd = socket(AF_INET, SOCK_DGRAM, 0); + if (sd < 0) + { + return false; + } + + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl(sd, SIOCGIFCONF, &ifc) < 0) + { + close(sd); + return false; + } + + close(sd); + + for (i = 0; i < ifc.ifc_len; i += sizeof(struct ifreq)) + { + FAR struct ifreq *ifr = (FAR struct ifreq *)&buf[i]; + FAR struct sockaddr_in *sin; + + if (ifr->ifr_addr.sa_family != AF_INET) + { + continue; + } + + sin = (FAR struct sockaddr_in *)&ifr->ifr_addr; + if (sin->sin_addr.s_addr != INADDR_ANY && + sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) + { + return true; + } + } + + return false; +} + +/**************************************************************************** + * Name: mdnsd_event_is_newaddr + * + * Description: + * Checks whether the given Netlink message header represents a new + * IPv4 or IPv6 address event (RTM_NEWADDR). + * + * Input Parameters: + * hdr - Pointer to the Netlink message header to inspect. + * + * Returned Value: + * true if the message is a new IPv4 or IPv6 address notification, + * false otherwise. + * + ****************************************************************************/ + +static bool mdnsd_event_is_newaddr(FAR struct nlmsghdr *hdr) +{ + FAR struct ifaddrmsg *addr; + + if (hdr->nlmsg_type != RTM_NEWADDR) + { + return false; + } + + if (hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg))) + { + return false; + } + + addr = (FAR struct ifaddrmsg *)NLMSG_DATA(hdr); + return addr->ifa_family == AF_INET || addr->ifa_family == AF_INET6; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + * + * Description: + * Entry point for the mdnsd_event application. Listens for Netlink + * address events and starts the mDNS daemon when an IP address is + * assigned to a network interface. + * + * Input Parameters: + * argc - Number of command-line arguments. + * argv - Array of command-line argument strings. + * + * Returned Value: + * EXIT_SUCCESS on success, EXIT_FAILURE on error. + * + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ +#ifdef CONFIG_NETLINK_ROUTE + struct sockaddr_nl addr; + uint8_t buffer[MDNSD_EVENT_BUFSIZE]; + int sd; + int ret; + + /* If an interface already has an IP (DHCP finished before we started), + * skip the netlink wait and start mDNS right away. + */ + + if (mdnsd_event_has_ipv4addr()) + { + printf("mdnsd_event: address already set, starting mDNS\n"); + usleep(MDNSD_EVENT_SETTLE_MS * 1000); + ret = mdnsd_start(CONFIG_EXAMPLES_MDNS_SERVICE, + CONFIG_EXAMPLES_MDNS_SERVICE_PORT); + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } + + sd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sd < 0) + { + fprintf(stderr, "mdnsd_event: socket failed: %d\n", errno); + return EXIT_FAILURE; + } + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; + + ret = bind(sd, (FAR struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) + { + fprintf(stderr, "mdnsd_event: bind failed: %d\n", errno); + close(sd); + return EXIT_FAILURE; + } + + printf("mdnsd_event: waiting for network address event\n"); + + for (; ; ) + { + FAR struct nlmsghdr *hdr; + ssize_t nread; + int remaining; + + nread = recv(sd, buffer, sizeof(buffer), 0); + if (nread < 0) + { + fprintf(stderr, "mdnsd_event: recv failed: %d\n", errno); + close(sd); + return EXIT_FAILURE; + } + + remaining = nread; + for (hdr = (FAR struct nlmsghdr *)buffer; + NLMSG_OK(hdr, remaining); + hdr = NLMSG_NEXT(hdr, remaining)) + { + if (mdnsd_event_is_newaddr(hdr)) + { + printf("mdnsd_event: address set, starting mDNS\n"); + usleep(MDNSD_EVENT_SETTLE_MS * 1000); + ret = mdnsd_start(CONFIG_EXAMPLES_MDNS_SERVICE, + CONFIG_EXAMPLES_MDNS_SERVICE_PORT); + close(sd); + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } + } + } +#else + fprintf(stderr, "mdnsd_event: CONFIG_NETLINK_ROUTE is required\n"); + return EXIT_FAILURE; +#endif +}