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 <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/302
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
* 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
| 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 |
.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,
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
[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 },
};
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])) {
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);
char *dhcp_statefile;
int dhcp_statedir_fd;
- char *dhcp_hostsfile;
+ char *dhcp_hostsdir;
int dhcp_hostsdir_fd;
char *ra_piofolder;
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);
}
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);
}
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;
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:
#ifndef _STATEFILES_H_
#define _STATEFILES_H_
+#define ODHCPD_HOSTS_FILE_PREFIX "odhcpd.hosts"
+
bool statefiles_write(void);
#endif /* _STATEFILES_H_ */