summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Donald2025-12-22 13:44:58 +0000
committerÁlvaro Fernández Rojas2026-01-25 18:08:59 +0000
commite282a3c7e3f765d2fc3dcbd6a976c75880fcab14 (patch)
treeb0d7b7e86ef52d821d57ffaa45e2c32f4f092b90
parent386784657453900881c4ac99db2983a95e876203 (diff)
dhcpv6-ia: switch case refactor in dhcpv6_handle_ias()
Rewrite core logic using switch cases for clarity and improved performance. This also simplifies the DHCPV6_MSG_REBIND cases: DHCPV6_MSG_REBIND and !a is parsed in the switch DHCPV6_MSG_REBIND and a is now parsed after the switch (5 lines) (cherry picked from commit 9857adb8ac999742836e5c30e75da9aad34a4ba9) Signed-off-by: Paul Donald <newtwen+github@gmail.com> https://github.com/openwrt/odhcpd/pull/360 Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
-rw-r--r--src/dhcpv6-ia.c203
1 files changed, 139 insertions, 64 deletions
diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c
index 4747151..3366bd0 100644
--- a/src/dhcpv6-ia.c
+++ b/src/dhcpv6-ia.c
@@ -958,7 +958,8 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
char duidbuf[DUID_HEXSTRLEN], hostname[256];
dhcpv6_for_each_option(start, end, otype, olen, odata) {
- if (otype == DHCPV6_OPT_CLIENTID) {
+ switch (otype) {
+ case DHCPV6_OPT_CLIENTID:
duid = odata;
duid_len = olen;
@@ -969,17 +970,33 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
if (olen <= DUID_MAX_LEN)
odhcpd_hexlify(duidbuf, odata, olen);
- } else if (otype == DHCPV6_OPT_FQDN && olen >= 2 && olen <= 255) {
+ break;
+
+ case DHCPV6_OPT_FQDN:
+ if (olen < 2 || olen > 255)
+ break;
+
uint8_t fqdn_buf[256];
memcpy(fqdn_buf, odata, olen);
fqdn_buf[olen++] = 0;
if (dn_expand(&fqdn_buf[1], &fqdn_buf[olen], &fqdn_buf[1], hostname, sizeof(hostname)) > 0)
hostname_len = strcspn(hostname, ".");
- } else if (otype == DHCPV6_OPT_RECONF_ACCEPT)
+
+ break;
+
+ case DHCPV6_OPT_RECONF_ACCEPT:
accept_reconf = true;
- else if (otype == DHCPV6_OPT_RAPID_COMMIT && hdr->msg_type == DHCPV6_MSG_SOLICIT)
- rapid_commit = true;
+ break;
+
+ case DHCPV6_OPT_RAPID_COMMIT:
+ if (hdr->msg_type == DHCPV6_MSG_SOLICIT)
+ rapid_commit = true;
+ break;
+
+ default:
+ break;
+ }
}
if (!duid || duid_len < DUID_MIN_LEN || duid_len > DUID_MAX_LEN)
@@ -1095,8 +1112,8 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
* If there's a DUID configured for this static lease, but without
* an IAID, we will proceed under the assumption that a request
* with the right DUID but with *any* IAID should be able to take
- * over the assignment. E.g. when switching from WiFi to ethernet
- * on the same client. This is similar to how multiple MAC adresses
+ * over the assignment. E.g. when switching from WiFi to Ethernet
+ * on the same client. This is similar to how multiple MAC addresses
* are handled for DHCPv4.
*/
for (size_t i = 0; i < lease_cfg->duid_count; i++) {
@@ -1139,11 +1156,16 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
proceed:
/* Generic message handling */
uint16_t status = DHCPV6_STATUS_OK;
+ bool assigned = false;
- if (hdr->msg_type == DHCPV6_MSG_SOLICIT ||
- hdr->msg_type == DHCPV6_MSG_REQUEST ||
- (hdr->msg_type == DHCPV6_MSG_REBIND && !a)) {
- bool assigned = !!a;
+ switch (hdr->msg_type) {
+ case DHCPV6_MSG_SOLICIT:
+ case DHCPV6_MSG_REQUEST:
+ case DHCPV6_MSG_REBIND: {
+ if (hdr->msg_type == DHCPV6_MSG_REBIND && a)
+ break;
+
+ assigned = (a != NULL);
if (!a) {
if ((!iface->no_dynamic_dhcp || (lease_cfg && is_na)) &&
@@ -1157,25 +1179,27 @@ proceed:
a->iaid = ia->iaid;
a->length = reqlen;
a->peer = *addr;
+ a->iface = iface;
+ a->flags = is_pd ? OAF_DHCPV6_PD : OAF_DHCPV6_NA;
+ a->valid_until = now;
+ a->preferred_until = now;
+
if (is_na)
a->assigned_host_id = lease_cfg ? lease_cfg->hostid : 0;
else
a->assigned_subnet_id = reqhint;
- a->valid_until = now;
- a->preferred_until = now;
- a->iface = iface;
- a->flags = (is_pd ? OAF_DHCPV6_PD : OAF_DHCPV6_NA);
if (first)
memcpy(a->key, first->key, sizeof(a->key));
else
odhcpd_urandom(a->key, sizeof(a->key));
- if (is_pd && iface->dhcpv6_pd)
+ if (is_pd && iface->dhcpv6_pd) {
while (!(assigned = assign_pd(iface, a)) &&
++a->length <= 64);
- else if (is_na && iface->dhcpv6_na)
+ } else if (is_na && iface->dhcpv6_na) {
assigned = assign_na(iface, a);
+ }
if (lease_cfg && assigned) {
if (lease_cfg->hostname) {
@@ -1193,15 +1217,20 @@ proceed:
}
}
- if (!assigned || iface->addr6_len == 0)
+ /* Status evaluation */
+ if (!assigned || iface->addr6_len == 0) {
/* Set error status */
- status = (is_pd) ? DHCPV6_STATUS_NOPREFIXAVAIL : DHCPV6_STATUS_NOADDRSAVAIL;
- else if (hdr->msg_type == DHCPV6_MSG_REQUEST && !dhcpv6_ia_on_link(ia, a, iface)) {
+ status = is_pd ? DHCPV6_STATUS_NOPREFIXAVAIL : DHCPV6_STATUS_NOADDRSAVAIL;
+ } else if (hdr->msg_type == DHCPV6_MSG_REQUEST && !dhcpv6_ia_on_link(ia, a, iface)) {
/* Send NOTONLINK status for the IA */
status = DHCPV6_STATUS_NOTONLINK;
assigned = false;
- } else if (accept_reconf && assigned && !first &&
- hdr->msg_type != DHCPV6_MSG_REBIND) {
+ }
+
+ /* Reconfigure Accept */
+ if (accept_reconf && assigned && !first &&
+ hdr->msg_type != DHCPV6_MSG_REBIND) {
+
size_t handshake_len = 4;
buf[0] = 0;
buf[1] = DHCPV6_OPT_RECONF_ACCEPT;
@@ -1217,12 +1246,12 @@ proceed:
1,
{0}
};
+
memcpy(auth.key, a->key, sizeof(a->key));
memcpy(buf + handshake_len, &auth, sizeof(auth));
handshake_len += sizeof(auth);
}
-
buf += handshake_len;
buflen -= handshake_len;
response_len += handshake_len;
@@ -1230,61 +1259,97 @@ proceed:
first = a;
}
- ia_response_len = build_ia(buf, buflen, status, ia, a, iface,
- hdr->msg_type == DHCPV6_MSG_REBIND ? false : true);
+ ia_response_len = build_ia(
+ buf, buflen, status, ia, a, iface,
+ hdr->msg_type != DHCPV6_MSG_REBIND);
/* Was only a solicitation: mark binding for removal in 60 seconds */
- if (assigned && hdr->msg_type == DHCPV6_MSG_SOLICIT && !rapid_commit) {
- a->bound = false;
- a->valid_until = now + 60;
-
- } else if (assigned &&
- ((hdr->msg_type == DHCPV6_MSG_SOLICIT && rapid_commit) ||
- hdr->msg_type == DHCPV6_MSG_REQUEST ||
- hdr->msg_type == DHCPV6_MSG_REBIND)) {
- if (hostname_len > 0 && (!a->lease_cfg || !a->lease_cfg->hostname)) {
- char *tmp = realloc(a->hostname, hostname_len + 1);
- if (tmp) {
- a->hostname = tmp;
- memcpy(a->hostname, hostname, hostname_len);
- a->hostname[hostname_len] = 0;
- a->hostname_valid = odhcpd_hostname_valid(a->hostname);
+ if (assigned) {
+ switch (hdr->msg_type) {
+ case DHCPV6_MSG_SOLICIT:
+ if (!rapid_commit) {
+ a->bound = false;
+ a->valid_until = now + 60;
+ break;
+ }
+
+ _o_fallthrough;
+ case DHCPV6_MSG_REQUEST:
+ case DHCPV6_MSG_REBIND:
+ if (hostname_len > 0 && (!a->lease_cfg || !a->lease_cfg->hostname)) {
+
+ char *tmp = realloc(a->hostname, hostname_len + 1);
+ if (tmp) {
+ a->hostname = tmp;
+ memcpy(a->hostname, hostname, hostname_len);
+ a->hostname[hostname_len] = 0;
+ a->hostname_valid = odhcpd_hostname_valid(a->hostname);
+ }
}
+
+ a->accept_fr_nonce = accept_reconf;
+ a->bound = true;
+ apply_lease(a, true);
+ break;
+
+ default:
+ break;
}
- a->accept_fr_nonce = accept_reconf;
- a->bound = true;
- apply_lease(a, true);
- } else if (!assigned) {
- /* Cleanup failed assignment */
+ } else {
+ /* Clean up failed assignment */
dhcpv6_free_lease(a);
a = NULL;
}
- } else if (hdr->msg_type == DHCPV6_MSG_RENEW ||
- hdr->msg_type == DHCPV6_MSG_RELEASE ||
- hdr->msg_type == DHCPV6_MSG_REBIND ||
- hdr->msg_type == DHCPV6_MSG_DECLINE) {
- if (!a && hdr->msg_type != DHCPV6_MSG_REBIND) {
+
+ break;
+ }
+
+ case DHCPV6_MSG_RENEW:
+ case DHCPV6_MSG_RELEASE:
+ case DHCPV6_MSG_DECLINE: {
+ /* RENEW / RELEASE / DECLINE require an existing binding */
+ if (!a) {
status = DHCPV6_STATUS_NOBINDING;
ia_response_len = build_ia(buf, buflen, status, ia, a, iface, false);
- } else if (hdr->msg_type == DHCPV6_MSG_RENEW ||
- hdr->msg_type == DHCPV6_MSG_REBIND) {
+ break;
+ }
+
+ switch (hdr->msg_type) {
+ case DHCPV6_MSG_RENEW:
ia_response_len = build_ia(buf, buflen, status, ia, a, iface, false);
- if (a) {
- a->bound = true;
- apply_lease(a, true);
- }
- } else if (hdr->msg_type == DHCPV6_MSG_RELEASE) {
+
+ a->bound = true;
+ apply_lease(a, true);
+ break;
+
+ case DHCPV6_MSG_RELEASE:
+ /* Immediately expire the lease */
a->valid_until = now - 1;
- } else if ((a->flags & OAF_DHCPV6_NA) && hdr->msg_type == DHCPV6_MSG_DECLINE) {
+ break;
+
+ case DHCPV6_MSG_DECLINE:
+ /* DECLINE only applies to non-temporary addresses */
+ if (!(a->flags & OAF_DHCPV6_NA))
+ break;
+
a->bound = false;
- if (!a->lease_cfg || a->lease_cfg->hostid != a->assigned_host_id) {
- memset(a->duid, 0, a->duid_len);
- a->valid_until = now + 3600; /* Block address for 1h */
- } else
+ if (a->lease_cfg &&
+ a->lease_cfg->hostid == a->assigned_host_id) {
+ /* Static lease: release immediately */
a->valid_until = now - 1;
+ } else {
+ /* Dynamic lease: block address for 1 hour */
+ memset(a->duid, 0, a->duid_len);
+ a->valid_until = now + 3600;
+ }
+ break;
}
- } else if (hdr->msg_type == DHCPV6_MSG_CONFIRM) {
+
+ break;
+ }
+
+ case DHCPV6_MSG_CONFIRM:
if (ia_addr_present && !dhcpv6_ia_on_link(ia, a, iface)) {
notonlink = true;
break;
@@ -1294,13 +1359,23 @@ proceed:
response_len = 0;
goto out;
}
+ break;
+
+ default:
+ break;
+ }
+
+ if (hdr->msg_type == DHCPV6_MSG_REBIND && a) {
+ ia_response_len = build_ia(buf, buflen, status, ia, a, iface, false);
+ a->bound = true;
+ apply_lease(a, true);
}
buf += ia_response_len;
buflen -= ia_response_len;
response_len += ia_response_len;
dhcpv6_log(hdr->msg_type, iface, now, duidbuf, is_pd, a, status);
- }
+ } /* end dhcpv6_for_each_option */
switch (hdr->msg_type) {
case DHCPV6_MSG_RELEASE: