rpcd-mod-luci: correct DUID logic

dnsmasq writes a statefile for DHCPv4 leases with the MAC address as the
second field and the clientid as the fifth field. The clientid starts
with a type byte (0x01 = ethernet, 0xff = DUID/IAID identifier).

Similarly, odhcpd writes the MAC address as the second field for IPv4
leases.

This patch changes the behaviour of rpcd-mod-luci so that it doesn't
report a non-DUID as a DUID.

Signed-off-by: David Härdeman <david@hardeman.nu>
This commit is contained in:
David Härdeman
2025-11-30 18:35:56 +01:00
committed by Paul Donald
parent d96d7cf798
commit 3b7869e6df

View File

@@ -329,7 +329,7 @@ duid2ea(const char *duid)
return &ea; return &ea;
} }
static void strip_colon_duid(char *str) { static void strip_colon(char *str) {
char *pr = str, *pw = str; char *pr = str, *pw = str;
while (*pr) { while (*pr) {
@@ -483,39 +483,40 @@ lease_next(void)
memset(&e, 0, sizeof(e)); memset(&e, 0, sizeof(e));
while (fgets(e.buf, sizeof(e.buf), lease_state.files[lease_state.off].fh)) { while (fgets(e.buf, sizeof(e.buf), lease_state.files[lease_state.off].fh)) {
ea = NULL;
if (lease_state.files[lease_state.off].odhcpd) { if (lease_state.files[lease_state.off].odhcpd) {
strtok(e.buf, " \t\n"); /* # */ strtok(e.buf, " \t\n"); /* # */
strtok(NULL, " \t\n"); /* iface */ strtok(NULL, " \t\n"); /* iface */
e.duid = strtok(NULL, " \t\n"); /* duid */ e.duid = strtok(NULL, " \t\n"); /* duid or MAC */
if (!e.duid) if (!e.duid)
continue; continue;
e.iaid = strtok(NULL, " \t\n"); /* iaid */ e.iaid = strtok(NULL, " \t\n"); /* iaid or "ipv4"*/
if (!e.iaid) if (!e.iaid)
continue; continue;
if (!strcmp(e.iaid, "ipv4")) { if (!strcmp(e.iaid, "ipv4")) {
e.af = AF_INET; e.af = AF_INET;
e.mask = 32; e.mask = 32;
} ea = ether_aton(e.duid);
else { e.duid = NULL;
e.iaid = NULL;
} else {
e.af = AF_INET6; e.af = AF_INET6;
e.mask = 128; e.mask = 128;
} }
e.hostname = strtok(NULL, " \t\n"); /* name */ e.hostname = strtok(NULL, " \t\n"); /* name */
if (!e.hostname) if (!e.hostname)
continue; continue;
p = strtok(NULL, " \t\n"); /* ts */ p = strtok(NULL, " \t\n"); /* ts */
if (!p) if (!p)
continue; continue;
n = strtol(p, NULL, 10); n = strtol(p, NULL, 10);
if (n > lease_state.now) if (n > lease_state.now)
e.expire = n - lease_state.now; e.expire = n - lease_state.now;
else if (n >= 0) else if (n >= 0)
@@ -526,12 +527,10 @@ lease_next(void)
strtok(NULL, " \t\n"); /* id */ strtok(NULL, " \t\n"); /* id */
p = strtok(NULL, " \t\n"); /* length */ p = strtok(NULL, " \t\n"); /* length */
if (!p) if (!p)
continue; continue;
n = atoi(p); /* length */ n = atoi(p); /* length */
if (n != 0) if (n != 0)
e.mask = n; e.mask = n;
@@ -542,7 +541,8 @@ lease_next(void)
e.n_addr++; e.n_addr++;
} }
ea = duid2ea(e.duid); if (!ea)
ea = duid2ea(e.duid);
if (ea) if (ea)
e.mac = *ea; e.mac = *ea;
@@ -550,10 +550,9 @@ lease_next(void)
if (!strcmp(e.hostname, "-")) if (!strcmp(e.hostname, "-"))
e.hostname = NULL; e.hostname = NULL;
if (!strcmp(e.duid, "-")) if (e.duid && !strcmp(e.duid, "-"))
e.duid = NULL; e.duid = NULL;
} } else {
else {
p = strtok(e.buf, " \t\n"); p = strtok(e.buf, " \t\n");
if (!p) if (!p)
@@ -601,13 +600,26 @@ lease_next(void)
if (!e.hostname || !e.duid) if (!e.hostname || !e.duid)
continue; continue;
strip_colon_duid(e.duid);
if (!strcmp(e.hostname, "*")) if (!strcmp(e.hostname, "*"))
e.hostname = NULL; e.hostname = NULL;
if (!strcmp(e.duid, "*")) if (e.af == AF_INET && strlen(e.duid) > 15 && !strncmp(e.duid, "ff:", 3)) {
/* ff:<iaid-4-bytes>:<duid-x-bytes...> */
e.duid[14] = '\0';
e.iaid = &e.duid[3];
strip_colon(e.iaid);
e.duid = &e.duid[15];
strip_colon(e.duid);
} else if(e.af == AF_INET && strlen(e.duid) == 20 && !strncmp(e.duid, "01:", 3)) {
/* 01:<mac-addr-6-bytes> */
if (!ea)
ea = ether_aton(&e.duid[3]);
e.duid = NULL; e.duid = NULL;
} else if (!strcmp(e.duid, "*")) {
e.duid = NULL;
} else {
strip_colon(e.duid);
}
if (!ea && e.duid) if (!ea && e.duid)
ea = duid2ea(e.duid); ea = duid2ea(e.duid);