diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index d1881ee5..ff48e8ca 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -1326,3 +1326,318 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac out: return response_len; } + +/* Register or update address registration in the lease database */ +static bool register_ia_addr_in_lease_db(struct sockaddr_in6 *source, + const uint8_t *clientid_data, uint16_t clientid_len, + const char *clientfqdn_data, size_t clientfqdn_len, + const struct dhcpv6_ia_addr *ia_addr, + struct interface *iface) +{ + /* RFC9686 §4.2.1: Lease lifetime is the valid-lifetime from IA Address option */ + time_t now = odhcpd_time(); + uint32_t valid_lt = ntohl(ia_addr->valid_lt); + time_t lease_end = now + valid_lt; + bool onlink = false; + /* variables for logging */ + char addrbuf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &ia_addr->addr, addrbuf, sizeof(addrbuf)); + char duidbuf[DUID_HEXSTRLEN] = {0}; + const char *duidstr = duidbuf; + + /* Search for existing binding with this address */ + struct dhcpv6_lease *binding = NULL, *lease = NULL; + list_for_each_entry(binding, &iface->ia_assignments, head) { + /* Check if this address is already registered by another client */ + if ((binding->flags & OAF_DHCPV6_ADDR_REG) && + memcmp(&binding->peer.sin6_addr, &ia_addr->addr, sizeof(struct in6_addr)) == 0) { + /* Found address registration for this address */ + if (binding->duid_len == clientid_len && + memcmp(binding->duid, clientid_data, clientid_len) == 0) { + /* Same client, update the lease */ + lease = binding; + break; + } else { + /* RFC9686 §4.2.1: Different client, should log and update binding + * We'll update to the new client */ + + if (!binding->duid_len || binding->duid_len > DUID_MAX_LEN) + duidstr = ""; + else + odhcpd_hexlify(duidbuf, binding->duid, binding->duid_len); + + notice("ADDR-REG-INFORM: address collision for %s: was bound to " + "different client DUID(%s); updating", addrbuf, duidstr); + lease = binding; + break; + } + } + } + + if (!lease) { + /* Create new lease for this address registration */ + lease = dhcpv6_alloc_lease(clientid_len); + if (!lease) { + warn("ADDR-REG-INFORM: failed to allocate lease for %s", addrbuf); + return false; + } + + /* Initialize lease structure */ + lease->iface = iface; + lease->peer = *source; + lease->peer.sin6_addr = ia_addr->addr; /* Store full registered address */ + lease->duid_len = clientid_len; + memcpy(lease->duid, clientid_data, clientid_len); + lease->length = 128; + + /* Add optional Client FQDN */ + if (clientfqdn_len > 0 && clientfqdn_data) { + char *tmp = realloc(lease->hostname, clientfqdn_len + 1); + if (tmp) { + lease->hostname = tmp; + memcpy(lease->hostname, clientfqdn_data, clientfqdn_len); + lease->hostname[clientfqdn_len] = 0; + lease->hostname_valid = odhcpd_hostname_valid(lease->hostname); + } + } + + + + /* Try to find matching interface prefix and extract host ID */ + for (size_t i = 0; i < iface->addr6_len; i++) { + if (!valid_addr(&iface->addr6[i], now)) + continue; + + if (ADDR_MATCH_PIO_FILTER(&iface->addr6[i], iface)) + continue; + + /* RFC9686 §4.2.1: the server SHOULD verify that the address + * is "appropriate to the link" */ + if (odhcpd_bmemcmp(&ia_addr->addr, &iface->addr6[i].addr.in6, + iface->addr6[i].prefix_len) == 0) { + /* Address is within this prefix - extract host ID portion */ + onlink = true; + uint64_t host_id = 0; + memcpy(&host_id, &ia_addr->addr.s6_addr[8], 8); + lease->assigned_host_id = be64toh(host_id); + break; + } + } + + if (onlink) { + /* Add to interface's lease list */ + list_add(&lease->head, &iface->ia_assignments); + } else { + /* RFC9686 §4.2.1: If the address fails verification, the server SHOULD log this fact */ + notice("ADDR-REG-INFORM: address %s not on this link: %s; discarding", addrbuf, iface->name); + dhcpv6_free_lease(lease); + lease = NULL; + return onlink; + } + + } else { + /* Update existing lease */ + lease->peer = *source; + lease->peer.sin6_addr = ia_addr->addr; /* Update registered address */ + lease->duid_len = clientid_len; + memcpy(lease->duid, clientid_data, clientid_len); + } + + /* Update lifetimes */ + lease->valid_until = lease_end; + lease->preferred_until = now + ntohl(ia_addr->preferred_lt); + lease->bound = true; + + /* Mark flags - RFC9686 Address Registration lease */ + lease->flags = OAF_DHCPV6_ADDR_REG; + + + if (!lease->duid_len || lease->duid_len > DUID_MAX_LEN) + duidstr = ""; + else + odhcpd_hexlify(duidbuf, lease->duid, lease->duid_len); + + info("ADDR-REG-INFORM: registered %s for client with DUID %s, " + "valid until %ld (in %u seconds)", addrbuf, duidstr, + lease_end, valid_lt); + + return true; +} + +/* Send RFC9686 ADDR-REG-REPLY message to client */ +static void send_ia_addr_reg_reply(struct sockaddr_in6 *source, + const struct dhcpv6_client_header *hdr, + const uint8_t *clientid_data, + uint16_t clientid_len, + const struct dhcpv6_ia_addr *ia_addr, + struct interface *iface) +{ + /* RFC9686 §4.3: Reply MUST contain: + * - msg_type: ADDR-REG-REPLY (37) + * - transaction_id: copied from ADDR-REG-INFORM + * - IA Address option: identical to the one in the request + */ + + struct { + uint8_t msg_type; + uint8_t tr_id[3]; + } _o_packed reply = { + .msg_type = DHCPV6_MSG_ADDR_REG_REPLY, + }; + /* Copy transaction ID from request */ + memcpy(reply.tr_id, hdr->transaction_id, sizeof(reply.tr_id)); + + struct { + uint16_t code; + uint16_t len; + uint8_t data[DUID_MAX_LEN]; + } _o_packed serverid = { + .code = htons(DHCPV6_OPT_SERVERID), + .len = 0, + .data = { 0 }, + }; + struct { + uint16_t code; + uint16_t len; + uint8_t data[DUID_MAX_LEN]; + } _o_packed clientid = { + .code = htons(DHCPV6_OPT_CLIENTID), + .len = htons(clientid_len), + .data = { 0 }, + }; + memcpy(clientid.data, clientid_data, clientid_len); + + if (config.default_duid_len > 0) { + memcpy(serverid.data, config.default_duid, config.default_duid_len); + serverid.len = htons(config.default_duid_len); + } else { + uint16_t duid_ll_hdr[] = { + htons(DUID_TYPE_LL), + htons(ARPHRD_ETHER) + }; + memcpy(serverid.data, duid_ll_hdr, sizeof(duid_ll_hdr)); + odhcpd_get_mac(iface, &serverid.data[sizeof(duid_ll_hdr)]); + serverid.len = htons(sizeof(duid_ll_hdr) + ETH_ALEN); + } + + struct iovec iov[] = { + { &reply, sizeof(reply) }, + { &serverid, sizeof(serverid) }, + { &clientid, sizeof(clientid) }, + { (void *)ia_addr, sizeof(struct dhcpv6_ia_addr) }, + }; + + size_t serverid_len, clientid_opt_len; + + serverid_len = sizeof(serverid.code) + sizeof(serverid.len) + ntohs(serverid.len); + clientid_opt_len = sizeof(clientid.code) + sizeof(clientid.len) + clientid_len; + + iov[1].iov_len = serverid_len; + iov[2].iov_len = clientid_opt_len; + + /* RFC9686 §4.3: If not relayed, destination is the address being registered. + * If relayed, we would construct Relay-reply (handled separately in relay_server_response). + * For direct replies, source is already the registered address. */ + + char addrbuf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &ia_addr->addr, addrbuf, sizeof(addrbuf)); + debug("Sending ADDR-REG-REPLY for %s on %s", addrbuf, iface->name); + + odhcpd_send(iface->dhcpv6_event.uloop.fd, source, iov, ARRAY_SIZE(iov), iface); +} + +/* RFC9686 Address Registration Message Handler */ +void handle_ia_addr_reg_inform(struct sockaddr_in6 *source, + const void *data, size_t len, struct interface *iface) +{ + const struct dhcpv6_client_header *hdr = data; + uint8_t *opts = (uint8_t *)&hdr[1], *opts_end = (uint8_t *)data + len; + uint16_t otype, olen; + uint8_t *odata; + + uint8_t *clientid_data = NULL; + uint16_t clientid_len = 0; + char clientfqdn_data[256]; + size_t clientfqdn_len = 0; + struct dhcpv6_ia_addr *ia_addr = NULL; + + if (len < sizeof(*hdr)) { + warn("ADDR-REG-INFORM: received message too short; dropping"); + return; + } + + /* RFC9686 §4.2: Client MUST include Client Identifier option */ + /* RFC9686 §4.2: ADDR-REG-INFORM MUST NOT contain Server Identifier */ + /* RFC9686 §4.2: MUST contain exactly one IA Address option */ + /* RFC9686 §4.2: MAY include other options, such as the Client FQDN option */ + + dhcpv6_for_each_option(opts, opts_end, otype, olen, odata) { + switch (otype) { + case DHCPV6_OPT_CLIENTID: + if (olen > 0) { + clientid_data = odata; + clientid_len = olen; + } + break; + case DHCPV6_OPT_SERVERID: + /* RFC9686 §4.2: Server MUST discard messages with Server ID */ + notice("ADDR-REG-INFORM: message contains Server Identifier, discarding"); + return; + case DHCPV6_OPT_FQDN: + if (olen >= 2 && olen <= 255) { + 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], clientfqdn_data, sizeof(clientfqdn_data)) > 0) + clientfqdn_len = strcspn(clientfqdn_data, "."); + } + break; + case DHCPV6_OPT_IA_ADDR: + if (olen == sizeof(struct dhcpv6_ia_addr) - 4) { + if (ia_addr != NULL) { + notice("ADDR-REG-INFORM: message contains more than one IA_ADDR, discarding"); + return; + } + ia_addr = (struct dhcpv6_ia_addr *)&odata[-4]; + } + break; + case DHCPV6_OPT_ORO: + /* RFC9686 §4.2: ADDR-REG-INFORM MUST NOT contain Option Request option */ + notice("ADDR-REG-INFORM: message contains Option Request, discarding"); + return; + default: + break; + } + } + + /* Validate message contents */ + if (!clientid_data || clientid_len == 0) { + notice("ADDR-REG-INFORM: missing Client Identifier option, discarding"); + return; + } + + if (!ia_addr) { + notice("ADDR-REG-INFORM: missing or invalid IA Address option, discarding"); + return; + } + + /* RFC9686 §4.2.1: Verify source address matches the IA Address option + * The message MUST be sent from the address being registered */ + if (memcmp(&source->sin6_addr, &ia_addr->addr, sizeof(struct in6_addr)) != 0) { + notice("ADDR-REG-INFORM: source address does not match IA Address option"); + return; + } + + char addrbuf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &ia_addr->addr, addrbuf, sizeof(addrbuf)); + debug("Got ADDR-REG-INFORM for %s on %s", addrbuf, iface->name); + + /* Register address in lease database */ + if(!register_ia_addr_in_lease_db(source, clientid_data, clientid_len, + clientfqdn_data, clientfqdn_len, ia_addr, iface)) + return; + + /* Send ADDR-REG-REPLY response */ + send_ia_addr_reg_reply(source, hdr, clientid_data, clientid_len, ia_addr, iface); +} diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 9d32e6b0..d6a5c4e7 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -31,6 +31,9 @@ static void relay_client_request(struct sockaddr_in6 *source, const void *data, size_t len, struct interface *iface); static void relay_server_response(uint8_t *data, size_t len); +void handle_ia_addr_reg_inform(struct sockaddr_in6 *source, + const void *data, size_t len, struct interface *iface); + static void handle_dhcpv6(void *addr, void *data, size_t len, struct interface *iface, void *dest); static void handle_client_request(void *addr, void *data, size_t len, @@ -167,6 +170,7 @@ enum { IOV_MAXRT, #define IOV_STAT IOV_MAXRT IOV_RAPID_COMMIT, + IOV_ADDR_REG_ENABLE, IOV_DNS, IOV_DNS_ADDR, IOV_SEARCH, @@ -345,6 +349,7 @@ static void handle_client_request(void *addr, void *data, size_t len, /* if we include DHCPV4 support, handle this message type */ case DHCPV6_MSG_DHCPV4_QUERY: #endif + case DHCPV6_MSG_ADDR_REG_INFORM: break; /* Invalid message types for clients i.e. server messages */ case DHCPV6_MSG_ADVERTISE: @@ -356,12 +361,19 @@ static void handle_client_request(void *addr, void *data, size_t len, case DHCPV6_MSG_DHCPV4_QUERY: #endif case DHCPV6_MSG_DHCPV4_RESPONSE: + case DHCPV6_MSG_ADDR_REG_REPLY: default: return; } debug("Got a DHCPv6-request on %s", iface->name); + /* RFC9686 - Handle ADDR-REG-INFORM separately */ + if (hdr->msg_type == DHCPV6_MSG_ADDR_REG_INFORM && iface->dhcpv6 == MODE_SERVER) { + handle_ia_addr_reg_inform((struct sockaddr_in6 *)addr, data, len, iface); + return; + } + /* Construct reply message */ struct _o_packed { uint8_t msg_type; @@ -499,6 +511,16 @@ static void handle_client_request(void *addr, void *data, size_t len, uint16_t len; } capt_portal; + /* RFC9686 Address Registration Enable option */ + bool addr_reg_enable_want = false; + struct { + uint16_t type; + uint16_t len; + } addr_reg_enable = { + htons(DHCPV6_OPT_ADDR_REG_ENABLE), + 0 + }; + /* RFC8910 §2: * DHCP servers MAY send the Captive Portal option without any explicit request * If it is configured, send it. @@ -609,6 +631,11 @@ static void handle_client_request(void *addr, void *data, size_t len, memcpy(&d6dnr->len, &d6dnr_len_be, sizeof(d6dnr_len_be)); } break; + + case DHCPV6_OPT_ADDR_REG_ENABLE: + /* RFC9686: Signal address registration support */ + addr_reg_enable_want = true; + break; } } @@ -633,6 +660,7 @@ static void handle_client_request(void *addr, void *data, size_t len, [IOV_CLIENTID] = {&clientid, 0}, [IOV_MAXRT] = {&maxrt, sizeof(maxrt)}, [IOV_RAPID_COMMIT] = {&rapid_commit, 0}, + [IOV_ADDR_REG_ENABLE] = {&addr_reg_enable, addr_reg_enable_want ? sizeof(addr_reg_enable) : 0}, [IOV_DNS] = { &dns_hdr, (dns_addrs6_cnt) ? sizeof(dns_hdr) : 0}, [IOV_DNS_ADDR] = { dns_addrs6, dns_addrs6_cnt * sizeof(*dns_addrs6) }, [IOV_SEARCH] = { &dns_search_hdr, iface->dns_search_len ? sizeof(dns_search_hdr) : 0 }, @@ -783,6 +811,7 @@ static void handle_client_request(void *addr, void *data, size_t len, iov[IOV_RAPID_COMMIT].iov_len + iov[IOV_DNS].iov_len + iov[IOV_DNS_ADDR].iov_len + iov[IOV_SEARCH].iov_len + iov[IOV_SEARCH_DOMAIN].iov_len + iov[IOV_PDBUF].iov_len + + iov[IOV_ADDR_REG_ENABLE].iov_len + iov[IOV_DHCPV4O6_SERVER].iov_len + iov[IOV_DHCPV6_RAW].iov_len + iov[IOV_NTP].iov_len + iov[IOV_NTP_ADDR].iov_len + @@ -858,6 +887,10 @@ static void relay_server_response(uint8_t *data, size_t len) /* If the payload is relay-reply we have to send to the server port */ if (payload_data[0] == DHCPV6_MSG_RELAY_REPL) { target.sin6_port = htons(DHCPV6_SERVER_PORT); + } else if (payload_data[0] == DHCPV6_MSG_ADDR_REG_REPLY) { + /* RFC9686: Forward ADDR-REG-REPLY back to client */ + /* The client address is in the peer_address field of the relay message */ + /* For relayed ADDR-REG-REPLY, just forward as-is to client port */ } else { /* Go through the payload data */ struct dhcpv6_client_header *dch = (void*)payload_data; end = payload_data + payload_len; @@ -924,6 +957,74 @@ static struct odhcpd_ipaddr *relay_link_address(struct interface *iface) return addr; } +/* Recursively validate ADDR-REG-INFORM messages through relay layers. + * RFC9686 §4.2.1: The IA Address must match the source address of the + * original message (peer-address in innermost relay-forward, or source + * IP if not relayed). Returns true if valid, false if should be discarded. */ +static bool validate_addr_reg_inform(const void *data, size_t len, + const struct in6_addr *peer_addr) +{ + const struct dhcpv6_relay_header *rh = data; + const struct dhcpv6_client_header *ch = data; + + if (len < sizeof(struct dhcpv6_client_header)) + return false; + + /* If this is a relay-forward, unwrap and recurse */ + if (rh->msg_type == DHCPV6_MSG_RELAY_FORW) { + if (len < sizeof(struct dhcpv6_relay_header)) + return false; + + uint16_t otype, olen; + uint8_t *odata; + const uint8_t *end = (const uint8_t *)data + len; + + dhcpv6_for_each_option(rh->options, end, otype, olen, odata) { + if (otype == DHCPV6_OPT_RELAY_MSG) { + /* Recurse into the inner message with the relay's peer address. + * Copy peer_address to a local aligned buffer to avoid + * address-of-packed-member warning. */ + struct in6_addr peer; + memcpy(&peer, &rh->peer_address, sizeof(peer)); + return validate_addr_reg_inform(odata, olen, &peer); + } + } + /* No relay message option found */ + return false; + } + + /* We've reached the innermost client message */ + if (ch->msg_type != DHCPV6_MSG_ADDR_REG_INFORM) + return true; /* Not an ADDR-REG-INFORM, no validation needed */ + + /* Validate that IA_ADDR matches peer address */ + uint16_t otype, olen; + uint8_t *odata; + const uint8_t *start = (const uint8_t *)&ch[1]; + const uint8_t *end = (const uint8_t *)data + len; + + dhcpv6_for_each_option(start, end, otype, olen, odata) { + if (otype != DHCPV6_OPT_IA_NA) + continue; + + struct dhcpv6_ia_hdr *ia = (struct dhcpv6_ia_hdr *)&odata[-4]; + uint8_t *sdata; + uint16_t stype, slen; + + dhcpv6_for_each_sub_option(&ia[1], odata + olen, stype, slen, sdata) { + if (stype != DHCPV6_OPT_IA_ADDR || slen < sizeof(struct dhcpv6_ia_addr) - 4) + continue; + + struct dhcpv6_ia_addr *ia_addr = (struct dhcpv6_ia_addr *)&sdata[-4]; + /* RFC9686 §4.2.1: IA Address must match source/peer address */ + if (memcmp(&ia_addr->addr, peer_addr, sizeof(struct in6_addr)) != 0) + return false; + } + } + + return true; +} + /* Relay client request (regular DHCPv6-relay) */ static void relay_client_request(struct sockaddr_in6 *source, const void *data, size_t len, struct interface *iface) @@ -955,6 +1056,7 @@ static void relay_client_request(struct sockaddr_in6 *source, case DHCPV6_MSG_INFORMATION_REQUEST: case DHCPV6_MSG_RELAY_FORW: case DHCPV6_MSG_DHCPV4_QUERY: + case DHCPV6_MSG_ADDR_REG_INFORM: break; /* Invalid message types from clients i.e. server messages */ case DHCPV6_MSG_ADVERTISE: @@ -962,6 +1064,7 @@ static void relay_client_request(struct sockaddr_in6 *source, case DHCPV6_MSG_RECONFIGURE: case DHCPV6_MSG_RELAY_REPL: case DHCPV6_MSG_DHCPV4_RESPONSE: + case DHCPV6_MSG_ADDR_REG_REPLY: return; default: break; @@ -976,6 +1079,14 @@ static void relay_client_request(struct sockaddr_in6 *source, hdr.hop_count = h->hop_count + 1; } + /* RFC9686 §4.2 "fate sharing" or §4.2.1 + * Validate ADDR-REG-INFORM messages recursively through relay layers. + * The IA Address must match the source address of the original message. */ + if (!validate_addr_reg_inform(data, len, &source->sin6_addr)) { + notice("DHCPv6-relay: Discarding ADDR-REG-INFORM: address does not match source"); + return; + } + /* use memcpy here as the destination fields are unaligned */ memcpy(&hdr.peer_address, &source->sin6_addr, sizeof(struct in6_addr)); memcpy(&hdr.interface_id_data, &iface->ifindex, sizeof(iface->ifindex)); diff --git a/src/dhcpv6.h b/src/dhcpv6.h index 79f8c93c..c55b548a 100644 --- a/src/dhcpv6.h +++ b/src/dhcpv6.h @@ -41,6 +41,9 @@ /* RFC7341 */ #define DHCPV6_MSG_DHCPV4_QUERY 20 #define DHCPV6_MSG_DHCPV4_RESPONSE 21 +/* RFC9686 */ +#define DHCPV6_MSG_ADDR_REG_INFORM 36 +#define DHCPV6_MSG_ADDR_REG_REPLY 37 #define DHCPV6_OPT_CLIENTID 1 #define DHCPV6_OPT_SERVERID 2 @@ -77,6 +80,8 @@ /* RFC8910 */ #define DHCPV6_OPT_CAPTIVE_PORTAL 103 #define DHCPV6_OPT_DNR 144 +/* RFC9686 */ +#define DHCPV6_OPT_ADDR_REG_ENABLE 148 #define DHCPV6_DUID_VENDOR 2 @@ -153,6 +158,12 @@ struct dhcpv6_ia_addr { uint32_t valid_lt; } _o_packed; +/* RFC9686 - Address Registration Option (empty option, no data) */ +struct dhcpv6_addr_reg_enable { + uint16_t type; + uint16_t len; +} _o_packed; + struct dhcpv6_cer_id { uint16_t type; uint16_t len; diff --git a/src/odhcpd.c b/src/odhcpd.c index 9921e1b0..c889030f 100644 --- a/src/odhcpd.c +++ b/src/odhcpd.c @@ -693,6 +693,14 @@ void odhcpd_enum_addr6(struct interface *iface, struct dhcpv6_lease *lease, continue; addr = in6_from_prefix_and_iid(&addrs[i], lease->assigned_host_id); + } else if (lease->flags & OAF_DHCPV6_ADDR_REG) { + /* RFC9686 Address Registration - use full address from peer */ + addr = lease->peer.sin6_addr; + /* For address registration, we only enumerate once with the registered address. + * Otherwise the same address (there's only one IA_Addr) is returned per interface + * prefix. */ + if (i > 0) + continue; } else { if (!valid_prefix_length(lease, addrs[i].prefix_len)) continue; diff --git a/src/odhcpd.h b/src/odhcpd.h index f005909b..17b48c9f 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -192,6 +192,7 @@ enum odhcpd_mode { enum odhcpd_assignment_flags { OAF_DHCPV6_NA = (1 << 0), OAF_DHCPV6_PD = (1 << 1), + OAF_DHCPV6_ADDR_REG = (1 << 2), /* RFC9686 Address Registration */ }; /* 2-byte type + 128-byte DUID, RFC8415, §11.1 */ @@ -613,6 +614,9 @@ void ubus_bcast_dhcpv4_event(const char *type, const char *iface, } #endif /* WITH_UBUS */ +void handle_ia_addr_reg_inform(struct sockaddr_in6 *source, + const void *data, size_t len, struct interface *iface); + ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *iface, const struct sockaddr_in6 *addr, const void *data, const uint8_t *end); int dhcpv6_ia_init(void); diff --git a/src/statefiles.c b/src/statefiles.c index 05c96637..9dc8b0dc 100644 --- a/src/statefiles.c +++ b/src/statefiles.c @@ -373,7 +373,8 @@ static bool statefiles_write_host6(struct write_ctxt *ctxt, struct dhcpv6_lease { char ipbuf[INET6_ADDRSTRLEN]; - if (!lease->hostname || !lease->hostname_valid || !(lease->flags & OAF_DHCPV6_NA)) + if (!lease->hostname || !lease->hostname_valid || + !(lease->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_ADDR_REG))) return false; if (ctxt->fp) { @@ -465,7 +466,8 @@ static void statefiles_write_state6_addr(struct dhcpv6_lease *lease, struct in6_ struct write_ctxt *ctxt = (struct write_ctxt *)arg; char ipbuf[INET6_ADDRSTRLEN]; - if (lease->hostname && lease->hostname_valid && lease->flags & OAF_DHCPV6_NA) { + if (lease->hostname && lease->hostname_valid && + (lease->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_ADDR_REG))) { md5_hash(addr, sizeof(*addr), &ctxt->md5); md5_hash(lease->hostname, strlen(lease->hostname), &ctxt->md5); } @@ -493,7 +495,7 @@ static void statefiles_write_state6(struct write_ctxt *ctxt, struct dhcpv6_lease (lease->valid_until > ctxt->now ? (int64_t)(lease->valid_until - ctxt->now + ctxt->wall_time) : (INFINITE_VALID(lease->valid_until) ? -1 : 0)), - (lease->flags & OAF_DHCPV6_NA ? + ((lease->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_ADDR_REG)) ? lease->assigned_host_id : (uint64_t)lease->assigned_subnet_id), lease->length); diff --git a/src/ubus.c b/src/ubus.c index 413bbfc2..b9469361 100644 --- a/src/ubus.c +++ b/src/ubus.c @@ -147,7 +147,7 @@ static int handle_dhcpv6_leases(_o_unused struct ubus_context *ctx, _o_unused st blobmsg_add_u8(&b, "accept-reconf", a->accept_fr_nonce); if (a->flags & OAF_DHCPV6_NA) blobmsg_add_u64(&b, "assigned", a->assigned_host_id); - else + else if (a->flags & OAF_DHCPV6_PD) blobmsg_add_u16(&b, "assigned", a->assigned_subnet_id); m = blobmsg_open_array(&b, "flags"); @@ -156,9 +156,13 @@ static int handle_dhcpv6_leases(_o_unused struct ubus_context *ctx, _o_unused st if (a->lease_cfg) blobmsg_add_string(&b, NULL, "static"); + + if (a->flags & OAF_DHCPV6_ADDR_REG) + blobmsg_add_string(&b, NULL, "self-generated"); + blobmsg_close_array(&b, m); - m = blobmsg_open_array(&b, a->flags & OAF_DHCPV6_NA ? "ipv6-addr": "ipv6-prefix"); + m = blobmsg_open_array(&b, !(a->flags & OAF_DHCPV6_PD) ? "ipv6-addr": "ipv6-prefix"); odhcpd_enum_addr6(iface, a, now, dhcpv6_blobmsg_ia_addr, NULL); blobmsg_close_array(&b, m);