LCOV - code coverage report
Current view: top level - libsystemd-network - sd-dhcp6-client.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 496 801 61.9 %
Date: 2019-08-22 15:41:25 Functions: 39 49 79.6 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : /***
       3             :   Copyright © 2014-2015 Intel Corporation. All rights reserved.
       4             : ***/
       5             : 
       6             : #include <errno.h>
       7             : #include <string.h>
       8             : #include <sys/ioctl.h>
       9             : #include <linux/if_arp.h>
      10             : #include <linux/if_infiniband.h>
      11             : 
      12             : #include "sd-dhcp6-client.h"
      13             : 
      14             : #include "alloc-util.h"
      15             : #include "dhcp-identifier.h"
      16             : #include "dhcp6-internal.h"
      17             : #include "dhcp6-lease-internal.h"
      18             : #include "dhcp6-protocol.h"
      19             : #include "dns-domain.h"
      20             : #include "event-util.h"
      21             : #include "fd-util.h"
      22             : #include "hostname-util.h"
      23             : #include "in-addr-util.h"
      24             : #include "network-internal.h"
      25             : #include "random-util.h"
      26             : #include "socket-util.h"
      27             : #include "string-table.h"
      28             : #include "util.h"
      29             : 
      30             : #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
      31             : 
      32             : /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
      33             : enum {
      34             :         DHCP6_REQUEST_IA_NA                     = 1,
      35             :         DHCP6_REQUEST_IA_TA                     = 2, /* currently not used */
      36             :         DHCP6_REQUEST_IA_PD                     = 4,
      37             : };
      38             : 
      39             : struct sd_dhcp6_client {
      40             :         unsigned n_ref;
      41             : 
      42             :         enum DHCP6State state;
      43             :         sd_event *event;
      44             :         int event_priority;
      45             :         int ifindex;
      46             :         struct in6_addr local_address;
      47             :         uint8_t mac_addr[MAX_MAC_ADDR_LEN];
      48             :         size_t mac_addr_len;
      49             :         uint16_t arp_type;
      50             :         DHCP6IA ia_na;
      51             :         DHCP6IA ia_pd;
      52             :         sd_event_source *timeout_t1;
      53             :         sd_event_source *timeout_t2;
      54             :         unsigned request;
      55             :         be32_t transaction_id;
      56             :         usec_t transaction_start;
      57             :         struct sd_dhcp6_lease *lease;
      58             :         int fd;
      59             :         bool information_request;
      60             :         bool iaid_set;
      61             :         be16_t *req_opts;
      62             :         size_t req_opts_allocated;
      63             :         size_t req_opts_len;
      64             :         char *fqdn;
      65             :         sd_event_source *receive_message;
      66             :         usec_t retransmit_time;
      67             :         uint8_t retransmit_count;
      68             :         sd_event_source *timeout_resend;
      69             :         sd_event_source *timeout_resend_expire;
      70             :         sd_dhcp6_client_callback_t callback;
      71             :         void *userdata;
      72             :         struct duid duid;
      73             :         size_t duid_len;
      74             : };
      75             : 
      76             : static const uint16_t default_req_opts[] = {
      77             :         SD_DHCP6_OPTION_DNS_SERVERS,
      78             :         SD_DHCP6_OPTION_DOMAIN_LIST,
      79             :         SD_DHCP6_OPTION_NTP_SERVER,
      80             :         SD_DHCP6_OPTION_SNTP_SERVERS,
      81             : };
      82             : 
      83             : const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
      84             :         [DHCP6_SOLICIT] = "SOLICIT",
      85             :         [DHCP6_ADVERTISE] = "ADVERTISE",
      86             :         [DHCP6_REQUEST] = "REQUEST",
      87             :         [DHCP6_CONFIRM] = "CONFIRM",
      88             :         [DHCP6_RENEW] = "RENEW",
      89             :         [DHCP6_REBIND] = "REBIND",
      90             :         [DHCP6_REPLY] = "REPLY",
      91             :         [DHCP6_RELEASE] = "RELEASE",
      92             :         [DHCP6_DECLINE] = "DECLINE",
      93             :         [DHCP6_RECONFIGURE] = "RECONFIGURE",
      94             :         [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
      95             :         [DHCP6_RELAY_FORW] = "RELAY-FORW",
      96             :         [DHCP6_RELAY_REPL] = "RELAY-REPL",
      97             : };
      98             : 
      99          38 : DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
     100             : 
     101             : const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
     102             :         [DHCP6_STATUS_SUCCESS] = "Success",
     103             :         [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
     104             :         [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
     105             :         [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
     106             :         [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
     107             :         [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
     108             : };
     109             : 
     110          16 : DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
     111             : 
     112             : #define DHCP6_CLIENT_DONT_DESTROY(client) \
     113             :         _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
     114             : 
     115             : static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
     116             : 
     117           4 : int sd_dhcp6_client_set_callback(
     118             :                 sd_dhcp6_client *client,
     119             :                 sd_dhcp6_client_callback_t cb,
     120             :                 void *userdata) {
     121             : 
     122           4 :         assert_return(client, -EINVAL);
     123             : 
     124           4 :         client->callback = cb;
     125           4 :         client->userdata = userdata;
     126             : 
     127           4 :         return 0;
     128             : }
     129             : 
     130           5 : int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
     131             : 
     132           5 :         assert_return(client, -EINVAL);
     133           5 :         assert_return(ifindex >= -1, -EINVAL);
     134           4 :         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
     135             : 
     136           4 :         client->ifindex = ifindex;
     137           4 :         return 0;
     138             : }
     139             : 
     140           2 : int sd_dhcp6_client_set_local_address(
     141             :                 sd_dhcp6_client *client,
     142             :                 const struct in6_addr *local_address) {
     143             : 
     144           2 :         assert_return(client, -EINVAL);
     145           2 :         assert_return(local_address, -EINVAL);
     146           2 :         assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
     147             : 
     148           2 :         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
     149             : 
     150           2 :         client->local_address = *local_address;
     151             : 
     152           2 :         return 0;
     153             : }
     154             : 
     155           2 : int sd_dhcp6_client_set_mac(
     156             :                 sd_dhcp6_client *client,
     157             :                 const uint8_t *addr, size_t addr_len,
     158             :                 uint16_t arp_type) {
     159             : 
     160           2 :         assert_return(client, -EINVAL);
     161           2 :         assert_return(addr, -EINVAL);
     162           2 :         assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
     163           2 :         assert_return(arp_type > 0, -EINVAL);
     164             : 
     165           2 :         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
     166             : 
     167           2 :         if (arp_type == ARPHRD_ETHER)
     168           2 :                 assert_return(addr_len == ETH_ALEN, -EINVAL);
     169           0 :         else if (arp_type == ARPHRD_INFINIBAND)
     170           0 :                 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
     171             :         else
     172           0 :                 return -EINVAL;
     173             : 
     174           2 :         if (client->mac_addr_len == addr_len &&
     175           0 :             memcmp(&client->mac_addr, addr, addr_len) == 0)
     176           0 :                 return 0;
     177             : 
     178           2 :         memcpy(&client->mac_addr, addr, addr_len);
     179           2 :         client->mac_addr_len = addr_len;
     180           2 :         client->arp_type = arp_type;
     181             : 
     182           2 :         return 0;
     183             : }
     184             : 
     185           2 : static int client_ensure_duid(sd_dhcp6_client *client) {
     186           2 :         if (client->duid_len != 0)
     187           1 :                 return 0;
     188             : 
     189           1 :         return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
     190             : }
     191             : 
     192             : /**
     193             :  * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
     194             :  * without further modification. Otherwise, if duid_type is supported, DUID
     195             :  * is set based on that type. Otherwise, an error is returned.
     196             :  */
     197           0 : static int dhcp6_client_set_duid_internal(
     198             :                 sd_dhcp6_client *client,
     199             :                 uint16_t duid_type,
     200             :                 const void *duid,
     201             :                 size_t duid_len,
     202             :                 usec_t llt_time) {
     203             :         int r;
     204             : 
     205           0 :         assert_return(client, -EINVAL);
     206           0 :         assert_return(duid_len == 0 || duid != NULL, -EINVAL);
     207           0 :         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
     208             : 
     209           0 :         if (duid != NULL) {
     210           0 :                 r = dhcp_validate_duid_len(duid_type, duid_len, true);
     211           0 :                 if (r < 0) {
     212           0 :                         r = dhcp_validate_duid_len(duid_type, duid_len, false);
     213           0 :                         if (r < 0)
     214           0 :                                 return r;
     215           0 :                         log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type);
     216             :                 }
     217             : 
     218           0 :                 client->duid.type = htobe16(duid_type);
     219           0 :                 memcpy(&client->duid.raw.data, duid, duid_len);
     220           0 :                 client->duid_len = sizeof(client->duid.type) + duid_len;
     221             :         } else
     222           0 :                 switch (duid_type) {
     223           0 :                 case DUID_TYPE_LLT:
     224           0 :                         if (client->mac_addr_len == 0)
     225           0 :                                 return -EOPNOTSUPP;
     226             : 
     227           0 :                         r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
     228           0 :                         if (r < 0)
     229           0 :                                 return r;
     230           0 :                         break;
     231           0 :                 case DUID_TYPE_EN:
     232           0 :                         r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
     233           0 :                         if (r < 0)
     234           0 :                                 return r;
     235           0 :                         break;
     236           0 :                 case DUID_TYPE_LL:
     237           0 :                         if (client->mac_addr_len == 0)
     238           0 :                                 return -EOPNOTSUPP;
     239             : 
     240           0 :                         r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
     241           0 :                         if (r < 0)
     242           0 :                                 return r;
     243           0 :                         break;
     244           0 :                 case DUID_TYPE_UUID:
     245           0 :                         r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
     246           0 :                         if (r < 0)
     247           0 :                                 return r;
     248           0 :                         break;
     249           0 :                 default:
     250           0 :                         return -EINVAL;
     251             :                 }
     252             : 
     253           0 :         return 0;
     254             : }
     255             : 
     256           0 : int sd_dhcp6_client_set_duid(
     257             :                 sd_dhcp6_client *client,
     258             :                 uint16_t duid_type,
     259             :                 const void *duid,
     260             :                 size_t duid_len) {
     261           0 :         return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
     262             : }
     263             : 
     264           0 : int sd_dhcp6_client_set_duid_llt(
     265             :                 sd_dhcp6_client *client,
     266             :                 usec_t llt_time) {
     267           0 :         return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
     268             : }
     269             : 
     270           0 : int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
     271           0 :         assert_return(client, -EINVAL);
     272           0 :         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
     273             : 
     274           0 :         client->ia_na.ia_na.id = htobe32(iaid);
     275           0 :         client->ia_pd.ia_pd.id = htobe32(iaid);
     276           0 :         client->iaid_set = true;
     277             : 
     278           0 :         return 0;
     279             : }
     280             : 
     281           6 : int sd_dhcp6_client_set_fqdn(
     282             :                 sd_dhcp6_client *client,
     283             :                 const char *fqdn) {
     284             : 
     285           6 :         assert_return(client, -EINVAL);
     286             : 
     287             :         /* Make sure FQDN qualifies as DNS and as Linux hostname */
     288           6 :         if (fqdn &&
     289           5 :             !(hostname_is_valid(fqdn, false) && dns_name_is_valid(fqdn) > 0))
     290           2 :                 return -EINVAL;
     291             : 
     292           4 :         return free_and_strdup(&client->fqdn, fqdn);
     293             : }
     294             : 
     295           5 : int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
     296           5 :         assert_return(client, -EINVAL);
     297           5 :         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
     298             : 
     299           4 :         client->information_request = enabled;
     300             : 
     301           4 :         return 0;
     302             : }
     303             : 
     304           4 : int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
     305           4 :         assert_return(client, -EINVAL);
     306           4 :         assert_return(enabled, -EINVAL);
     307             : 
     308           4 :         *enabled = client->information_request;
     309             : 
     310           4 :         return 0;
     311             : }
     312             : 
     313           7 : int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
     314             :         size_t t;
     315             : 
     316           7 :         assert_return(client, -EINVAL);
     317           7 :         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
     318             : 
     319           6 :         switch(option) {
     320             : 
     321           4 :         case SD_DHCP6_OPTION_DNS_SERVERS:
     322             :         case SD_DHCP6_OPTION_DOMAIN_LIST:
     323             :         case SD_DHCP6_OPTION_SNTP_SERVERS:
     324             :         case SD_DHCP6_OPTION_NTP_SERVER:
     325             :         case SD_DHCP6_OPTION_RAPID_COMMIT:
     326           4 :                 break;
     327             : 
     328           2 :         default:
     329           2 :                 return -EINVAL;
     330             :         }
     331             : 
     332          10 :         for (t = 0; t < client->req_opts_len; t++)
     333          10 :                 if (client->req_opts[t] == htobe16(option))
     334           4 :                         return -EEXIST;
     335             : 
     336           0 :         if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
     337             :                             client->req_opts_len + 1))
     338           0 :                 return -ENOMEM;
     339             : 
     340           0 :         client->req_opts[client->req_opts_len++] = htobe16(option);
     341             : 
     342           0 :         return 0;
     343             : }
     344             : 
     345           1 : int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
     346           1 :         assert_return(client, -EINVAL);
     347           1 :         assert_return(delegation, -EINVAL);
     348             : 
     349           1 :         *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD);
     350             : 
     351           1 :         return 0;
     352             : }
     353             : 
     354           1 : int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
     355           1 :         assert_return(client, -EINVAL);
     356             : 
     357           1 :         SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation);
     358             : 
     359           1 :         return 0;
     360             : }
     361             : 
     362           4 : int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
     363           4 :         assert_return(client, -EINVAL);
     364           4 :         assert_return(request, -EINVAL);
     365             : 
     366           4 :         *request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA);
     367             : 
     368           4 :         return 0;
     369             : }
     370             : 
     371           3 : int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
     372           3 :         assert_return(client, -EINVAL);
     373             : 
     374           3 :         SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request);
     375             : 
     376           3 :         return 0;
     377             : }
     378             : 
     379           0 : int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
     380           0 :         assert_return(client, -EINVAL);
     381             : 
     382           0 :         client->transaction_id = transaction_id;
     383             : 
     384           0 :         return 0;
     385             : }
     386             : 
     387           2 : int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
     388           2 :         assert_return(client, -EINVAL);
     389             : 
     390           2 :         if (!client->lease)
     391           0 :                 return -ENOMSG;
     392             : 
     393           2 :         if (ret)
     394           2 :                 *ret = client->lease;
     395             : 
     396           2 :         return 0;
     397             : }
     398             : 
     399           3 : static void client_notify(sd_dhcp6_client *client, int event) {
     400           3 :         assert(client);
     401             : 
     402           3 :         if (client->callback)
     403           2 :                 client->callback(client, event, client->userdata);
     404           3 : }
     405             : 
     406           5 : static int client_reset(sd_dhcp6_client *client) {
     407           5 :         assert(client);
     408             : 
     409           5 :         client->lease = sd_dhcp6_lease_unref(client->lease);
     410             : 
     411           5 :         client->receive_message =
     412           5 :                 sd_event_source_unref(client->receive_message);
     413             : 
     414           5 :         client->transaction_id = 0;
     415           5 :         client->transaction_start = 0;
     416             : 
     417           5 :         client->retransmit_time = 0;
     418           5 :         client->retransmit_count = 0;
     419             : 
     420           5 :         (void) event_source_disable(client->timeout_resend);
     421           5 :         (void) event_source_disable(client->timeout_resend_expire);
     422           5 :         (void) event_source_disable(client->timeout_t1);
     423           5 :         (void) event_source_disable(client->timeout_t2);
     424             : 
     425           5 :         client->state = DHCP6_STATE_STOPPED;
     426             : 
     427           5 :         return 0;
     428             : }
     429             : 
     430           1 : static void client_stop(sd_dhcp6_client *client, int error) {
     431           2 :         DHCP6_CLIENT_DONT_DESTROY(client);
     432             : 
     433           1 :         assert(client);
     434             : 
     435           1 :         client_notify(client, error);
     436             : 
     437           1 :         client_reset(client);
     438           1 : }
     439             : 
     440           3 : static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
     441           3 :         _cleanup_free_ DHCP6Message *message = NULL;
     442           3 :         struct in6_addr all_servers =
     443             :                 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
     444           3 :         size_t len, optlen = 512;
     445             :         uint8_t *opt;
     446             :         int r;
     447             :         usec_t elapsed_usec;
     448             :         be16_t elapsed_time;
     449             : 
     450           3 :         assert(client);
     451             : 
     452           3 :         len = sizeof(DHCP6Message) + optlen;
     453             : 
     454           3 :         message = malloc0(len);
     455           3 :         if (!message)
     456           0 :                 return -ENOMEM;
     457             : 
     458           3 :         opt = (uint8_t *)(message + 1);
     459             : 
     460           3 :         message->transaction_id = client->transaction_id;
     461             : 
     462           3 :         switch(client->state) {
     463           1 :         case DHCP6_STATE_INFORMATION_REQUEST:
     464           1 :                 message->type = DHCP6_INFORMATION_REQUEST;
     465             : 
     466           1 :                 break;
     467             : 
     468           1 :         case DHCP6_STATE_SOLICITATION:
     469           1 :                 message->type = DHCP6_SOLICIT;
     470             : 
     471           1 :                 r = dhcp6_option_append(&opt, &optlen,
     472             :                                         SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
     473           1 :                 if (r < 0)
     474           0 :                         return r;
     475             : 
     476           1 :                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
     477           1 :                         r = dhcp6_option_append_ia(&opt, &optlen,
     478           1 :                                                    &client->ia_na);
     479           1 :                         if (r < 0)
     480           0 :                                 return r;
     481             :                 }
     482             : 
     483           1 :                 if (client->fqdn) {
     484           1 :                         r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
     485           1 :                         if (r < 0)
     486           0 :                                 return r;
     487             :                 }
     488             : 
     489           1 :                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
     490           0 :                         r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
     491           0 :                         if (r < 0)
     492           0 :                                 return r;
     493             : 
     494           0 :                         opt += r;
     495           0 :                         optlen -= r;
     496             :                 }
     497             : 
     498           1 :                 break;
     499             : 
     500           1 :         case DHCP6_STATE_REQUEST:
     501             :         case DHCP6_STATE_RENEW:
     502             : 
     503           1 :                 if (client->state == DHCP6_STATE_REQUEST)
     504           1 :                         message->type = DHCP6_REQUEST;
     505             :                 else
     506           0 :                         message->type = DHCP6_RENEW;
     507             : 
     508           1 :                 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
     509           1 :                                         client->lease->serverid_len,
     510           1 :                                         client->lease->serverid);
     511           1 :                 if (r < 0)
     512           0 :                         return r;
     513             : 
     514           1 :                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
     515           1 :                         r = dhcp6_option_append_ia(&opt, &optlen,
     516           1 :                                                    &client->lease->ia);
     517           1 :                         if (r < 0)
     518           0 :                                 return r;
     519             :                 }
     520             : 
     521           1 :                 if (client->fqdn) {
     522           1 :                         r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
     523           1 :                         if (r < 0)
     524           0 :                                 return r;
     525             :                 }
     526             : 
     527           1 :                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
     528           0 :                         r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
     529           0 :                         if (r < 0)
     530           0 :                                 return r;
     531             : 
     532           0 :                         opt += r;
     533           0 :                         optlen -= r;
     534             :                 }
     535             : 
     536           1 :                 break;
     537             : 
     538           0 :         case DHCP6_STATE_REBIND:
     539           0 :                 message->type = DHCP6_REBIND;
     540             : 
     541           0 :                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
     542           0 :                         r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
     543           0 :                         if (r < 0)
     544           0 :                                 return r;
     545             :                 }
     546             : 
     547           0 :                 if (client->fqdn) {
     548           0 :                         r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
     549           0 :                         if (r < 0)
     550           0 :                                 return r;
     551             :                 }
     552             : 
     553           0 :                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
     554           0 :                         r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
     555           0 :                         if (r < 0)
     556           0 :                                 return r;
     557             : 
     558           0 :                         opt += r;
     559           0 :                         optlen -= r;
     560             :                 }
     561             : 
     562           0 :                 break;
     563             : 
     564           0 :         case DHCP6_STATE_STOPPED:
     565             :         case DHCP6_STATE_BOUND:
     566           0 :                 return -EINVAL;
     567             :         }
     568             : 
     569           6 :         r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
     570           3 :                                 client->req_opts_len * sizeof(be16_t),
     571           3 :                                 client->req_opts);
     572           3 :         if (r < 0)
     573           0 :                 return r;
     574             : 
     575           3 :         assert(client->duid_len);
     576           3 :         r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
     577           3 :                                 client->duid_len, &client->duid);
     578           3 :         if (r < 0)
     579           0 :                 return r;
     580             : 
     581           3 :         elapsed_usec = time_now - client->transaction_start;
     582           3 :         if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
     583           3 :                 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
     584             :         else
     585           0 :                 elapsed_time = 0xffff;
     586             : 
     587           3 :         r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
     588             :                                 sizeof(elapsed_time), &elapsed_time);
     589           3 :         if (r < 0)
     590           0 :                 return r;
     591             : 
     592           3 :         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
     593             :                                           len - optlen);
     594           3 :         if (r < 0)
     595           0 :                 return r;
     596             : 
     597           3 :         log_dhcp6_client(client, "Sent %s",
     598             :                          dhcp6_message_type_to_string(message->type));
     599             : 
     600           3 :         return 0;
     601             : }
     602             : 
     603           0 : static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
     604           0 :         sd_dhcp6_client *client = userdata;
     605             : 
     606           0 :         assert(s);
     607           0 :         assert(client);
     608           0 :         assert(client->lease);
     609             : 
     610           0 :         (void) event_source_disable(client->timeout_t2);
     611             : 
     612           0 :         log_dhcp6_client(client, "Timeout T2");
     613             : 
     614           0 :         client_start(client, DHCP6_STATE_REBIND);
     615             : 
     616           0 :         return 0;
     617             : }
     618             : 
     619           0 : static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
     620           0 :         sd_dhcp6_client *client = userdata;
     621             : 
     622           0 :         assert(s);
     623           0 :         assert(client);
     624           0 :         assert(client->lease);
     625             : 
     626           0 :         (void) event_source_disable(client->timeout_t1);
     627             : 
     628           0 :         log_dhcp6_client(client, "Timeout T1");
     629             : 
     630           0 :         client_start(client, DHCP6_STATE_RENEW);
     631             : 
     632           0 :         return 0;
     633             : }
     634             : 
     635           0 : static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
     636           0 :         sd_dhcp6_client *client = userdata;
     637           0 :         DHCP6_CLIENT_DONT_DESTROY(client);
     638             :         enum DHCP6State state;
     639             : 
     640           0 :         assert(s);
     641           0 :         assert(client);
     642           0 :         assert(client->event);
     643             : 
     644           0 :         state = client->state;
     645             : 
     646           0 :         client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
     647             : 
     648             :         /* RFC 3315, section 18.1.4., says that "...the client may choose to
     649             :            use a Solicit message to locate a new DHCP server..." */
     650           0 :         if (state == DHCP6_STATE_REBIND)
     651           0 :                 client_start(client, DHCP6_STATE_SOLICITATION);
     652             : 
     653           0 :         return 0;
     654             : }
     655             : 
     656           5 : static usec_t client_timeout_compute_random(usec_t val) {
     657          15 :         return val - val / 10 +
     658           5 :                 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
     659             : }
     660             : 
     661           4 : static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
     662           4 :         int r = 0;
     663           4 :         sd_dhcp6_client *client = userdata;
     664           4 :         usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
     665           4 :         usec_t max_retransmit_duration = 0;
     666           4 :         uint8_t max_retransmit_count = 0;
     667             :         char time_string[FORMAT_TIMESPAN_MAX];
     668           4 :         uint32_t expire = 0;
     669             : 
     670           4 :         assert(s);
     671           4 :         assert(client);
     672           4 :         assert(client->event);
     673             : 
     674           4 :         (void) event_source_disable(client->timeout_resend);
     675             : 
     676           4 :         switch (client->state) {
     677           1 :         case DHCP6_STATE_INFORMATION_REQUEST:
     678           1 :                 init_retransmit_time = DHCP6_INF_TIMEOUT;
     679           1 :                 max_retransmit_time = DHCP6_INF_MAX_RT;
     680             : 
     681           1 :                 break;
     682             : 
     683           2 :         case DHCP6_STATE_SOLICITATION:
     684             : 
     685           2 :                 if (client->retransmit_count && client->lease) {
     686           1 :                         client_start(client, DHCP6_STATE_REQUEST);
     687           1 :                         return 0;
     688             :                 }
     689             : 
     690           1 :                 init_retransmit_time = DHCP6_SOL_TIMEOUT;
     691           1 :                 max_retransmit_time = DHCP6_SOL_MAX_RT;
     692             : 
     693           1 :                 break;
     694             : 
     695           1 :         case DHCP6_STATE_REQUEST:
     696           1 :                 init_retransmit_time = DHCP6_REQ_TIMEOUT;
     697           1 :                 max_retransmit_time = DHCP6_REQ_MAX_RT;
     698           1 :                 max_retransmit_count = DHCP6_REQ_MAX_RC;
     699             : 
     700           1 :                 break;
     701             : 
     702           0 :         case DHCP6_STATE_RENEW:
     703           0 :                 init_retransmit_time = DHCP6_REN_TIMEOUT;
     704           0 :                 max_retransmit_time = DHCP6_REN_MAX_RT;
     705             : 
     706             :                 /* RFC 3315, section 18.1.3. says max retransmit duration will
     707             :                    be the remaining time until T2. Instead of setting MRD,
     708             :                    wait for T2 to trigger with the same end result */
     709             : 
     710           0 :                 break;
     711             : 
     712           0 :         case DHCP6_STATE_REBIND:
     713           0 :                 init_retransmit_time = DHCP6_REB_TIMEOUT;
     714           0 :                 max_retransmit_time = DHCP6_REB_MAX_RT;
     715             : 
     716           0 :                 if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
     717           0 :                         r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
     718             :                                                          &expire);
     719           0 :                         if (r < 0) {
     720           0 :                                 client_stop(client, r);
     721           0 :                                 return 0;
     722             :                         }
     723           0 :                         max_retransmit_duration = expire * USEC_PER_SEC;
     724             :                 }
     725             : 
     726           0 :                 break;
     727             : 
     728           0 :         case DHCP6_STATE_STOPPED:
     729             :         case DHCP6_STATE_BOUND:
     730           0 :                 return 0;
     731             :         }
     732             : 
     733           3 :         if (max_retransmit_count &&
     734           1 :             client->retransmit_count >= max_retransmit_count) {
     735           0 :                 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
     736           0 :                 return 0;
     737             :         }
     738             : 
     739           3 :         r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
     740           3 :         if (r < 0)
     741           0 :                 goto error;
     742             : 
     743           3 :         r = client_send_message(client, time_now);
     744           3 :         if (r >= 0)
     745           3 :                 client->retransmit_count++;
     746             : 
     747           3 :         if (!client->retransmit_time) {
     748           3 :                 client->retransmit_time =
     749           3 :                         client_timeout_compute_random(init_retransmit_time);
     750             : 
     751           3 :                 if (client->state == DHCP6_STATE_SOLICITATION)
     752           1 :                         client->retransmit_time += init_retransmit_time / 10;
     753             : 
     754             :         } else {
     755           0 :                 if (max_retransmit_time &&
     756           0 :                     client->retransmit_time > max_retransmit_time / 2)
     757           0 :                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
     758             :                 else
     759           0 :                         client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
     760             :         }
     761             : 
     762           3 :         log_dhcp6_client(client, "Next retransmission in %s",
     763             :                          format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
     764             : 
     765           3 :         r = event_reset_time(client->event, &client->timeout_resend,
     766             :                              clock_boottime_or_monotonic(),
     767           3 :                              time_now + client->retransmit_time, 10 * USEC_PER_MSEC,
     768             :                              client_timeout_resend, client,
     769           3 :                              client->event_priority, "dhcp6-resend-timer", true);
     770           3 :         if (r < 0)
     771           0 :                 goto error;
     772             : 
     773           3 :         if (max_retransmit_duration && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
     774             : 
     775           0 :                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
     776             :                                  max_retransmit_duration / USEC_PER_SEC);
     777             : 
     778           0 :                 r = event_reset_time(client->event, &client->timeout_resend_expire,
     779             :                                      clock_boottime_or_monotonic(),
     780             :                                      time_now + max_retransmit_duration, USEC_PER_SEC,
     781             :                                      client_timeout_resend_expire, client,
     782           0 :                                      client->event_priority, "dhcp6-resend-expire-timer", true);
     783           0 :                 if (r < 0)
     784           0 :                         goto error;
     785             :         }
     786             : 
     787           3 : error:
     788           3 :         if (r < 0)
     789           0 :                 client_stop(client, r);
     790             : 
     791           3 :         return 0;
     792             : }
     793             : 
     794           2 : static int client_ensure_iaid(sd_dhcp6_client *client) {
     795             :         int r;
     796             :         uint32_t iaid;
     797             : 
     798           2 :         assert(client);
     799             : 
     800           2 :         if (client->iaid_set)
     801           1 :                 return 0;
     802             : 
     803           1 :         r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid);
     804           1 :         if (r < 0)
     805           0 :                 return r;
     806             : 
     807           1 :         client->ia_na.ia_na.id = iaid;
     808           1 :         client->ia_pd.ia_pd.id = iaid;
     809           1 :         client->iaid_set = true;
     810             : 
     811           1 :         return 0;
     812             : }
     813             : 
     814           3 : static int client_parse_message(
     815             :                 sd_dhcp6_client *client,
     816             :                 DHCP6Message *message,
     817             :                 size_t len,
     818             :                 sd_dhcp6_lease *lease) {
     819             : 
     820           3 :         uint32_t lt_t1 = ~0, lt_t2 = ~0;
     821           3 :         bool clientid = false;
     822           3 :         size_t pos = 0;
     823             :         int r;
     824             : 
     825           3 :         assert(client);
     826           3 :         assert(message);
     827           3 :         assert(len >= sizeof(DHCP6Message));
     828           3 :         assert(lease);
     829             : 
     830           3 :         len -= sizeof(DHCP6Message);
     831             : 
     832          22 :         while (pos < len) {
     833          19 :                 DHCP6Option *option = (DHCP6Option *) &message->options[pos];
     834             :                 uint16_t optcode, optlen;
     835             :                 be32_t iaid_lease;
     836             :                 uint8_t *optval;
     837             :                 int status;
     838             : 
     839          19 :                 if (len < pos + offsetof(DHCP6Option, data))
     840           0 :                         return -ENOBUFS;
     841             : 
     842          19 :                 optcode = be16toh(option->code);
     843          19 :                 optlen = be16toh(option->len);
     844          19 :                 optval = option->data;
     845             : 
     846          19 :                 if (len < pos + offsetof(DHCP6Option, data) + optlen)
     847           0 :                         return -ENOBUFS;
     848             : 
     849          19 :                 switch (optcode) {
     850           3 :                 case SD_DHCP6_OPTION_CLIENTID:
     851           3 :                         if (clientid) {
     852           0 :                                 log_dhcp6_client(client, "%s contains multiple clientids",
     853             :                                                  dhcp6_message_type_to_string(message->type));
     854           0 :                                 return -EINVAL;
     855             :                         }
     856             : 
     857           3 :                         if (optlen != client->duid_len ||
     858           3 :                             memcmp(&client->duid, optval, optlen) != 0) {
     859           0 :                                 log_dhcp6_client(client, "%s DUID does not match",
     860             :                                                  dhcp6_message_type_to_string(message->type));
     861             : 
     862           0 :                                 return -EINVAL;
     863             :                         }
     864           3 :                         clientid = true;
     865             : 
     866           3 :                         break;
     867             : 
     868           3 :                 case SD_DHCP6_OPTION_SERVERID:
     869           3 :                         r = dhcp6_lease_get_serverid(lease, NULL, NULL);
     870           3 :                         if (r >= 0) {
     871           0 :                                 log_dhcp6_client(client, "%s contains multiple serverids",
     872             :                                                  dhcp6_message_type_to_string(message->type));
     873           0 :                                 return -EINVAL;
     874             :                         }
     875             : 
     876           3 :                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
     877           3 :                         if (r < 0)
     878           0 :                                 return r;
     879             : 
     880           3 :                         break;
     881             : 
     882           1 :                 case SD_DHCP6_OPTION_PREFERENCE:
     883           1 :                         if (optlen != 1)
     884           0 :                                 return -EINVAL;
     885             : 
     886           1 :                         r = dhcp6_lease_set_preference(lease, optval[0]);
     887           1 :                         if (r < 0)
     888           0 :                                 return r;
     889             : 
     890           1 :                         break;
     891             : 
     892           0 :                 case SD_DHCP6_OPTION_STATUS_CODE:
     893           0 :                         status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option));
     894           0 :                         if (status < 0)
     895           0 :                                 return status;
     896             : 
     897           0 :                         if (status > 0) {
     898           0 :                                 log_dhcp6_client(client, "%s Status %s",
     899             :                                                  dhcp6_message_type_to_string(message->type),
     900             :                                                  dhcp6_message_status_to_string(status));
     901             : 
     902           0 :                                 return -EINVAL;
     903             :                         }
     904             : 
     905           0 :                         break;
     906             : 
     907           3 :                 case SD_DHCP6_OPTION_IA_NA:
     908           3 :                         if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
     909           1 :                                 log_dhcp6_client(client, "Information request ignoring IA NA option");
     910             : 
     911           1 :                                 break;
     912             :                         }
     913             : 
     914           2 :                         r = dhcp6_option_parse_ia(option, &lease->ia);
     915           2 :                         if (r < 0 && r != -ENOMSG)
     916           0 :                                 return r;
     917             : 
     918           2 :                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
     919           2 :                         if (r < 0)
     920           0 :                                 return r;
     921             : 
     922           2 :                         if (client->ia_na.ia_na.id != iaid_lease) {
     923           0 :                                 log_dhcp6_client(client, "%s has wrong IAID for IA NA",
     924             :                                                  dhcp6_message_type_to_string(message->type));
     925           0 :                                 return -EINVAL;
     926             :                         }
     927             : 
     928           2 :                         if (lease->ia.addresses) {
     929           2 :                                 lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
     930           2 :                                 lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
     931             :                         }
     932             : 
     933           2 :                         break;
     934             : 
     935           0 :                 case SD_DHCP6_OPTION_IA_PD:
     936           0 :                         if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
     937           0 :                                 log_dhcp6_client(client, "Information request ignoring IA PD option");
     938             : 
     939           0 :                                 break;
     940             :                         }
     941             : 
     942           0 :                         r = dhcp6_option_parse_ia(option, &lease->pd);
     943           0 :                         if (r < 0 && r != -ENOMSG)
     944           0 :                                 return r;
     945             : 
     946           0 :                         r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
     947           0 :                         if (r < 0)
     948           0 :                                 return r;
     949             : 
     950           0 :                         if (client->ia_pd.ia_pd.id != iaid_lease) {
     951           0 :                                 log_dhcp6_client(client, "%s has wrong IAID for IA PD",
     952             :                                                  dhcp6_message_type_to_string(message->type));
     953           0 :                                 return -EINVAL;
     954             :                         }
     955             : 
     956           0 :                         if (lease->pd.addresses) {
     957           0 :                                 lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
     958           0 :                                 lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
     959             :                         }
     960             : 
     961           0 :                         break;
     962             : 
     963           0 :                 case SD_DHCP6_OPTION_RAPID_COMMIT:
     964           0 :                         r = dhcp6_lease_set_rapid_commit(lease);
     965           0 :                         if (r < 0)
     966           0 :                                 return r;
     967             : 
     968           0 :                         break;
     969             : 
     970           3 :                 case SD_DHCP6_OPTION_DNS_SERVERS:
     971           3 :                         r = dhcp6_lease_set_dns(lease, optval, optlen);
     972           3 :                         if (r < 0)
     973           0 :                                 return r;
     974             : 
     975           3 :                         break;
     976             : 
     977           3 :                 case SD_DHCP6_OPTION_DOMAIN_LIST:
     978           3 :                         r = dhcp6_lease_set_domains(lease, optval, optlen);
     979           3 :                         if (r < 0)
     980           0 :                                 return r;
     981             : 
     982           3 :                         break;
     983             : 
     984           0 :                 case SD_DHCP6_OPTION_NTP_SERVER:
     985           0 :                         r = dhcp6_lease_set_ntp(lease, optval, optlen);
     986           0 :                         if (r < 0)
     987           0 :                                 return r;
     988             : 
     989           0 :                         break;
     990             : 
     991           3 :                 case SD_DHCP6_OPTION_SNTP_SERVERS:
     992           3 :                         r = dhcp6_lease_set_sntp(lease, optval, optlen);
     993           3 :                         if (r < 0)
     994           0 :                                 return r;
     995             : 
     996           3 :                         break;
     997             :                 }
     998             : 
     999          19 :                 pos += offsetof(DHCP6Option, data) + optlen;
    1000             :         }
    1001             : 
    1002           3 :         if (!clientid) {
    1003           0 :                 log_dhcp6_client(client, "%s has incomplete options",
    1004             :                                  dhcp6_message_type_to_string(message->type));
    1005           0 :                 return -EINVAL;
    1006             :         }
    1007             : 
    1008           3 :         if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
    1009           2 :                 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
    1010           2 :                 if (r < 0) {
    1011           0 :                         log_dhcp6_client(client, "%s has no server id",
    1012             :                                          dhcp6_message_type_to_string(message->type));
    1013           0 :                         return -EINVAL;
    1014             :                 }
    1015             : 
    1016             :         } else {
    1017           1 :                 if (lease->ia.addresses) {
    1018           0 :                         lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
    1019           0 :                         lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
    1020             :                 }
    1021             : 
    1022           1 :                 if (lease->pd.addresses) {
    1023           0 :                         lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
    1024           0 :                         lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
    1025             :                 }
    1026             :         }
    1027             : 
    1028           3 :         return 0;
    1029             : }
    1030             : 
    1031           3 : static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
    1032           3 :         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
    1033             :         bool rapid_commit;
    1034             :         int r;
    1035             : 
    1036           3 :         assert(client);
    1037           3 :         assert(reply);
    1038             : 
    1039           3 :         if (reply->type != DHCP6_REPLY)
    1040           1 :                 return 0;
    1041             : 
    1042           2 :         r = dhcp6_lease_new(&lease);
    1043           2 :         if (r < 0)
    1044           0 :                 return -ENOMEM;
    1045             : 
    1046           2 :         r = client_parse_message(client, reply, len, lease);
    1047           2 :         if (r < 0)
    1048           0 :                 return r;
    1049             : 
    1050           2 :         if (client->state == DHCP6_STATE_SOLICITATION) {
    1051           0 :                 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
    1052           0 :                 if (r < 0)
    1053           0 :                         return r;
    1054             : 
    1055           0 :                 if (!rapid_commit)
    1056           0 :                         return 0;
    1057             :         }
    1058             : 
    1059           2 :         sd_dhcp6_lease_unref(client->lease);
    1060           2 :         client->lease = TAKE_PTR(lease);
    1061             : 
    1062           2 :         return DHCP6_STATE_BOUND;
    1063             : }
    1064             : 
    1065           1 : static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
    1066           1 :         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
    1067           1 :         uint8_t pref_advertise = 0, pref_lease = 0;
    1068             :         int r;
    1069             : 
    1070           1 :         if (advertise->type != DHCP6_ADVERTISE)
    1071           0 :                 return 0;
    1072             : 
    1073           1 :         r = dhcp6_lease_new(&lease);
    1074           1 :         if (r < 0)
    1075           0 :                 return r;
    1076             : 
    1077           1 :         r = client_parse_message(client, advertise, len, lease);
    1078           1 :         if (r < 0)
    1079           0 :                 return r;
    1080             : 
    1081           1 :         r = dhcp6_lease_get_preference(lease, &pref_advertise);
    1082           1 :         if (r < 0)
    1083           0 :                 return r;
    1084             : 
    1085           1 :         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
    1086             : 
    1087           1 :         if (r < 0 || pref_advertise > pref_lease) {
    1088           1 :                 sd_dhcp6_lease_unref(client->lease);
    1089           1 :                 client->lease = TAKE_PTR(lease);
    1090           1 :                 r = 0;
    1091             :         }
    1092             : 
    1093           1 :         if (pref_advertise == 255 || client->retransmit_count > 1)
    1094           0 :                 r = DHCP6_STATE_REQUEST;
    1095             : 
    1096           1 :         return r;
    1097             : }
    1098             : 
    1099           3 : static int client_receive_message(
    1100             :                 sd_event_source *s,
    1101             :                 int fd, uint32_t
    1102             :                 revents,
    1103             :                 void *userdata) {
    1104             : 
    1105           3 :         sd_dhcp6_client *client = userdata;
    1106           6 :         DHCP6_CLIENT_DONT_DESTROY(client);
    1107           3 :         _cleanup_free_ DHCP6Message *message = NULL;
    1108             :         ssize_t buflen, len;
    1109           3 :         int r = 0;
    1110             : 
    1111           3 :         assert(s);
    1112           3 :         assert(client);
    1113           3 :         assert(client->event);
    1114             : 
    1115           3 :         buflen = next_datagram_size_fd(fd);
    1116           3 :         if (buflen == -ENETDOWN) {
    1117             :                 /* the link is down. Don't return an error or the I/O event
    1118             :                    source will be disconnected and we won't be able to receive
    1119             :                    packets again when the link comes back. */
    1120           0 :                 return 0;
    1121             :         }
    1122           3 :         if (buflen < 0)
    1123           0 :                 return buflen;
    1124             : 
    1125           3 :         message = malloc(buflen);
    1126           3 :         if (!message)
    1127           0 :                 return -ENOMEM;
    1128             : 
    1129           3 :         len = recv(fd, message, buflen, 0);
    1130           3 :         if (len < 0) {
    1131             :                 /* see comment above for why we shouldn't error out on ENETDOWN. */
    1132           0 :                 if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
    1133           0 :                         return 0;
    1134             : 
    1135           0 :                 return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
    1136             : 
    1137             :         }
    1138           3 :         if ((size_t) len < sizeof(DHCP6Message)) {
    1139           0 :                 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
    1140           0 :                 return 0;
    1141             :         }
    1142             : 
    1143           3 :         switch(message->type) {
    1144           0 :         case DHCP6_SOLICIT:
    1145             :         case DHCP6_REQUEST:
    1146             :         case DHCP6_CONFIRM:
    1147             :         case DHCP6_RENEW:
    1148             :         case DHCP6_REBIND:
    1149             :         case DHCP6_RELEASE:
    1150             :         case DHCP6_DECLINE:
    1151             :         case DHCP6_INFORMATION_REQUEST:
    1152             :         case DHCP6_RELAY_FORW:
    1153             :         case DHCP6_RELAY_REPL:
    1154           0 :                 return 0;
    1155             : 
    1156           3 :         case DHCP6_ADVERTISE:
    1157             :         case DHCP6_REPLY:
    1158             :         case DHCP6_RECONFIGURE:
    1159           3 :                 break;
    1160             : 
    1161           0 :         default:
    1162           0 :                 log_dhcp6_client(client, "Unknown message type %d", message->type);
    1163           0 :                 return 0;
    1164             :         }
    1165             : 
    1166           3 :         if (client->transaction_id != (message->transaction_id &
    1167           3 :                                        htobe32(0x00ffffff)))
    1168           0 :                 return 0;
    1169             : 
    1170           3 :         switch (client->state) {
    1171           1 :         case DHCP6_STATE_INFORMATION_REQUEST:
    1172           1 :                 r = client_receive_reply(client, message, len);
    1173           1 :                 if (r < 0)
    1174           0 :                         return 0;
    1175             : 
    1176           1 :                 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
    1177             : 
    1178           1 :                 client_start(client, DHCP6_STATE_STOPPED);
    1179             : 
    1180           1 :                 break;
    1181             : 
    1182           1 :         case DHCP6_STATE_SOLICITATION:
    1183           1 :                 r = client_receive_advertise(client, message, len);
    1184             : 
    1185           1 :                 if (r == DHCP6_STATE_REQUEST) {
    1186           0 :                         client_start(client, r);
    1187             : 
    1188           0 :                         break;
    1189             :                 }
    1190             : 
    1191             :                 _fallthrough_; /* for Soliciation Rapid Commit option check */
    1192             :         case DHCP6_STATE_REQUEST:
    1193             :         case DHCP6_STATE_RENEW:
    1194             :         case DHCP6_STATE_REBIND:
    1195             : 
    1196           2 :                 r = client_receive_reply(client, message, len);
    1197           2 :                 if (r < 0)
    1198           0 :                         return 0;
    1199             : 
    1200           2 :                 if (r == DHCP6_STATE_BOUND) {
    1201             : 
    1202           1 :                         r = client_start(client, DHCP6_STATE_BOUND);
    1203           1 :                         if (r < 0) {
    1204           0 :                                 client_stop(client, r);
    1205           0 :                                 return 0;
    1206             :                         }
    1207             : 
    1208           1 :                         client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
    1209             :                 }
    1210             : 
    1211           2 :                 break;
    1212             : 
    1213           0 :         case DHCP6_STATE_BOUND:
    1214             : 
    1215           0 :                 break;
    1216             : 
    1217           0 :         case DHCP6_STATE_STOPPED:
    1218           0 :                 return 0;
    1219             :         }
    1220             : 
    1221           3 :         log_dhcp6_client(client, "Recv %s",
    1222             :                          dhcp6_message_type_to_string(message->type));
    1223             : 
    1224           3 :         return 0;
    1225             : }
    1226             : 
    1227           1 : static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
    1228             :                                uint32_t *lifetime_t2) {
    1229           1 :         assert_return(client, -EINVAL);
    1230           1 :         assert_return(client->lease, -EINVAL);
    1231             : 
    1232           1 :         if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
    1233           1 :                 *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
    1234           1 :                 *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
    1235             : 
    1236           1 :                 return 0;
    1237             :         }
    1238             : 
    1239           0 :         if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
    1240           0 :                 *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
    1241           0 :                 *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
    1242             : 
    1243           0 :                 return 0;
    1244             :         }
    1245             : 
    1246           0 :         return -ENOMSG;
    1247             : }
    1248             : 
    1249           5 : static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
    1250             :         int r;
    1251             :         usec_t timeout, time_now;
    1252             :         char time_string[FORMAT_TIMESPAN_MAX];
    1253             :         uint32_t lifetime_t1, lifetime_t2;
    1254             : 
    1255           5 :         assert_return(client, -EINVAL);
    1256           5 :         assert_return(client->event, -EINVAL);
    1257           5 :         assert_return(client->ifindex > 0, -EINVAL);
    1258           5 :         assert_return(client->state != state, -EINVAL);
    1259             : 
    1260           5 :         (void) event_source_disable(client->timeout_resend_expire);
    1261           5 :         (void) event_source_disable(client->timeout_resend);
    1262           5 :         client->retransmit_time = 0;
    1263           5 :         client->retransmit_count = 0;
    1264             : 
    1265           5 :         r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
    1266           5 :         if (r < 0)
    1267           0 :                 return r;
    1268             : 
    1269           5 :         if (!client->receive_message) {
    1270           2 :                 r = sd_event_add_io(client->event, &client->receive_message,
    1271             :                                     client->fd, EPOLLIN, client_receive_message,
    1272             :                                     client);
    1273           2 :                 if (r < 0)
    1274           0 :                         goto error;
    1275             : 
    1276           2 :                 r = sd_event_source_set_priority(client->receive_message,
    1277           2 :                                                  client->event_priority);
    1278           2 :                 if (r < 0)
    1279           0 :                         goto error;
    1280             : 
    1281           2 :                 r = sd_event_source_set_description(client->receive_message,
    1282             :                                                     "dhcp6-receive-message");
    1283           2 :                 if (r < 0)
    1284           0 :                         goto error;
    1285             :         }
    1286             : 
    1287           5 :         switch (state) {
    1288           1 :         case DHCP6_STATE_STOPPED:
    1289           1 :                 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
    1290           0 :                         client->state = DHCP6_STATE_STOPPED;
    1291             : 
    1292           0 :                         return 0;
    1293             :                 }
    1294             : 
    1295             :                 _fallthrough_;
    1296             :         case DHCP6_STATE_SOLICITATION:
    1297           2 :                 client->state = DHCP6_STATE_SOLICITATION;
    1298             : 
    1299           2 :                 break;
    1300             : 
    1301           2 :         case DHCP6_STATE_INFORMATION_REQUEST:
    1302             :         case DHCP6_STATE_REQUEST:
    1303             :         case DHCP6_STATE_RENEW:
    1304             :         case DHCP6_STATE_REBIND:
    1305             : 
    1306           2 :                 client->state = state;
    1307             : 
    1308           2 :                 break;
    1309             : 
    1310           1 :         case DHCP6_STATE_BOUND:
    1311             : 
    1312           1 :                 r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
    1313           1 :                 if (r < 0)
    1314           0 :                         goto error;
    1315             : 
    1316           1 :                 if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
    1317           0 :                         log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
    1318             :                                          lifetime_t1, lifetime_t2);
    1319             : 
    1320           0 :                         return 0;
    1321             :                 }
    1322             : 
    1323           1 :                 timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
    1324             : 
    1325           1 :                 log_dhcp6_client(client, "T1 expires in %s",
    1326             :                                  format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
    1327             : 
    1328           1 :                 r = event_reset_time(client->event, &client->timeout_t1,
    1329             :                                      clock_boottime_or_monotonic(),
    1330             :                                      time_now + timeout, 10 * USEC_PER_SEC,
    1331             :                                      client_timeout_t1, client,
    1332           1 :                                      client->event_priority, "dhcp6-t1-timeout", true);
    1333           1 :                 if (r < 0)
    1334           0 :                         goto error;
    1335             : 
    1336           1 :                 timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
    1337             : 
    1338           1 :                 log_dhcp6_client(client, "T2 expires in %s",
    1339             :                                  format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
    1340             : 
    1341           1 :                 r = event_reset_time(client->event, &client->timeout_t2,
    1342             :                                      clock_boottime_or_monotonic(),
    1343             :                                      time_now + timeout, 10 * USEC_PER_SEC,
    1344             :                                      client_timeout_t2, client,
    1345           1 :                                      client->event_priority, "dhcp6-t2-timeout", true);
    1346           1 :                 if (r < 0)
    1347           0 :                         goto error;
    1348             : 
    1349           1 :                 client->state = state;
    1350             : 
    1351           1 :                 return 0;
    1352             :         }
    1353             : 
    1354           4 :         client->transaction_id = random_u32() & htobe32(0x00ffffff);
    1355           4 :         client->transaction_start = time_now;
    1356             : 
    1357           4 :         r = event_reset_time(client->event, &client->timeout_resend,
    1358             :                              clock_boottime_or_monotonic(),
    1359             :                              0, 0,
    1360             :                              client_timeout_resend, client,
    1361           4 :                              client->event_priority, "dhcp6-resend-timeout", true);
    1362           4 :         if (r < 0)
    1363           0 :                 goto error;
    1364             : 
    1365           4 :         return 0;
    1366             : 
    1367           0 :  error:
    1368           0 :         client_reset(client);
    1369           0 :         return r;
    1370             : }
    1371             : 
    1372           1 : int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
    1373           1 :         assert_return(client, -EINVAL);
    1374             : 
    1375           1 :         client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
    1376             : 
    1377           1 :         client->fd = safe_close(client->fd);
    1378             : 
    1379           1 :         return 0;
    1380             : }
    1381             : 
    1382           0 : int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
    1383           0 :         assert_return(client, -EINVAL);
    1384             : 
    1385           0 :         return client->state != DHCP6_STATE_STOPPED;
    1386             : }
    1387             : 
    1388           2 : int sd_dhcp6_client_start(sd_dhcp6_client *client) {
    1389           2 :         enum DHCP6State state = DHCP6_STATE_SOLICITATION;
    1390           2 :         int r = 0;
    1391             : 
    1392           2 :         assert_return(client, -EINVAL);
    1393           2 :         assert_return(client->event, -EINVAL);
    1394           2 :         assert_return(client->ifindex > 0, -EINVAL);
    1395           2 :         assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
    1396             : 
    1397           2 :         if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
    1398           0 :                 return -EBUSY;
    1399             : 
    1400           2 :         if (!client->information_request && !client->request)
    1401           0 :                 return -EINVAL;
    1402             : 
    1403           2 :         r = client_reset(client);
    1404           2 :         if (r < 0)
    1405           0 :                 return r;
    1406             : 
    1407           2 :         r = client_ensure_iaid(client);
    1408           2 :         if (r < 0)
    1409           0 :                 return r;
    1410             : 
    1411           2 :         r = client_ensure_duid(client);
    1412           2 :         if (r < 0)
    1413           0 :                 return r;
    1414             : 
    1415           2 :         if (client->fd < 0) {
    1416           2 :                 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
    1417           2 :                 if (r < 0) {
    1418           0 :                         _cleanup_free_ char *p = NULL;
    1419             : 
    1420           0 :                         (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
    1421           0 :                         return log_dhcp6_client_errno(client, r,
    1422             :                                                       "Failed to bind to UDP socket at address %s: %m", strna(p));
    1423             :                 }
    1424             : 
    1425           2 :                 client->fd = r;
    1426             :         }
    1427             : 
    1428           2 :         if (client->information_request)
    1429           1 :                 state = DHCP6_STATE_INFORMATION_REQUEST;
    1430             : 
    1431           2 :         log_dhcp6_client(client, "Started in %s mode",
    1432             :                          client->information_request? "Information request":
    1433             :                          "Managed");
    1434             : 
    1435           2 :         return client_start(client, state);
    1436             : }
    1437             : 
    1438           2 : int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
    1439             :         int r;
    1440             : 
    1441           2 :         assert_return(client, -EINVAL);
    1442           2 :         assert_return(!client->event, -EBUSY);
    1443             : 
    1444           2 :         if (event)
    1445           2 :                 client->event = sd_event_ref(event);
    1446             :         else {
    1447           0 :                 r = sd_event_default(&client->event);
    1448           0 :                 if (r < 0)
    1449           0 :                         return 0;
    1450             :         }
    1451             : 
    1452           2 :         client->event_priority = priority;
    1453             : 
    1454           2 :         return 0;
    1455             : }
    1456             : 
    1457           3 : int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
    1458           3 :         assert_return(client, -EINVAL);
    1459             : 
    1460           3 :         client->event = sd_event_unref(client->event);
    1461             : 
    1462           3 :         return 0;
    1463             : }
    1464             : 
    1465           0 : sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
    1466           0 :         assert_return(client, NULL);
    1467             : 
    1468           0 :         return client->event;
    1469             : }
    1470             : 
    1471           2 : static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
    1472           2 :         assert(client);
    1473             : 
    1474           2 :         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
    1475           2 :         client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire);
    1476           2 :         client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
    1477           2 :         client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
    1478             : 
    1479           2 :         client_reset(client);
    1480             : 
    1481           2 :         client->fd = safe_close(client->fd);
    1482             : 
    1483           2 :         sd_dhcp6_client_detach_event(client);
    1484             : 
    1485           2 :         free(client->req_opts);
    1486           2 :         free(client->fqdn);
    1487           2 :         return mfree(client);
    1488             : }
    1489             : 
    1490          16 : DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
    1491             : 
    1492           2 : int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
    1493           2 :         _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
    1494           2 :         _cleanup_free_ be16_t *req_opts = NULL;
    1495             :         size_t t;
    1496             : 
    1497           2 :         assert_return(ret, -EINVAL);
    1498             : 
    1499           2 :         req_opts = new(be16_t, ELEMENTSOF(default_req_opts));
    1500           2 :         if (!req_opts)
    1501           0 :                 return -ENOMEM;
    1502             : 
    1503          10 :         for (t = 0; t < ELEMENTSOF(default_req_opts); t++)
    1504           8 :                 req_opts[t] = htobe16(default_req_opts[t]);
    1505             : 
    1506           2 :         client = new(sd_dhcp6_client, 1);
    1507           2 :         if (!client)
    1508           0 :                 return -ENOMEM;
    1509             : 
    1510           4 :         *client = (sd_dhcp6_client) {
    1511             :                 .n_ref = 1,
    1512             :                 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
    1513             :                 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
    1514             :                 .ifindex = -1,
    1515             :                 .request = DHCP6_REQUEST_IA_NA,
    1516             :                 .fd = -1,
    1517             :                 .req_opts_len = ELEMENTSOF(default_req_opts),
    1518           2 :                 .req_opts = TAKE_PTR(req_opts),
    1519             :         };
    1520             : 
    1521           2 :         *ret = TAKE_PTR(client);
    1522             : 
    1523           2 :         return 0;
    1524             : }

Generated by: LCOV version 1.14