chiark / gitweb /
sd-dhcp6-client: Add Option Request Option support
[elogind.git] / src / libsystemd-network / sd-dhcp6-client.c
index 97a2ba7ea9ba57ca8a9e173fd2d6dccb8876a29b..5d65603dc229dc3331ab887695c03d0e8be89b0b 100644 (file)
@@ -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",
@@ -95,6 +104,12 @@ const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
 
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
+#define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
+
+#define DHCP6_CLIENT_DONT_DESTROY(client) \
+        _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
+
 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
 
 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
@@ -131,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);
@@ -143,14 +189,9 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
         return 0;
 }
 
-static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
-        if (client->cb) {
-                client = sd_dhcp6_client_ref(client);
+static void client_notify(sd_dhcp6_client *client, int event) {
+        if (client->cb)
                 client->cb(client, event, client->userdata);
-                client = sd_dhcp6_client_unref(client);
-        }
-
-        return client;
 }
 
 static int client_reset(sd_dhcp6_client *client) {
@@ -179,14 +220,14 @@ static int client_reset(sd_dhcp6_client *client) {
         return 0;
 }
 
-static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
-        assert_return(client, NULL);
+static void client_stop(sd_dhcp6_client *client, int error) {
+        DHCP6_CLIENT_DONT_DESTROY(client);
 
-        client = client_notify(client, error);
-        if (client)
-                client_reset(client);
+        assert(client);
 
-        return client;
+        client_notify(client, error);
+
+        client_reset(client);
 }
 
 static int client_send_message(sd_dhcp6_client *client) {
@@ -238,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)
@@ -633,6 +680,7 @@ static int client_receive_advertise(sd_dhcp6_client *client,
 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
                                   void *userdata) {
         sd_dhcp6_client *client = userdata;
+        DHCP6_CLIENT_DONT_DESTROY(client);
         _cleanup_free_ DHCP6Message *message;
         int r, buflen, len;
 
@@ -650,7 +698,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
 
         len = read(fd, message, buflen);
         if ((size_t)len < sizeof(DHCP6Message)) {
-                log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno));
+                log_dhcp6_client(client, "could not receive message from UDP socket: %m");
                 return 0;
         }
 
@@ -704,9 +752,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
                                 return 0;
                         }
 
-                        client = client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
-                        if (!client)
-                                return 0;
+                        client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
                 }
 
                 break;
@@ -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;
@@ -935,14 +982,12 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
         return client;
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
-#define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
-
 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
 {
-        _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
+        _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
         sd_id128_t machine_id;
         int r;
+        size_t t;
 
         assert_return(ret, -EINVAL);
 
@@ -971,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;