From: florian Date: Sat, 25 Nov 2023 12:00:39 +0000 (+0000) Subject: First stab at IPv6-only preferred from RFC8925. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=6d7478f075cd153e49d5a994c743847462f5b8a6;p=openbsd First stab at IPv6-only preferred from RFC8925. This lets dhcpleased(8) request "IPv6-only preferred". If the server replies with this option dhcpleased stops and does not request a lease and deconfigures IPv4 on the interface. For now this is pretty much useless unless one dynamically configures pf(4) to act as a CLAT. gelatod(8) from ports can help with this. However, this helps me while hacking on a kernel based stateless CLAT by moving dhcpleased out of the way while having an IPv6-mostly network configured to compare behaviour with macOS. Input jmc OK phessler Input & OK sthen --- diff --git a/sbin/dhcpleased/dhcpleased.conf.5 b/sbin/dhcpleased/dhcpleased.conf.5 index 4d3eb86fbad..27798d74d34 100644 --- a/sbin/dhcpleased/dhcpleased.conf.5 +++ b/sbin/dhcpleased/dhcpleased.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: dhcpleased.conf.5,v 1.12 2023/03/02 17:09:52 jmc Exp $ +.\" $OpenBSD: dhcpleased.conf.5,v 1.13 2023/11/25 12:00:39 florian Exp $ .\" .\" Copyright (c) 2018, 2021 Florian Obser .\" Copyright (c) 2005 Esben Norby @@ -18,7 +18,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: March 2 2023 $ +.Dd $Mdocdate: November 25 2023 $ .Dt DHCPLEASED.CONF 5 .Os .Sh NAME @@ -68,6 +68,9 @@ Ignore leases from .Ar server-ip . This option can be listed multiple times. The default is to not ignore servers. +.It Ic prefer ipv6 +Send the IPv6-Only preferred option to the server. +If the server responds with the option, no lease is configured. .It Ic send client id Ar client-id Send the DHCP client identifier option with a value of .Ar client-id . diff --git a/sbin/dhcpleased/dhcpleased.h b/sbin/dhcpleased/dhcpleased.h index c5d7e204651..80fe9dcf5fe 100644 --- a/sbin/dhcpleased/dhcpleased.h +++ b/sbin/dhcpleased/dhcpleased.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dhcpleased.h,v 1.14 2022/03/21 04:35:41 dlg Exp $ */ +/* $OpenBSD: dhcpleased.h,v 1.15 2023/11/25 12:00:39 florian Exp $ */ /* * Copyright (c) 2017, 2021 Florian Obser @@ -132,6 +132,7 @@ #define DHO_NDS_SERVERS 85 #define DHO_NDS_TREE_NAME 86 #define DHO_NDS_CONTEXT 87 +#define DHO_IPV6_ONLY_PREFERRED 108 #define DHO_DOMAIN_SEARCH 119 #define DHO_CLASSLESS_STATIC_ROUTES 121 #define DHO_TFTP_CONFIG_FILE 144 @@ -258,6 +259,7 @@ struct iface_conf { int ignore; struct in_addr ignore_servers[MAX_SERVERS]; int ignore_servers_len; + int prefer_ipv6; }; struct dhcpleased_conf { diff --git a/sbin/dhcpleased/engine.c b/sbin/dhcpleased/engine.c index b2e34ef6dbc..d435850af1d 100644 --- a/sbin/dhcpleased/engine.c +++ b/sbin/dhcpleased/engine.c @@ -1,4 +1,4 @@ -/* $OpenBSD: engine.c,v 1.39 2023/11/03 15:02:06 tb Exp $ */ +/* $OpenBSD: engine.c,v 1.40 2023/11/25 12:00:39 florian Exp $ */ /* * Copyright (c) 2017, 2021 Florian Obser @@ -70,6 +70,7 @@ enum if_state { IF_REBINDING, /* IF_INIT_REBOOT, */ IF_REBOOTING, + IF_IPV6_ONLY, }; const char* if_state_name[] = { @@ -82,6 +83,7 @@ const char* if_state_name[] = { "Rebinding", /* "Init-Reboot", */ "Rebooting", + "IPv6 only", }; struct dhcpleased_iface { @@ -113,6 +115,7 @@ struct dhcpleased_iface { uint32_t lease_time; uint32_t renewal_time; uint32_t rebinding_time; + uint32_t ipv6_only_time; }; LIST_HEAD(, dhcpleased_iface) dhcpleased_interfaces; @@ -339,6 +342,7 @@ engine_dispatch_frontend(int fd, short event, void *bula) case IF_REBINDING: case IF_REBOOTING: case IF_BOUND: + case IF_IPV6_ONLY: state_transition(iface, IF_REBOOTING); break; } @@ -727,6 +731,7 @@ parse_dhcp(struct dhcpleased_iface *iface, struct imsg_dhcp *dhcp) size_t rem, i; uint32_t sum, usum, lease_time = 0, renewal_time = 0; uint32_t rebinding_time = 0; + uint32_t ipv6_only_time = 0; uint8_t *p, dho = DHO_PAD, dho_len, slen; uint8_t dhcp_message_type = 0; int routes_len = 0, routers = 0, csr = 0; @@ -1173,6 +1178,18 @@ parse_dhcp(struct dhcpleased_iface *iface, struct imsg_dhcp *dhcp) } break; } + case DHO_IPV6_ONLY_PREFERRED: + if (dho_len != sizeof(ipv6_only_time)) + goto wrong_length; + memcpy(&ipv6_only_time, p, sizeof(ipv6_only_time)); + ipv6_only_time = ntohl(ipv6_only_time); + if (log_getverbose() > 1) { + log_debug("DHO_IPV6_ONLY_PREFERRED %us", + ipv6_only_time); + } + p += dho_len; + rem -= dho_len; + break; default: if (log_getverbose() > 1) log_debug("DHO_%u, len: %u", dho, dho_len); @@ -1207,6 +1224,14 @@ parse_dhcp(struct dhcpleased_iface *iface, struct imsg_dhcp *dhcp) "offered IP address", __func__); return; } +#ifndef SMALL + if (iface_conf != NULL && iface_conf->prefer_ipv6 && + ipv6_only_time > 0) { + iface->ipv6_only_time = ipv6_only_time; + state_transition(iface, IF_IPV6_ONLY); + break; + } +#endif iface->server_identifier = server_identifier; iface->dhcp_server = server_identifier; iface->requested_ip = dhcp_hdr->yiaddr; @@ -1307,6 +1332,14 @@ parse_dhcp(struct dhcpleased_iface *iface, struct imsg_dhcp *dhcp) strlcpy(iface->domainname, domainname, sizeof(iface->domainname)); strlcpy(iface->hostname, hostname, sizeof(iface->hostname)); +#ifndef SMALL + if (iface_conf != NULL && iface_conf->prefer_ipv6 && + ipv6_only_time > 0) { + iface->ipv6_only_time = ipv6_only_time; + state_transition(iface, IF_IPV6_ONLY); + break; + } +#endif state_transition(iface, IF_BOUND); break; case DHCPNAK: @@ -1386,6 +1419,7 @@ state_transition(struct dhcpleased_iface *iface, enum if_state new_state) send_deconfigure_interface(iface); /* fall through */ case IF_DOWN: + case IF_IPV6_ONLY: iface->timo.tv_sec = START_EXP_BACKOFF; break; case IF_BOUND: @@ -1434,6 +1468,25 @@ state_transition(struct dhcpleased_iface *iface, enum if_state new_state) iface->timo.tv_sec /= 2; request_dhcp_request(iface); break; + case IF_IPV6_ONLY: + switch (old_state) { + case IF_REQUESTING: + case IF_RENEWING: + case IF_REBINDING: + case IF_REBOOTING: + /* going IPv6 only: delete legacy IP */ + send_rdns_withdraw(iface); + send_deconfigure_interface(iface); + /* fall through */ + case IF_INIT: + case IF_DOWN: + case IF_IPV6_ONLY: + iface->timo.tv_sec = iface->ipv6_only_time; + break; + case IF_BOUND: + fatal("invalid transition Bound -> IPv6 only"); + break; + } } if_name = if_indextoname(iface->if_index, ifnamebuf); @@ -1499,6 +1552,9 @@ iface_timeout(int fd, short events, void *arg) else state_transition(iface, IF_REBINDING); break; + case IF_IPV6_ONLY: + state_transition(iface, IF_REQUESTING); + break; } } @@ -1584,6 +1640,9 @@ request_dhcp_request(struct dhcpleased_iface *iface) imsg.requested_ip.s_addr = INADDR_ANY; /* MUST NOT */ imsg.ciaddr = iface->requested_ip; /* IP address */ break; + case IF_IPV6_ONLY: + fatalx("invalid state IF_IPV6_ONLY in %s", __func__); + break; } engine_imsg_compose_frontend(IMSG_SEND_REQUEST, 0, &imsg, sizeof(imsg)); diff --git a/sbin/dhcpleased/frontend.c b/sbin/dhcpleased/frontend.c index e94a3591c6a..3640f3bdbbc 100644 --- a/sbin/dhcpleased/frontend.c +++ b/sbin/dhcpleased/frontend.c @@ -1,4 +1,4 @@ -/* $OpenBSD: frontend.c,v 1.30 2022/07/14 15:23:09 florian Exp $ */ +/* $OpenBSD: frontend.c,v 1.31 2023/11/25 12:00:39 florian Exp $ */ /* * Copyright (c) 2017, 2021 Florian Obser @@ -924,6 +924,11 @@ build_packet(uint8_t message_type, char *if_name, uint32_t xid, 8, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS, DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS, DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES}; + static uint8_t dhcp_req_list_v6[] = {DHO_DHCP_PARAMETER_REQUEST_LIST, + 9, DHO_SUBNET_MASK, DHO_ROUTERS, DHO_DOMAIN_NAME_SERVERS, + DHO_HOST_NAME, DHO_DOMAIN_NAME, DHO_BROADCAST_ADDRESS, + DHO_DOMAIN_SEARCH, DHO_CLASSLESS_STATIC_ROUTES, + DHO_IPV6_ONLY_PREFERRED}; static uint8_t dhcp_requested_address[] = {DHO_DHCP_REQUESTED_ADDRESS, 4, 0, 0, 0, 0}; static uint8_t dhcp_server_identifier[] = {DHO_DHCP_SERVER_IDENTIFIER, @@ -997,15 +1002,23 @@ build_packet(uint8_t message_type, char *if_name, uint32_t xid, memcpy(p, iface_conf->vc_id, iface_conf->vc_id_len); p += iface_conf->vc_id_len; } + if (iface_conf->prefer_ipv6) { + memcpy(p, dhcp_req_list_v6, sizeof(dhcp_req_list_v6)); + p += sizeof(dhcp_req_list_v6); + + } else { + memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); + p += sizeof(dhcp_req_list); + } } else #endif /* SMALL */ { memcpy(dhcp_client_id + 3, hw_address, sizeof(*hw_address)); memcpy(p, dhcp_client_id, sizeof(dhcp_client_id)); p += sizeof(dhcp_client_id); + memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); + p += sizeof(dhcp_req_list); } - memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); - p += sizeof(dhcp_req_list); if (requested_ip->s_addr != INADDR_ANY) { memcpy(dhcp_requested_address + 2, requested_ip, diff --git a/sbin/dhcpleased/parse.y b/sbin/dhcpleased/parse.y index e3a68953aea..eeae4289e39 100644 --- a/sbin/dhcpleased/parse.y +++ b/sbin/dhcpleased/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.7 2022/03/21 04:35:41 dlg Exp $ */ +/* $OpenBSD: parse.y,v 1.8 2023/11/25 12:00:39 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser @@ -109,7 +109,7 @@ typedef struct { %} %token DHCP_IFACE ERROR SEND VENDOR CLASS ID CLIENT IGNORE DNS ROUTES HOST NAME -%token NO +%token NO PREFER IPV6 %token STRING %token NUMBER @@ -324,6 +324,9 @@ ifaceoptsl : SEND VENDOR CLASS ID STRING { } free($2); } + | PREFER IPV6 { + iface_conf->prefer_ipv6 = 1; + } ; %% @@ -366,8 +369,10 @@ lookup(char *s) {"id", ID}, {"ignore", IGNORE}, {"interface", DHCP_IFACE}, + {"ipv6", IPV6}, {"name", NAME}, {"no", NO}, + {"prefer", PREFER}, {"routes", ROUTES}, {"send", SEND}, {"vendor", VENDOR}, diff --git a/sbin/dhcpleased/printconf.c b/sbin/dhcpleased/printconf.c index 076fe2ec0ad..0ef3970366c 100644 --- a/sbin/dhcpleased/printconf.c +++ b/sbin/dhcpleased/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.4 2022/01/04 06:20:37 florian Exp $ */ +/* $OpenBSD: printconf.c,v 1.5 2023/11/25 12:00:39 florian Exp $ */ /* * Copyright (c) 2018 Florian Obser @@ -126,6 +126,8 @@ print_config(struct dhcpleased_conf *conf) printf("\tignore %s\n", hbuf); } + if (iface->prefer_ipv6) + printf("\t prefer ipv6\n"); printf("}\n"); } }