LCOV - code coverage report
Current view: top level - libsystemd-network - sd-dhcp6-client.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 402 617 65.2 %
Date: 2015-07-29 18:47:03 Functions: 29 37 78.4 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright (C) 2014 Intel Corporation. All rights reserved.
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <errno.h>
      23             : #include <string.h>
      24             : #include <sys/ioctl.h>
      25             : #include <linux/if_infiniband.h>
      26             : 
      27             : #include "udev.h"
      28             : #include "udev-util.h"
      29             : #include "util.h"
      30             : #include "refcnt.h"
      31             : #include "random-util.h"
      32             : 
      33             : #include "network-internal.h"
      34             : #include "sd-dhcp6-client.h"
      35             : #include "dhcp6-protocol.h"
      36             : #include "dhcp6-internal.h"
      37             : #include "dhcp6-lease-internal.h"
      38             : #include "dhcp-identifier.h"
      39             : 
      40             : #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
      41             : 
      42             : struct sd_dhcp6_client {
      43             :         RefCount n_ref;
      44             : 
      45             :         enum DHCP6State state;
      46             :         sd_event *event;
      47             :         int event_priority;
      48             :         int index;
      49             :         uint8_t mac_addr[MAX_MAC_ADDR_LEN];
      50             :         size_t mac_addr_len;
      51             :         uint16_t arp_type;
      52             :         DHCP6IA ia_na;
      53             :         be32_t transaction_id;
      54             :         usec_t transaction_start;
      55             :         struct sd_dhcp6_lease *lease;
      56             :         int fd;
      57             :         bool information_request;
      58             :         be16_t *req_opts;
      59             :         size_t req_opts_allocated;
      60             :         size_t req_opts_len;
      61             :         sd_event_source *receive_message;
      62             :         usec_t retransmit_time;
      63             :         uint8_t retransmit_count;
      64             :         sd_event_source *timeout_resend;
      65             :         sd_event_source *timeout_resend_expire;
      66             :         sd_dhcp6_client_cb_t cb;
      67             :         void *userdata;
      68             :         struct duid duid;
      69             :         size_t duid_len;
      70             : };
      71             : 
      72             : static const uint16_t default_req_opts[] = {
      73             :         DHCP6_OPTION_DNS_SERVERS,
      74             :         DHCP6_OPTION_DOMAIN_LIST,
      75             :         DHCP6_OPTION_NTP_SERVER,
      76             : };
      77             : 
      78             : const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
      79             :         [DHCP6_SOLICIT] = "SOLICIT",
      80             :         [DHCP6_ADVERTISE] = "ADVERTISE",
      81             :         [DHCP6_REQUEST] = "REQUEST",
      82             :         [DHCP6_CONFIRM] = "CONFIRM",
      83             :         [DHCP6_RENEW] = "RENEW",
      84             :         [DHCP6_REBIND] = "REBIND",
      85             :         [DHCP6_REPLY] = "REPLY",
      86             :         [DHCP6_RELEASE] = "RELEASE",
      87             :         [DHCP6_DECLINE] = "DECLINE",
      88             :         [DHCP6_RECONFIGURE] = "RECONFIGURE",
      89             :         [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
      90             :         [DHCP6_RELAY_FORW] = "RELAY-FORW",
      91             :         [DHCP6_RELAY_REPL] = "RELAY-REPL",
      92             : };
      93             : 
      94          38 : DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
      95             : 
      96             : const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
      97             :         [DHCP6_STATUS_SUCCESS] = "Success",
      98             :         [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
      99             :         [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
     100             :         [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
     101             :         [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
     102             :         [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
     103             : };
     104             : 
     105          16 : DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
     106             : 
     107           5 : DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
     108             : #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
     109             : 
     110             : #define DHCP6_CLIENT_DONT_DESTROY(client) \
     111             :         _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
     112             : 
     113             : static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
     114             : 
     115           3 : int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
     116             :                                  sd_dhcp6_client_cb_t cb, void *userdata)
     117             : {
     118           3 :         assert_return(client, -EINVAL);
     119             : 
     120           3 :         client->cb = cb;
     121           3 :         client->userdata = userdata;
     122             : 
     123           3 :         return 0;
     124             : }
     125             : 
     126           5 : int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
     127             : {
     128           5 :         assert_return(client, -EINVAL);
     129           5 :         assert_return(interface_index >= -1, -EINVAL);
     130             : 
     131           4 :         client->index = interface_index;
     132             : 
     133           4 :         return 0;
     134             : }
     135             : 
     136           2 : int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
     137             :                             size_t addr_len, uint16_t arp_type)
     138             : {
     139           2 :         assert_return(client, -EINVAL);
     140           2 :         assert_return(addr, -EINVAL);
     141           2 :         assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
     142           2 :         assert_return(arp_type > 0, -EINVAL);
     143             : 
     144           2 :         if (arp_type == ARPHRD_ETHER)
     145           2 :                 assert_return(addr_len == ETH_ALEN, -EINVAL);
     146           0 :         else if (arp_type == ARPHRD_INFINIBAND)
     147           0 :                 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
     148             :         else
     149           0 :                 return -EINVAL;
     150             : 
     151           2 :         if (client->mac_addr_len == addr_len &&
     152           0 :             memcmp(&client->mac_addr, addr, addr_len) == 0)
     153           0 :                 return 0;
     154             : 
     155           2 :         memcpy(&client->mac_addr, addr, addr_len);
     156           2 :         client->mac_addr_len = addr_len;
     157           2 :         client->arp_type = arp_type;
     158             : 
     159           2 :         return 0;
     160             : }
     161             : 
     162           2 : static int client_ensure_duid(sd_dhcp6_client *client)
     163             : {
     164           2 :         if (client->duid_len != 0)
     165           1 :                 return 0;
     166           1 :         return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
     167             : }
     168             : 
     169           0 : int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
     170             :                              size_t duid_len)
     171             : {
     172           0 :         assert_return(client, -EINVAL);
     173           0 :         assert_return(duid, -EINVAL);
     174           0 :         assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
     175             : 
     176           0 :         switch (type) {
     177             :         case DHCP6_DUID_LLT:
     178           0 :                 if (duid_len <= sizeof(client->duid.llt))
     179           0 :                         return -EINVAL;
     180           0 :                 break;
     181             :         case DHCP6_DUID_EN:
     182           0 :                 if (duid_len != sizeof(client->duid.en))
     183           0 :                         return -EINVAL;
     184           0 :                 break;
     185             :         case DHCP6_DUID_LL:
     186           0 :                 if (duid_len <= sizeof(client->duid.ll))
     187           0 :                         return -EINVAL;
     188           0 :                 break;
     189             :         case DHCP6_DUID_UUID:
     190           0 :                 if (duid_len != sizeof(client->duid.uuid))
     191           0 :                         return -EINVAL;
     192           0 :                 break;
     193             :         default:
     194             :                 /* accept unknown type in order to be forward compatible */
     195           0 :                 break;
     196             :         }
     197             : 
     198           0 :         client->duid.type = htobe16(type);
     199           0 :         memcpy(&client->duid.raw.data, duid, duid_len);
     200           0 :         client->duid_len = duid_len + sizeof(client->duid.type);
     201             : 
     202           0 :         return 0;
     203             : }
     204             : 
     205           2 : int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
     206             :                                             bool enabled) {
     207           2 :         assert_return(client, -EINVAL);
     208             : 
     209           2 :         client->information_request = enabled;
     210             : 
     211           2 :         return 0;
     212             : }
     213             : 
     214           2 : int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
     215             :                                             bool *enabled) {
     216           2 :         assert_return(client, -EINVAL);
     217           2 :         assert_return(enabled, -EINVAL);
     218             : 
     219           2 :         *enabled = client->information_request;
     220             : 
     221           2 :         return 0;
     222             : }
     223             : 
     224           7 : int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
     225             :                                        uint16_t option) {
     226             :         size_t t;
     227             : 
     228           7 :         assert_return(client, -EINVAL);
     229           7 :         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
     230             : 
     231           6 :         switch(option) {
     232             :         case DHCP6_OPTION_DNS_SERVERS:
     233             :         case DHCP6_OPTION_DOMAIN_LIST:
     234             :         case DHCP6_OPTION_SNTP_SERVERS:
     235             :         case DHCP6_OPTION_NTP_SERVER:
     236           4 :                 break;
     237             : 
     238             :         default:
     239           2 :                 return -EINVAL;
     240             :         }
     241             : 
     242          10 :         for (t = 0; t < client->req_opts_len; t++)
     243           9 :                 if (client->req_opts[t] == htobe16(option))
     244           3 :                         return -EEXIST;
     245             : 
     246           1 :         if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
     247             :                             client->req_opts_len + 1))
     248           0 :                 return -ENOMEM;
     249             : 
     250           1 :         client->req_opts[client->req_opts_len++] = htobe16(option);
     251             : 
     252           1 :         return 0;
     253             : }
     254             : 
     255           0 : int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
     256           0 :         assert_return(client, -EINVAL);
     257           0 :         assert_return(ret, -EINVAL);
     258             : 
     259           0 :         if (!client->lease)
     260           0 :                 return -ENOMSG;
     261             : 
     262           0 :         *ret = sd_dhcp6_lease_ref(client->lease);
     263             : 
     264           0 :         return 0;
     265             : }
     266             : 
     267           2 : static void client_notify(sd_dhcp6_client *client, int event) {
     268           2 :         if (client->cb)
     269           2 :                 client->cb(client, event, client->userdata);
     270           2 : }
     271             : 
     272           4 : static int client_reset(sd_dhcp6_client *client) {
     273           4 :         assert_return(client, -EINVAL);
     274             : 
     275           4 :         client->receive_message =
     276           4 :                 sd_event_source_unref(client->receive_message);
     277             : 
     278           4 :         client->fd = safe_close(client->fd);
     279             : 
     280           4 :         client->transaction_id = 0;
     281           4 :         client->transaction_start = 0;
     282             : 
     283           4 :         client->ia_na.timeout_t1 =
     284           4 :                 sd_event_source_unref(client->ia_na.timeout_t1);
     285           4 :         client->ia_na.timeout_t2 =
     286           4 :                 sd_event_source_unref(client->ia_na.timeout_t2);
     287             : 
     288           4 :         client->retransmit_time = 0;
     289           4 :         client->retransmit_count = 0;
     290           4 :         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
     291           4 :         client->timeout_resend_expire =
     292           4 :                 sd_event_source_unref(client->timeout_resend_expire);
     293             : 
     294           4 :         client->state = DHCP6_STATE_STOPPED;
     295             : 
     296           4 :         return 0;
     297             : }
     298             : 
     299           0 : static void client_stop(sd_dhcp6_client *client, int error) {
     300           0 :         DHCP6_CLIENT_DONT_DESTROY(client);
     301             : 
     302           0 :         assert(client);
     303             : 
     304           0 :         client_notify(client, error);
     305             : 
     306           0 :         client_reset(client);
     307           0 : }
     308             : 
     309           3 : static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
     310           6 :         _cleanup_free_ DHCP6Message *message = NULL;
     311           3 :         struct in6_addr all_servers =
     312             :                 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
     313           3 :         size_t len, optlen = 512;
     314             :         uint8_t *opt;
     315             :         int r;
     316             :         usec_t elapsed_usec;
     317             :         be16_t elapsed_time;
     318             : 
     319           3 :         len = sizeof(DHCP6Message) + optlen;
     320             : 
     321           3 :         message = malloc0(len);
     322           3 :         if (!message)
     323           0 :                 return -ENOMEM;
     324             : 
     325           3 :         opt = (uint8_t *)(message + 1);
     326             : 
     327           3 :         message->transaction_id = client->transaction_id;
     328             : 
     329           3 :         switch(client->state) {
     330             :         case DHCP6_STATE_INFORMATION_REQUEST:
     331           1 :                 message->type = DHCP6_INFORMATION_REQUEST;
     332             : 
     333           1 :                 break;
     334             : 
     335             :         case DHCP6_STATE_SOLICITATION:
     336           1 :                 message->type = DHCP6_SOLICIT;
     337             : 
     338           1 :                 r = dhcp6_option_append(&opt, &optlen,
     339             :                                         DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
     340           1 :                 if (r < 0)
     341           0 :                         return r;
     342             : 
     343           1 :                 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
     344           1 :                 if (r < 0)
     345           0 :                         return r;
     346             : 
     347           1 :                 break;
     348             : 
     349             :         case DHCP6_STATE_REQUEST:
     350             :         case DHCP6_STATE_RENEW:
     351             : 
     352           1 :                 if (client->state == DHCP6_STATE_REQUEST)
     353           1 :                         message->type = DHCP6_REQUEST;
     354             :                 else
     355           0 :                         message->type = DHCP6_RENEW;
     356             : 
     357           1 :                 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
     358           1 :                                         client->lease->serverid_len,
     359           1 :                                         client->lease->serverid);
     360           1 :                 if (r < 0)
     361           0 :                         return r;
     362             : 
     363           1 :                 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
     364           1 :                 if (r < 0)
     365           0 :                         return r;
     366             : 
     367           1 :                 break;
     368             : 
     369             :         case DHCP6_STATE_REBIND:
     370           0 :                 message->type = DHCP6_REBIND;
     371             : 
     372           0 :                 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
     373           0 :                 if (r < 0)
     374           0 :                         return r;
     375             : 
     376           0 :                 break;
     377             : 
     378             :         case DHCP6_STATE_STOPPED:
     379             :         case DHCP6_STATE_BOUND:
     380           0 :                 return -EINVAL;
     381             :         }
     382             : 
     383           3 :         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
     384           3 :                                 client->req_opts_len * sizeof(be16_t),
     385           3 :                                 client->req_opts);
     386           3 :         if (r < 0)
     387           0 :                 return r;
     388             : 
     389           3 :         assert (client->duid_len);
     390           3 :         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
     391           3 :                                 client->duid_len, &client->duid);
     392           3 :         if (r < 0)
     393           0 :                 return r;
     394             : 
     395           3 :         elapsed_usec = time_now - client->transaction_start;
     396           3 :         if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
     397           3 :                 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
     398             :         else
     399           0 :                 elapsed_time = 0xffff;
     400             : 
     401           3 :         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
     402             :                                 sizeof(elapsed_time), &elapsed_time);
     403           3 :         if (r < 0)
     404           0 :                 return r;
     405             : 
     406           3 :         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
     407             :                                           len - optlen);
     408           3 :         if (r < 0)
     409           0 :                 return r;
     410             : 
     411           3 :         log_dhcp6_client(client, "Sent %s",
     412             :                          dhcp6_message_type_to_string(message->type));
     413             : 
     414           3 :         return 0;
     415             : }
     416             : 
     417           0 : static int client_timeout_t2(sd_event_source *s, uint64_t usec,
     418             :                              void *userdata) {
     419           0 :         sd_dhcp6_client *client = userdata;
     420             : 
     421           0 :         assert_return(s, -EINVAL);
     422           0 :         assert_return(client, -EINVAL);
     423           0 :         assert_return(client->lease, -EINVAL);
     424             : 
     425           0 :         client->lease->ia.timeout_t2 =
     426           0 :                 sd_event_source_unref(client->lease->ia.timeout_t2);
     427             : 
     428           0 :         log_dhcp6_client(client, "Timeout T2");
     429             : 
     430           0 :         client_start(client, DHCP6_STATE_REBIND);
     431             : 
     432           0 :         return 0;
     433             : }
     434             : 
     435           0 : static int client_timeout_t1(sd_event_source *s, uint64_t usec,
     436             :                              void *userdata) {
     437           0 :         sd_dhcp6_client *client = userdata;
     438             : 
     439           0 :         assert_return(s, -EINVAL);
     440           0 :         assert_return(client, -EINVAL);
     441           0 :         assert_return(client->lease, -EINVAL);
     442             : 
     443           0 :         client->lease->ia.timeout_t1 =
     444           0 :                 sd_event_source_unref(client->lease->ia.timeout_t1);
     445             : 
     446           0 :         log_dhcp6_client(client, "Timeout T1");
     447             : 
     448           0 :         client_start(client, DHCP6_STATE_RENEW);
     449             : 
     450           0 :         return 0;
     451             : }
     452             : 
     453           0 : static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
     454             :                                         void *userdata) {
     455           0 :         sd_dhcp6_client *client = userdata;
     456           0 :         DHCP6_CLIENT_DONT_DESTROY(client);
     457             :         enum DHCP6State state;
     458             : 
     459           0 :         assert(s);
     460           0 :         assert(client);
     461           0 :         assert(client->event);
     462             : 
     463           0 :         state = client->state;
     464             : 
     465           0 :         client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
     466             : 
     467             :         /* RFC 3315, section 18.1.4., says that "...the client may choose to
     468             :            use a Solicit message to locate a new DHCP server..." */
     469           0 :         if (state == DHCP6_STATE_REBIND)
     470           0 :                 client_start(client, DHCP6_STATE_SOLICITATION);
     471             : 
     472           0 :         return 0;
     473             : }
     474             : 
     475           5 : static usec_t client_timeout_compute_random(usec_t val) {
     476          10 :         return val - val / 10 +
     477           5 :                 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
     478             : }
     479             : 
     480           4 : static int client_timeout_resend(sd_event_source *s, uint64_t usec,
     481             :                                  void *userdata) {
     482           4 :         int r = 0;
     483           4 :         sd_dhcp6_client *client = userdata;
     484           4 :         usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
     485           4 :         usec_t max_retransmit_duration = 0;
     486           4 :         uint8_t max_retransmit_count = 0;
     487             :         char time_string[FORMAT_TIMESPAN_MAX];
     488           4 :         uint32_t expire = 0;
     489             : 
     490           4 :         assert(s);
     491           4 :         assert(client);
     492           4 :         assert(client->event);
     493             : 
     494           4 :         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
     495             : 
     496           4 :         switch (client->state) {
     497             :         case DHCP6_STATE_INFORMATION_REQUEST:
     498           1 :                 init_retransmit_time = DHCP6_INF_TIMEOUT;
     499           1 :                 max_retransmit_time = DHCP6_INF_MAX_RT;
     500             : 
     501           1 :                 break;
     502             : 
     503             :         case DHCP6_STATE_SOLICITATION:
     504             : 
     505           2 :                 if (client->retransmit_count && client->lease) {
     506           1 :                         client_start(client, DHCP6_STATE_REQUEST);
     507           1 :                         return 0;
     508             :                 }
     509             : 
     510           1 :                 init_retransmit_time = DHCP6_SOL_TIMEOUT;
     511           1 :                 max_retransmit_time = DHCP6_SOL_MAX_RT;
     512             : 
     513           1 :                 break;
     514             : 
     515             :         case DHCP6_STATE_REQUEST:
     516           1 :                 init_retransmit_time = DHCP6_REQ_TIMEOUT;
     517           1 :                 max_retransmit_time = DHCP6_REQ_MAX_RT;
     518           1 :                 max_retransmit_count = DHCP6_REQ_MAX_RC;
     519             : 
     520           1 :                 break;
     521             : 
     522             :         case DHCP6_STATE_RENEW:
     523           0 :                 init_retransmit_time = DHCP6_REN_TIMEOUT;
     524           0 :                 max_retransmit_time = DHCP6_REN_MAX_RT;
     525             : 
     526             :                 /* RFC 3315, section 18.1.3. says max retransmit duration will
     527             :                    be the remaining time until T2. Instead of setting MRD,
     528             :                    wait for T2 to trigger with the same end result */
     529             : 
     530           0 :                 break;
     531             : 
     532             :         case DHCP6_STATE_REBIND:
     533           0 :                 init_retransmit_time = DHCP6_REB_TIMEOUT;
     534           0 :                 max_retransmit_time = DHCP6_REB_MAX_RT;
     535             : 
     536           0 :                 if (!client->timeout_resend_expire) {
     537           0 :                         r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
     538             :                                                          &expire);
     539           0 :                         if (r < 0) {
     540           0 :                                 client_stop(client, r);
     541           0 :                                 return 0;
     542             :                         }
     543           0 :                         max_retransmit_duration = expire * USEC_PER_SEC;
     544             :                 }
     545             : 
     546           0 :                 break;
     547             : 
     548             :         case DHCP6_STATE_STOPPED:
     549             :         case DHCP6_STATE_BOUND:
     550           0 :                 return 0;
     551             :         }
     552             : 
     553           4 :         if (max_retransmit_count &&
     554           1 :             client->retransmit_count >= max_retransmit_count) {
     555           0 :                 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
     556           0 :                 return 0;
     557             :         }
     558             : 
     559           3 :         r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
     560           3 :         if (r < 0)
     561           0 :                 goto error;
     562             : 
     563           3 :         r = client_send_message(client, time_now);
     564           3 :         if (r >= 0)
     565           3 :                 client->retransmit_count++;
     566             : 
     567           3 :         if (!client->retransmit_time) {
     568           3 :                 client->retransmit_time =
     569           3 :                         client_timeout_compute_random(init_retransmit_time);
     570             : 
     571           3 :                 if (client->state == DHCP6_STATE_SOLICITATION)
     572           1 :                         client->retransmit_time += init_retransmit_time / 10;
     573             : 
     574             :         } else {
     575           0 :                 if (max_retransmit_time &&
     576           0 :                     client->retransmit_time > max_retransmit_time / 2)
     577           0 :                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
     578             :                 else
     579           0 :                         client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
     580             :         }
     581             : 
     582           3 :         log_dhcp6_client(client, "Next retransmission in %s",
     583             :                          format_timespan(time_string, FORMAT_TIMESPAN_MAX,
     584             :                                          client->retransmit_time, 0));
     585             : 
     586           3 :         r = sd_event_add_time(client->event, &client->timeout_resend,
     587             :                               clock_boottime_or_monotonic(),
     588           3 :                               time_now + client->retransmit_time,
     589             :                               10 * USEC_PER_MSEC, client_timeout_resend,
     590             :                               client);
     591           3 :         if (r < 0)
     592           0 :                 goto error;
     593             : 
     594           3 :         r = sd_event_source_set_priority(client->timeout_resend,
     595           3 :                                          client->event_priority);
     596           3 :         if (r < 0)
     597           0 :                 goto error;
     598             : 
     599           3 :         r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer");
     600           3 :         if (r < 0)
     601           0 :                 goto error;
     602             : 
     603           3 :         if (max_retransmit_duration && !client->timeout_resend_expire) {
     604             : 
     605           0 :                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
     606             :                                  max_retransmit_duration / USEC_PER_SEC);
     607             : 
     608           0 :                 r = sd_event_add_time(client->event,
     609             :                                       &client->timeout_resend_expire,
     610             :                                       clock_boottime_or_monotonic(),
     611             :                                       time_now + max_retransmit_duration,
     612             :                                       USEC_PER_SEC,
     613             :                                       client_timeout_resend_expire, client);
     614           0 :                 if (r < 0)
     615           0 :                         goto error;
     616             : 
     617           0 :                 r = sd_event_source_set_priority(client->timeout_resend_expire,
     618           0 :                                                  client->event_priority);
     619           0 :                 if (r < 0)
     620           0 :                         goto error;
     621             : 
     622           0 :                 r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
     623           0 :                 if (r < 0)
     624           0 :                         goto error;
     625             :         }
     626             : 
     627             : error:
     628           3 :         if (r < 0)
     629           0 :                 client_stop(client, r);
     630             : 
     631           3 :         return 0;
     632             : }
     633             : 
     634           2 : static int client_ensure_iaid(sd_dhcp6_client *client) {
     635             :         int r;
     636             : 
     637           2 :         assert(client);
     638             : 
     639           2 :         if (client->ia_na.id)
     640           1 :                 return 0;
     641             : 
     642           1 :         r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
     643           1 :         if (r < 0)
     644           0 :                 return r;
     645             : 
     646           1 :         return 0;
     647             : }
     648             : 
     649           3 : static int client_parse_message(sd_dhcp6_client *client,
     650             :                                 DHCP6Message *message, size_t len,
     651             :                                 sd_dhcp6_lease *lease) {
     652             :         int r;
     653           3 :         uint8_t *optval, *option, *id = NULL;
     654             :         uint16_t optcode, status;
     655             :         size_t optlen, id_len;
     656           3 :         bool clientid = false;
     657             :         be32_t iaid_lease;
     658             : 
     659           3 :         option = (uint8_t *)message + sizeof(DHCP6Message);
     660           3 :         len -= sizeof(DHCP6Message);
     661             : 
     662          25 :         while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
     663             :                                        &optval)) >= 0) {
     664          19 :                 switch (optcode) {
     665             :                 case DHCP6_OPTION_CLIENTID:
     666           3 :                         if (clientid) {
     667           0 :                                 log_dhcp6_client(client, "%s contains multiple clientids",
     668             :                                                  dhcp6_message_type_to_string(message->type));
     669           0 :                                 return -EINVAL;
     670             :                         }
     671             : 
     672           6 :                         if (optlen != client->duid_len ||
     673           3 :                             memcmp(&client->duid, optval, optlen) != 0) {
     674           0 :                                 log_dhcp6_client(client, "%s DUID does not match",
     675             :                                                  dhcp6_message_type_to_string(message->type));
     676             : 
     677           0 :                                 return -EINVAL;
     678             :                         }
     679           3 :                         clientid = true;
     680             : 
     681           3 :                         break;
     682             : 
     683             :                 case DHCP6_OPTION_SERVERID:
     684           3 :                         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
     685           3 :                         if (r >= 0 && id) {
     686           0 :                                 log_dhcp6_client(client, "%s contains multiple serverids",
     687             :                                                  dhcp6_message_type_to_string(message->type));
     688           0 :                                 return -EINVAL;
     689             :                         }
     690             : 
     691           3 :                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
     692           3 :                         if (r < 0)
     693           0 :                                 return r;
     694             : 
     695           3 :                         break;
     696             : 
     697             :                 case DHCP6_OPTION_PREFERENCE:
     698           1 :                         if (optlen != 1)
     699           0 :                                 return -EINVAL;
     700             : 
     701           1 :                         r = dhcp6_lease_set_preference(lease, *optval);
     702           1 :                         if (r < 0)
     703           0 :                                 return r;
     704             : 
     705           1 :                         break;
     706             : 
     707             :                 case DHCP6_OPTION_STATUS_CODE:
     708           0 :                         if (optlen < 2)
     709           0 :                                 return -EINVAL;
     710             : 
     711           0 :                         status = optval[0] << 8 | optval[1];
     712           0 :                         if (status) {
     713           0 :                                 log_dhcp6_client(client, "%s Status %s",
     714             :                                                  dhcp6_message_type_to_string(message->type),
     715             :                                                  dhcp6_message_status_to_string(status));
     716           0 :                                 return -EINVAL;
     717             :                         }
     718             : 
     719           0 :                         break;
     720             : 
     721             :                 case DHCP6_OPTION_IA_NA:
     722           3 :                         if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
     723           1 :                                 log_dhcp6_client(client, "Information request ignoring IA NA option");
     724             : 
     725           1 :                                 break;
     726             :                         }
     727             : 
     728           2 :                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
     729             :                                                   &lease->ia);
     730           2 :                         if (r < 0 && r != -ENOMSG)
     731           0 :                                 return r;
     732             : 
     733           2 :                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
     734           2 :                         if (r < 0)
     735           0 :                                 return r;
     736             : 
     737           2 :                         if (client->ia_na.id != iaid_lease) {
     738           0 :                                 log_dhcp6_client(client, "%s has wrong IAID",
     739             :                                                  dhcp6_message_type_to_string(message->type));
     740           0 :                                 return -EINVAL;
     741             :                         }
     742             : 
     743           2 :                         break;
     744             : 
     745             :                 case DHCP6_OPTION_RAPID_COMMIT:
     746           0 :                         r = dhcp6_lease_set_rapid_commit(lease);
     747           0 :                         if (r < 0)
     748           0 :                                 return r;
     749             : 
     750           0 :                         break;
     751             :                 }
     752             :         }
     753             : 
     754           3 :         if (r == -ENOMSG)
     755           3 :                 r = 0;
     756             : 
     757           3 :         if (r < 0 || !clientid) {
     758           0 :                 log_dhcp6_client(client, "%s has incomplete options",
     759             :                                  dhcp6_message_type_to_string(message->type));
     760           0 :                 return -EINVAL;
     761             :         }
     762             : 
     763           3 :         if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
     764           2 :                 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
     765           2 :                 if (r < 0)
     766           0 :                         log_dhcp6_client(client, "%s has no server id",
     767             :                                          dhcp6_message_type_to_string(message->type));
     768             :         }
     769             : 
     770           3 :         return r;
     771             : }
     772             : 
     773           3 : static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
     774             :                                 size_t len)
     775             : {
     776             :         int r;
     777           6 :         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
     778             :         bool rapid_commit;
     779             : 
     780           3 :         if (reply->type != DHCP6_REPLY)
     781           1 :                 return 0;
     782             : 
     783           2 :         r = dhcp6_lease_new(&lease);
     784           2 :         if (r < 0)
     785           0 :                 return -ENOMEM;
     786             : 
     787           2 :         r = client_parse_message(client, reply, len, lease);
     788           2 :         if (r < 0)
     789           0 :                 return r;
     790             : 
     791           2 :         if (client->state == DHCP6_STATE_SOLICITATION) {
     792           0 :                 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
     793           0 :                 if (r < 0)
     794           0 :                         return r;
     795             : 
     796           0 :                 if (!rapid_commit)
     797           0 :                         return 0;
     798             :         }
     799             : 
     800           2 :         if (client->lease) {
     801           1 :                 dhcp6_lease_clear_timers(&client->lease->ia);
     802           1 :                 client->lease = sd_dhcp6_lease_unref(client->lease);
     803             :         }
     804             : 
     805           2 :         if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
     806           1 :                 client->lease = lease;
     807           1 :                 lease = NULL;
     808             :         }
     809             : 
     810           2 :         return DHCP6_STATE_BOUND;
     811             : }
     812             : 
     813           1 : static int client_receive_advertise(sd_dhcp6_client *client,
     814             :                                     DHCP6Message *advertise, size_t len) {
     815             :         int r;
     816           2 :         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
     817           1 :         uint8_t pref_advertise = 0, pref_lease = 0;
     818             : 
     819           1 :         if (advertise->type != DHCP6_ADVERTISE)
     820           0 :                 return 0;
     821             : 
     822           1 :         r = dhcp6_lease_new(&lease);
     823           1 :         if (r < 0)
     824           0 :                 return r;
     825             : 
     826           1 :         r = client_parse_message(client, advertise, len, lease);
     827           1 :         if (r < 0)
     828           0 :                 return r;
     829             : 
     830           1 :         r = dhcp6_lease_get_preference(lease, &pref_advertise);
     831           1 :         if (r < 0)
     832           0 :                 return r;
     833             : 
     834           1 :         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
     835             : 
     836           1 :         if (r < 0 || pref_advertise > pref_lease) {
     837           1 :                 sd_dhcp6_lease_unref(client->lease);
     838           1 :                 client->lease = lease;
     839           1 :                 lease = NULL;
     840           1 :                 r = 0;
     841             :         }
     842             : 
     843           1 :         if (pref_advertise == 255 || client->retransmit_count > 1)
     844           0 :                 r = DHCP6_STATE_REQUEST;
     845             : 
     846           1 :         return r;
     847             : }
     848             : 
     849           3 : static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
     850             :                                   void *userdata) {
     851           3 :         sd_dhcp6_client *client = userdata;
     852           6 :         DHCP6_CLIENT_DONT_DESTROY(client);
     853           3 :         _cleanup_free_ DHCP6Message *message;
     854             :         int r, buflen, len;
     855             : 
     856           3 :         assert(s);
     857           3 :         assert(client);
     858           3 :         assert(client->event);
     859             : 
     860           3 :         r = ioctl(fd, FIONREAD, &buflen);
     861           3 :         if (r < 0 || buflen <= 0)
     862           0 :                 buflen = DHCP6_MIN_OPTIONS_SIZE;
     863             : 
     864           3 :         message = malloc0(buflen);
     865           3 :         if (!message)
     866           0 :                 return -ENOMEM;
     867             : 
     868           3 :         len = read(fd, message, buflen);
     869           3 :         if ((size_t)len < sizeof(DHCP6Message)) {
     870           0 :                 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
     871           0 :                 return 0;
     872             :         }
     873             : 
     874           3 :         switch(message->type) {
     875             :         case DHCP6_SOLICIT:
     876             :         case DHCP6_REQUEST:
     877             :         case DHCP6_CONFIRM:
     878             :         case DHCP6_RENEW:
     879             :         case DHCP6_REBIND:
     880             :         case DHCP6_RELEASE:
     881             :         case DHCP6_DECLINE:
     882             :         case DHCP6_INFORMATION_REQUEST:
     883             :         case DHCP6_RELAY_FORW:
     884             :         case DHCP6_RELAY_REPL:
     885           0 :                 return 0;
     886             : 
     887             :         case DHCP6_ADVERTISE:
     888             :         case DHCP6_REPLY:
     889             :         case DHCP6_RECONFIGURE:
     890           3 :                 break;
     891             : 
     892             :         default:
     893           0 :                 log_dhcp6_client(client, "unknown message type %d",
     894             :                                  message->type);
     895           0 :                 return 0;
     896             :         }
     897             : 
     898           6 :         if (client->transaction_id != (message->transaction_id &
     899           3 :                                        htobe32(0x00ffffff)))
     900           0 :                 return 0;
     901             : 
     902           3 :         switch (client->state) {
     903             :         case DHCP6_STATE_INFORMATION_REQUEST:
     904           1 :                 r = client_receive_reply(client, message, len);
     905           1 :                 if (r < 0)
     906           0 :                         return 0;
     907             : 
     908           1 :                 client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
     909             : 
     910           1 :                 client_start(client, DHCP6_STATE_STOPPED);
     911             : 
     912           1 :                 break;
     913             : 
     914             :         case DHCP6_STATE_SOLICITATION:
     915           1 :                 r = client_receive_advertise(client, message, len);
     916             : 
     917           1 :                 if (r == DHCP6_STATE_REQUEST) {
     918           0 :                         client_start(client, r);
     919             : 
     920           0 :                         break;
     921             :                 }
     922             : 
     923             :                 /* fall through for Soliciation Rapid Commit option check */
     924             :         case DHCP6_STATE_REQUEST:
     925             :         case DHCP6_STATE_RENEW:
     926             :         case DHCP6_STATE_REBIND:
     927             : 
     928           2 :                 r = client_receive_reply(client, message, len);
     929           2 :                 if (r < 0)
     930           0 :                         return 0;
     931             : 
     932           2 :                 if (r == DHCP6_STATE_BOUND) {
     933             : 
     934           1 :                         r = client_start(client, DHCP6_STATE_BOUND);
     935           1 :                         if (r < 0) {
     936           0 :                                 client_stop(client, r);
     937           0 :                                 return 0;
     938             :                         }
     939             : 
     940           1 :                         client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
     941             :                 }
     942             : 
     943           2 :                 break;
     944             : 
     945             :         case DHCP6_STATE_BOUND:
     946             : 
     947           0 :                 break;
     948             : 
     949             :         case DHCP6_STATE_STOPPED:
     950           0 :                 return 0;
     951             :         }
     952             : 
     953           3 :         if (r >= 0) {
     954           3 :                 log_dhcp6_client(client, "Recv %s",
     955             :                                  dhcp6_message_type_to_string(message->type));
     956             :         }
     957             : 
     958           3 :         return 0;
     959             : }
     960             : 
     961           5 : static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
     962             : {
     963             :         int r;
     964             :         usec_t timeout, time_now;
     965             :         char time_string[FORMAT_TIMESPAN_MAX];
     966             : 
     967           5 :         assert_return(client, -EINVAL);
     968           5 :         assert_return(client->event, -EINVAL);
     969           5 :         assert_return(client->index > 0, -EINVAL);
     970           5 :         assert_return(client->state != state, -EINVAL);
     971             : 
     972           5 :         client->timeout_resend_expire =
     973           5 :                 sd_event_source_unref(client->timeout_resend_expire);
     974           5 :         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
     975           5 :         client->retransmit_time = 0;
     976           5 :         client->retransmit_count = 0;
     977             : 
     978           5 :         if (client->state == DHCP6_STATE_STOPPED) {
     979           2 :                 time_now = now(clock_boottime_or_monotonic());
     980             :         } else {
     981           3 :                 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
     982             :                                  &time_now);
     983           3 :                 if (r < 0)
     984           0 :                         return r;
     985             :         }
     986             : 
     987           5 :         switch (state) {
     988             :         case DHCP6_STATE_STOPPED:
     989           1 :                 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
     990           0 :                         client->state = DHCP6_STATE_STOPPED;
     991             : 
     992           0 :                         return 0;
     993             :                 }
     994             : 
     995             :                 /* fall through */
     996             :         case DHCP6_STATE_SOLICITATION:
     997           2 :                 client->state = DHCP6_STATE_SOLICITATION;
     998             : 
     999           2 :                 break;
    1000             : 
    1001             :         case DHCP6_STATE_INFORMATION_REQUEST:
    1002             :         case DHCP6_STATE_REQUEST:
    1003             :         case DHCP6_STATE_RENEW:
    1004             :         case DHCP6_STATE_REBIND:
    1005             : 
    1006           2 :                 client->state = state;
    1007             : 
    1008           2 :                 break;
    1009             : 
    1010             :         case DHCP6_STATE_BOUND:
    1011             : 
    1012           2 :                 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
    1013           1 :                     client->lease->ia.lifetime_t2 == 0xffffffff) {
    1014             : 
    1015           0 :                         log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
    1016             :                                          be32toh(client->lease->ia.lifetime_t1),
    1017             :                                          be32toh(client->lease->ia.lifetime_t2));
    1018             : 
    1019           0 :                         return 0;
    1020             :                 }
    1021             : 
    1022           1 :                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
    1023             : 
    1024           1 :                 log_dhcp6_client(client, "T1 expires in %s",
    1025             :                                  format_timespan(time_string,
    1026             :                                                  FORMAT_TIMESPAN_MAX,
    1027             :                                                  timeout, 0));
    1028             : 
    1029           2 :                 r = sd_event_add_time(client->event,
    1030           1 :                                       &client->lease->ia.timeout_t1,
    1031             :                                       clock_boottime_or_monotonic(), time_now + timeout,
    1032             :                                       10 * USEC_PER_SEC, client_timeout_t1,
    1033             :                                       client);
    1034           1 :                 if (r < 0)
    1035           0 :                         return r;
    1036             : 
    1037           1 :                 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
    1038           1 :                                                  client->event_priority);
    1039           1 :                 if (r < 0)
    1040           0 :                         return r;
    1041             : 
    1042           1 :                 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
    1043           1 :                 if (r < 0)
    1044           0 :                         return r;
    1045             : 
    1046           1 :                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
    1047             : 
    1048           1 :                 log_dhcp6_client(client, "T2 expires in %s",
    1049             :                                  format_timespan(time_string,
    1050             :                                                  FORMAT_TIMESPAN_MAX,
    1051             :                                                  timeout, 0));
    1052             : 
    1053           2 :                 r = sd_event_add_time(client->event,
    1054           1 :                                       &client->lease->ia.timeout_t2,
    1055             :                                       clock_boottime_or_monotonic(), time_now + timeout,
    1056             :                                       10 * USEC_PER_SEC, client_timeout_t2,
    1057             :                                       client);
    1058           1 :                 if (r < 0)
    1059           0 :                         return r;
    1060             : 
    1061           1 :                 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
    1062           1 :                                                  client->event_priority);
    1063           1 :                 if (r < 0)
    1064           0 :                         return r;
    1065             : 
    1066           1 :                 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
    1067           1 :                 if (r < 0)
    1068           0 :                         return r;
    1069             : 
    1070           1 :                 client->state = state;
    1071             : 
    1072           1 :                 return 0;
    1073             :         }
    1074             : 
    1075           4 :         client->transaction_id = random_u32() & htobe32(0x00ffffff);
    1076           4 :         client->transaction_start = time_now;
    1077             : 
    1078           4 :         r = sd_event_add_time(client->event, &client->timeout_resend,
    1079             :                               clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
    1080             :                               client);
    1081           4 :         if (r < 0)
    1082           0 :                 return r;
    1083             : 
    1084           4 :         r = sd_event_source_set_priority(client->timeout_resend,
    1085           4 :                                          client->event_priority);
    1086           4 :         if (r < 0)
    1087           0 :                 return r;
    1088             : 
    1089           4 :         r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
    1090           4 :         if (r < 0)
    1091           0 :                 return r;
    1092             : 
    1093           4 :         return 0;
    1094             : }
    1095             : 
    1096           0 : int sd_dhcp6_client_stop(sd_dhcp6_client *client)
    1097             : {
    1098           0 :         client_stop(client, DHCP6_EVENT_STOP);
    1099             : 
    1100           0 :         return 0;
    1101             : }
    1102             : 
    1103           2 : int sd_dhcp6_client_start(sd_dhcp6_client *client)
    1104             : {
    1105           2 :         int r = 0;
    1106           2 :         enum DHCP6State state = DHCP6_STATE_SOLICITATION;
    1107             : 
    1108           2 :         assert_return(client, -EINVAL);
    1109           2 :         assert_return(client->event, -EINVAL);
    1110           2 :         assert_return(client->index > 0, -EINVAL);
    1111             : 
    1112           2 :         r = client_reset(client);
    1113           2 :         if (r < 0)
    1114           0 :                 return r;
    1115             : 
    1116           2 :         r = client_ensure_iaid(client);
    1117           2 :         if (r < 0)
    1118           0 :                 return r;
    1119             : 
    1120           2 :         r = client_ensure_duid(client);
    1121           2 :         if (r < 0)
    1122           0 :                 return r;
    1123             : 
    1124           2 :         r = dhcp6_network_bind_udp_socket(client->index, NULL);
    1125           2 :         if (r < 0)
    1126           0 :                 return r;
    1127             : 
    1128           2 :         client->fd = r;
    1129             : 
    1130           2 :         r = sd_event_add_io(client->event, &client->receive_message,
    1131             :                             client->fd, EPOLLIN, client_receive_message,
    1132             :                             client);
    1133           2 :         if (r < 0)
    1134           0 :                 goto error;
    1135             : 
    1136           2 :         r = sd_event_source_set_priority(client->receive_message,
    1137           2 :                                          client->event_priority);
    1138           2 :         if (r < 0)
    1139           0 :                 goto error;
    1140             : 
    1141           2 :         r = sd_event_source_set_description(client->receive_message,
    1142             :                                         "dhcp6-receive-message");
    1143           2 :         if (r < 0)
    1144           0 :                 goto error;
    1145             : 
    1146           2 :         if (client->information_request)
    1147           1 :                 state = DHCP6_STATE_INFORMATION_REQUEST;
    1148             : 
    1149           2 :         log_dhcp6_client(client, "Started in %s mode",
    1150             :                         client->information_request? "Information request":
    1151             :                         "Managed");
    1152             : 
    1153           2 :         return client_start(client, state);
    1154             : 
    1155             : error:
    1156           0 :         client_reset(client);
    1157           0 :         return r;
    1158             : }
    1159             : 
    1160           2 : int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
    1161             :                                  int priority)
    1162             : {
    1163             :         int r;
    1164             : 
    1165           2 :         assert_return(client, -EINVAL);
    1166           2 :         assert_return(!client->event, -EBUSY);
    1167             : 
    1168           2 :         if (event)
    1169           2 :                 client->event = sd_event_ref(event);
    1170             :         else {
    1171           0 :                 r = sd_event_default(&client->event);
    1172           0 :                 if (r < 0)
    1173           0 :                         return 0;
    1174             :         }
    1175             : 
    1176           2 :         client->event_priority = priority;
    1177             : 
    1178           2 :         return 0;
    1179             : }
    1180             : 
    1181           3 : int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
    1182           3 :         assert_return(client, -EINVAL);
    1183             : 
    1184           3 :         client->event = sd_event_unref(client->event);
    1185             : 
    1186           3 :         return 0;
    1187             : }
    1188             : 
    1189           0 : sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
    1190           0 :         if (!client)
    1191           0 :                 return NULL;
    1192             : 
    1193           0 :         return client->event;
    1194             : }
    1195             : 
    1196           3 : sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
    1197           3 :         if (client)
    1198           3 :                 assert_se(REFCNT_INC(client->n_ref) >= 2);
    1199             : 
    1200           3 :         return client;
    1201             : }
    1202             : 
    1203           9 : sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
    1204           9 :         if (client && REFCNT_DEC(client->n_ref) == 0) {
    1205           2 :                 client_reset(client);
    1206             : 
    1207           2 :                 sd_dhcp6_client_detach_event(client);
    1208           2 :                 sd_dhcp6_lease_unref(client->lease);
    1209             : 
    1210           2 :                 free(client->req_opts);
    1211           2 :                 free(client);
    1212             : 
    1213           2 :                 return NULL;
    1214             :         }
    1215             : 
    1216           7 :         return client;
    1217             : }
    1218             : 
    1219           2 : int sd_dhcp6_client_new(sd_dhcp6_client **ret)
    1220             : {
    1221           4 :         _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
    1222             :         size_t t;
    1223             : 
    1224           2 :         assert_return(ret, -EINVAL);
    1225             : 
    1226           2 :         client = new0(sd_dhcp6_client, 1);
    1227           2 :         if (!client)
    1228           0 :                 return -ENOMEM;
    1229             : 
    1230           2 :         client->n_ref = REFCNT_INIT;
    1231             : 
    1232           2 :         client->ia_na.type = DHCP6_OPTION_IA_NA;
    1233             : 
    1234           2 :         client->index = -1;
    1235             : 
    1236           2 :         client->fd = -1;
    1237             : 
    1238           2 :         client->req_opts_len = ELEMENTSOF(default_req_opts);
    1239             : 
    1240           2 :         client->req_opts = new0(be16_t, client->req_opts_len);
    1241           2 :         if (!client->req_opts)
    1242           0 :                 return -ENOMEM;
    1243             : 
    1244           8 :         for (t = 0; t < client->req_opts_len; t++)
    1245           6 :                 client->req_opts[t] = htobe16(default_req_opts[t]);
    1246             : 
    1247           2 :         *ret = client;
    1248           2 :         client = NULL;
    1249             : 
    1250           2 :         return 0;
    1251             : }

Generated by: LCOV version 1.11