From: David Härdeman Date: Sat, 8 Nov 2025 18:52:44 +0000 (+0100) Subject: statefiles: support per-interface hosts files X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=77f9a0dad51796c1ca873deef876fb9644cd98ef;p=project%2Fodhcpd.git statefiles: support per-interface hosts files With this change, the "hostsfile" option is changed to a "hostsdir" option. If set, per-interface hosts files will be written to the directory (this requires some changes to the UCI defaults in the OpenWrt repo). This prevents the hostnames from one interface "leaking" into other interfaces, which might not be desirable e.g. when running several instances of dnsmasq. dnsmasq already supports reading several hosts files from a directory (via the "--hostsdir=" option), so some changes to dnsmasq's init script will also be necessary (depending on whether the user wants dnsmasq to support resolution of all hosts or just the host on a given interface). Making hosts files (rather than odhcpd's state file) the primary interface for dnsmasq to be made aware of active leases/hostnames is also an important first step towards making the statefile properly private (the other important consumer is LuCI, via its rpcd plugin), which is necessary if we want to support persisting leases across restarts of odhcpd (or even reboots, if writing to flash is deemed acceptable) in the future (since I want to extend the state file to capture the full state of leases so that they can be properly reconstructed). In addition, the use of "--hostsdir=" in dnsmasq would make the "leasetrigger" unnecessary, since it just restarts dnsmasq to make it aware of the changes to the hostsfile. That still leaves the case when dnsmasq uses multiple instances and only wants to read a specific hostsfile (which, AFAIK, doesn't support automatic detection of when that specific file changes). Closes: https://github.com/openwrt/odhcpd/issues/237 Signed-off-by: David Härdeman Link: https://github.com/openwrt/odhcpd/pull/302 Signed-off-by: Álvaro Fernández Rojas --- diff --git a/README.md b/README.md index c9ac2ec..b810555 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ prefix delegation and can be used to relay RA, DHCPv6 and NDP between routed * stateless and stateful address assignment * prefix delegation support * dynamic reconfiguration in case prefixes change - * hostname detection and hosts-file creation + * hostname detection and hosts-files creation * relay: mostly standards-compliant DHCPv6-relay * support for rewriting announced DNS-server addresses @@ -64,7 +64,7 @@ and may also receive information from ubus | maindhcp | bool | 0 | Use odhcpd as the main DHCPv4 service | | leasefile | string| | DHCP/v6 lease/hostfile | | leasetrigger | string| | Lease trigger script | -| hostsfile | string| | DHCP/v6 hostfile | +| hostsdir | string| | DHCP/v6 hostfile directory (one file per interface will be created) | | loglevel |integer| 6 | Syslog level priority (0-7) | | piofolder |string | | Folder to store IPv6 prefix information (to detect stale prefixes, see RFC9096, §3.5) | | enable_tz |bool | 1 | Toggle whether RFC4833 timezone information is sent to clients, if set in system | diff --git a/src/config.c b/src/config.c index 0cb6d55..a1979a7 100644 --- a/src/config.c +++ b/src/config.c @@ -40,7 +40,7 @@ struct config config = { .dhcp_cb = NULL, .dhcp_statefile = NULL, .dhcp_statedir_fd = -1, - .dhcp_hostsfile = NULL, + .dhcp_hostsdir = NULL, .dhcp_hostsdir_fd = -1, .ra_piofolder = NULL, .ra_piofolder_fd = -1, @@ -213,7 +213,7 @@ enum { ODHCPD_ATTR_LEASEFILE, ODHCPD_ATTR_LEASETRIGGER, ODHCPD_ATTR_LOGLEVEL, - ODHCPD_ATTR_HOSTSFILE, + ODHCPD_ATTR_HOSTSDIR, ODHCPD_ATTR_PIOFOLDER, ODHCPD_ATTR_ENABLE_TZ, ODHCPD_ATTR_MAX @@ -224,7 +224,7 @@ static const struct blobmsg_policy odhcpd_attrs[ODHCPD_ATTR_MAX] = { [ODHCPD_ATTR_LEASEFILE] = { .name = "leasefile", .type = BLOBMSG_TYPE_STRING }, [ODHCPD_ATTR_LEASETRIGGER] = { .name = "leasetrigger", .type = BLOBMSG_TYPE_STRING }, [ODHCPD_ATTR_LOGLEVEL] = { .name = "loglevel", .type = BLOBMSG_TYPE_INT32 }, - [ODHCPD_ATTR_HOSTSFILE] = { .name = "hostsfile", .type = BLOBMSG_TYPE_STRING }, + [ODHCPD_ATTR_HOSTSDIR] = { .name = "hostsdir", .type = BLOBMSG_TYPE_STRING }, [ODHCPD_ATTR_PIOFOLDER] = { .name = "piofolder", .type = BLOBMSG_TYPE_STRING }, [ODHCPD_ATTR_ENABLE_TZ] = { .name = "enable_tz", .type = BLOBMSG_TYPE_BOOL }, }; @@ -456,9 +456,9 @@ static void set_config(struct uci_section *s) config.dhcp_statefile = strdup(blobmsg_get_string(c)); } - if ((c = tb[ODHCPD_ATTR_HOSTSFILE])) { - free(config.dhcp_hostsfile); - config.dhcp_hostsfile = strdup(blobmsg_get_string(c)); + if ((c = tb[ODHCPD_ATTR_HOSTSDIR])) { + free(config.dhcp_hostsdir); + config.dhcp_hostsdir = strdup(blobmsg_get_string(c)); } if ((c = tb[ODHCPD_ATTR_PIOFOLDER])) { @@ -2321,11 +2321,9 @@ void odhcpd_reload(void) error("Unable to open statedir: '%s': %m", dir); } - if (config.dhcp_hostsfile) { - char *dir = dirname(strdupa(config.dhcp_hostsfile)); - char *file = basename(config.dhcp_hostsfile); + if (config.dhcp_hostsdir) { + char *dir = strdupa(config.dhcp_hostsdir); - memmove(config.dhcp_hostsfile, file, strlen(file) + 1); mkdir_p(dir, 0755); close(config.dhcp_hostsdir_fd); diff --git a/src/odhcpd.h b/src/odhcpd.h index 5eaf4ce..354c529 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -200,7 +200,7 @@ struct config { char *dhcp_statefile; int dhcp_statedir_fd; - char *dhcp_hostsfile; + char *dhcp_hostsdir; int dhcp_hostsdir_fd; char *ra_piofolder; diff --git a/src/statefiles.c b/src/statefiles.c index b362a1a..c5f2b57 100644 --- a/src/statefiles.c +++ b/src/statefiles.c @@ -60,7 +60,7 @@ static bool statefiles_write_host6(struct write_ctxt *ctxt, struct dhcpv6_lease if (!lease->hostname || lease->flags & OAF_BROKEN_HOSTNAME || !(lease->flags & OAF_DHCPV6_NA)) return false; - if (!ctxt->fp) { + if (ctxt->fp) { inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf)); statefiles_write_host(ipbuf, lease->hostname, ctxt); } @@ -84,7 +84,7 @@ static bool statefiles_write_host4(struct write_ctxt *ctxt, struct dhcpv4_lease if (!lease->hostname || lease->flags & OAF_BROKEN_HOSTNAME) return false; - if (!ctxt->fp) { + if (ctxt->fp) { inet_ntop(AF_INET, &addr, ipbuf, sizeof(ipbuf)); statefiles_write_host(ipbuf, lease->hostname, ctxt); } @@ -95,32 +95,32 @@ static bool statefiles_write_host4(struct write_ctxt *ctxt, struct dhcpv4_lease static void statefiles_write_hosts(time_t now) { struct write_ctxt ctxt; - size_t tmp_hostsfile_strlen; - char *tmp_hostsfile; + const char *tmp_hostsfile = ".odhcpd.hosts"; int fd; - if (config.dhcp_hostsdir_fd < 0 || !config.dhcp_hostsfile) + if (config.dhcp_hostsdir_fd < 0) return; - tmp_hostsfile_strlen = strlen(config.dhcp_hostsfile) + 2; - tmp_hostsfile = alloca(tmp_hostsfile_strlen); - sprintf(tmp_hostsfile, ".%s", config.dhcp_hostsfile); + avl_for_each_element(&interfaces, ctxt.iface, avl) { + char *hostsfile; - fd = openat(config.dhcp_hostsdir_fd, tmp_hostsfile, O_CREAT | O_WRONLY | O_CLOEXEC, 0644); - if (fd < 0) - goto err; + hostsfile = alloca(strlen(ODHCPD_HOSTS_FILE_PREFIX) + strlen(ctxt.iface->name) + 1); + sprintf(hostsfile, "%s.%s", ODHCPD_HOSTS_FILE_PREFIX, ctxt.iface->name); - if (lockf(fd, F_LOCK, 0) < 0) - goto err; + fd = openat(config.dhcp_hostsdir_fd, tmp_hostsfile, O_CREAT | O_WRONLY | O_CLOEXEC, 0644); + if (fd < 0) + goto err; - if (ftruncate(fd, 0) < 0) - goto err; + if (lockf(fd, F_LOCK, 0) < 0) + goto err; + + if (ftruncate(fd, 0) < 0) + goto err; - ctxt.fp = fdopen(fd, "w"); - if (!ctxt.fp) - goto err; + ctxt.fp = fdopen(fd, "w"); + if (!ctxt.fp) + goto err; - avl_for_each_element(&interfaces, ctxt.iface, avl) { if (ctxt.iface->dhcpv6 == MODE_SERVER) { struct dhcpv6_lease *lease; @@ -149,11 +149,12 @@ static void statefiles_write_hosts(time_t now) statefiles_write_host4(&ctxt, lease); } } + + fclose(ctxt.fp); + renameat(config.dhcp_hostsdir_fd, tmp_hostsfile, + config.dhcp_hostsdir_fd, hostsfile); } - fclose(ctxt.fp); - renameat(config.dhcp_hostsdir_fd, tmp_hostsfile, - config.dhcp_hostsdir_fd, config.dhcp_hostsfile); return; err: diff --git a/src/statefiles.h b/src/statefiles.h index 9acd9ab..dec1aa1 100644 --- a/src/statefiles.h +++ b/src/statefiles.h @@ -7,6 +7,8 @@ #ifndef _STATEFILES_H_ #define _STATEFILES_H_ +#define ODHCPD_HOSTS_FILE_PREFIX "odhcpd.hosts" + bool statefiles_write(void); #endif /* _STATEFILES_H_ */