From da6fe470e17fa02f3adedc779585caf8669252bd Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Tue, 24 Jun 2014 16:20:32 +0300 Subject: [PATCH] sd-dhcp6-client: Add Option Request Option support Provide a function to request more options from the DHCPv6 server. Provide a sensible default set at startup and add test basic test cases for the intended usage. Define DNS and NTP related option codes and add comments for the unassigned codes. --- src/libsystemd-network/dhcp6-protocol.h | 12 +++++ src/libsystemd-network/sd-dhcp6-client.c | 57 ++++++++++++++++++++++ src/libsystemd-network/test-dhcp6-client.c | 9 ++++ src/systemd/sd-dhcp6-client.h | 2 + 4 files changed, 80 insertions(+) diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 8b3a81911e5..37a8671a00b 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -111,6 +111,18 @@ enum { DHCP6_OPTION_INTERFACE_ID = 18, DHCP6_OPTION_RECONF_MSG = 19, DHCP6_OPTION_RECONF_ACCEPT = 20, + + DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */ + DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */ + + DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075 */ + + /* option code 35 is unassigned */ + + DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */ + + /* option codes 89-142 are unassigned */ + /* option codes 144-65535 are unassigned */ }; enum { diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 928f562df08..5d65603dc22 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -51,6 +51,9 @@ struct sd_dhcp6_client { be32_t transaction_id; struct sd_dhcp6_lease *lease; int fd; + be16_t *req_opts; + size_t req_opts_allocated; + size_t req_opts_len; sd_event_source *receive_message; usec_t retransmit_time; uint8_t retransmit_count; @@ -66,6 +69,12 @@ struct sd_dhcp6_client { } _packed_ duid; }; +static const uint16_t default_req_opts[] = { + DHCP6_OPTION_DNS_SERVERS, + DHCP6_OPTION_DOMAIN_LIST, + DHCP6_OPTION_NTP_SERVER, +}; + const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = { [DHCP6_SOLICIT] = "SOLICIT", [DHCP6_ADVERTISE] = "ADVERTISE", @@ -137,6 +146,37 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, return 0; } +int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, + uint16_t option) { + size_t t; + + assert_return(client, -EINVAL); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + + switch(option) { + case DHCP6_OPTION_DNS_SERVERS: + case DHCP6_OPTION_DOMAIN_LIST: + case DHCP6_OPTION_SNTP_SERVERS: + case DHCP6_OPTION_NTP_SERVER: + break; + + default: + return -EINVAL; + } + + for (t = 0; t < client->req_opts_len; t++) + if (client->req_opts[t] == htobe16(option)) + return -EEXIST; + + if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated, + client->req_opts_len + 1)) + return -ENOMEM; + + client->req_opts[client->req_opts_len++] = htobe16(option); + + return 0; +} + int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); assert_return(ret, -EINVAL); @@ -239,6 +279,12 @@ static int client_send_message(sd_dhcp6_client *client) { return -EINVAL; } + r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO, + client->req_opts_len * sizeof(be16_t), + client->req_opts); + if (r < 0) + return r; + r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID, sizeof(client->duid), &client->duid); if (r < 0) @@ -927,6 +973,7 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { sd_dhcp6_client_detach_event(client); + free(client->req_opts); free(client); return NULL; @@ -940,6 +987,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL; sd_id128_t machine_id; int r; + size_t t; assert_return(ret, -EINVAL); @@ -968,6 +1016,15 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) siphash24(client->duid.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes); + client->req_opts_len = ELEMENTSOF(default_req_opts); + + client->req_opts = new0(be16_t, client->req_opts_len); + if (!client->req_opts) + return -ENOMEM; + + for (t = 0; t < client->req_opts_len; t++) + client->req_opts[t] = htobe16(default_req_opts[t]); + *ret = client; client = NULL; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index c5729dbc6b2..5bb410dab3f 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -68,6 +68,13 @@ static int test_client_basic(sd_event *e) { assert_se(sd_dhcp6_client_set_mac(client, &mac_addr) >= 0); + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_CLIENTID) == -EINVAL); + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EEXIST); + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_NTP_SERVER) == -EEXIST); + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == 0); + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST); + assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL); + assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0); assert_se(sd_dhcp6_client_detach_event(client) >= 0); @@ -520,6 +527,8 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event, assert_se(e); assert_se(event == DHCP6_EVENT_IP_ACQUIRE); + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY); + if (verbose) printf(" got DHCPv6 event %d\n", event); diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 15e633bb68f..93edcc41fca 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -45,6 +45,8 @@ int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index); int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const struct ether_addr *mac_addr); +int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, + uint16_t option); int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret);