sd-dhcp-server: support IPv6 only mode

This makes sd-dhcp-server send IPv6 only preferred option (RFC 8925).

Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
This commit is contained in:
Susant Sahani 2023-09-20 09:50:02 +09:00 committed by Yu Watanabe
parent a65f67aeaf
commit 14bd102e3c
4 changed files with 50 additions and 0 deletions

View file

@ -11,6 +11,11 @@
#include "macro.h"
#include "sparse-endian.h"
#include "time-util.h"
/* RFC 8925 - IPv6-Only Preferred Option for DHCPv4 3.4.
* MIN_V6ONLY_WAIT: The lower boundary for V6ONLY_WAIT. Value: 300 seconds */
#define MIN_V6ONLY_WAIT_USEC (300U * USEC_PER_SEC)
struct DHCPMessage {
uint8_t op;

View file

@ -83,6 +83,7 @@ struct sd_dhcp_server {
usec_t max_lease_time;
usec_t default_lease_time;
usec_t ipv6_only_preferred_usec;
sd_dhcp_server_callback_t callback;
void *callback_userdata;
@ -105,6 +106,8 @@ typedef struct DHCPRequest {
usec_t lifetime;
const uint8_t *agent_info_option;
char *hostname;
const uint8_t *parameter_request_list;
size_t parameter_request_list_len;
} DHCPRequest;
extern const struct hash_ops dhcp_lease_hash_ops;

View file

@ -330,6 +330,15 @@ int sd_dhcp_server_stop(sd_dhcp_server *server) {
return 0;
}
static bool dhcp_request_contains(DHCPRequest *req, uint8_t option) {
assert(req);
if (!req->parameter_request_list)
return false;
return memchr(req->parameter_request_list, option, req->parameter_request_list_len);
}
static int dhcp_server_send_unicast_raw(
sd_dhcp_server *server,
uint8_t hlen,
@ -649,6 +658,21 @@ static int server_send_offer_or_ack(
return r;
}
/* RFC 8925 section 3.3. DHCPv4 Server Behavior
* The server MUST NOT include the IPv6-Only Preferred option in the DHCPOFFER or DHCPACK message if
* the option was not present in the Parameter Request List sent by the client. */
if (dhcp_request_contains(req, SD_DHCP_OPTION_IPV6_ONLY_PREFERRED) &&
server->ipv6_only_preferred_usec > 0) {
be32_t sec = usec_to_be32_sec(server->ipv6_only_preferred_usec);
r = dhcp_option_append(
&packet->dhcp, req->max_optlen, &offset, 0,
SD_DHCP_OPTION_IPV6_ONLY_PREFERRED,
sizeof(sec), &sec);
if (r < 0)
return r;
}
ORDERED_SET_FOREACH(j, server->extra_options) {
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
j->option, j->length, j->data);
@ -778,6 +802,10 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
return 0;
}
break;
case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST:
req->parameter_request_list = option;
req->parameter_request_list_len = len;
break;
}
@ -1501,6 +1529,19 @@ int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint64_t t) {
return 0;
}
int sd_dhcp_server_set_ipv6_only_preferred_usec(sd_dhcp_server *server, uint64_t t) {
assert_return(server, -EINVAL);
/* When 0 is set, disables the IPv6 only mode. */
/* Refuse too short timespan unless test mode is enabled. */
if (t > 0 && t < MIN_V6ONLY_WAIT_USEC && !network_test_mode_enabled())
return -EINVAL;
server->ipv6_only_preferred_usec = t;
return 0;
}
int sd_dhcp_server_set_servers(
sd_dhcp_server *server,
sd_dhcp_lease_server_type_t what,

View file

@ -84,6 +84,7 @@ int sd_dhcp_server_set_static_lease(sd_dhcp_server *server, const struct in_addr
int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint64_t t);
int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint64_t t);
int sd_dhcp_server_set_ipv6_only_preferred_usec(sd_dhcp_server *server, uint64_t t);
int sd_dhcp_server_forcerenew(sd_dhcp_server *server);