LCOV - code coverage report
Current view: top level - libsystemd-network - sd-dhcp-client.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 470 863 54.5 %
Date: 2015-07-29 18:47:03 Functions: 31 43 72.1 %

          Line data    Source code
       1             : /***
       2             :   This file is part of systemd.
       3             : 
       4             :   Copyright (C) 2013 Intel Corporation. All rights reserved.
       5             : 
       6             :   systemd is free software; you can redistribute it and/or modify it
       7             :   under the terms of the GNU Lesser General Public License as published by
       8             :   the Free Software Foundation; either version 2.1 of the License, or
       9             :   (at your option) any later version.
      10             : 
      11             :   systemd is distributed in the hope that it will be useful, but
      12             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      14             :   Lesser General Public License for more details.
      15             : 
      16             :   You should have received a copy of the GNU Lesser General Public License
      17             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      18             : ***/
      19             : 
      20             : #include <stdlib.h>
      21             : #include <errno.h>
      22             : #include <string.h>
      23             : #include <stdio.h>
      24             : #include <net/ethernet.h>
      25             : #include <net/if_arp.h>
      26             : #include <linux/if_infiniband.h>
      27             : #include <sys/ioctl.h>
      28             : 
      29             : #include "util.h"
      30             : #include "refcnt.h"
      31             : #include "random-util.h"
      32             : #include "async.h"
      33             : 
      34             : #include "dhcp-protocol.h"
      35             : #include "dhcp-internal.h"
      36             : #include "dhcp-lease-internal.h"
      37             : #include "dhcp-identifier.h"
      38             : #include "sd-dhcp-client.h"
      39             : 
      40             : #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN)  /* Arbitrary limit */
      41             : #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
      42             : 
      43             : struct sd_dhcp_client {
      44             :         RefCount n_ref;
      45             : 
      46             :         DHCPState state;
      47             :         sd_event *event;
      48             :         int event_priority;
      49             :         sd_event_source *timeout_resend;
      50             :         int index;
      51             :         int fd;
      52             :         union sockaddr_union link;
      53             :         sd_event_source *receive_message;
      54             :         bool request_broadcast;
      55             :         uint8_t *req_opts;
      56             :         size_t req_opts_allocated;
      57             :         size_t req_opts_size;
      58             :         be32_t last_addr;
      59             :         uint8_t mac_addr[MAX_MAC_ADDR_LEN];
      60             :         size_t mac_addr_len;
      61             :         uint16_t arp_type;
      62             :         struct {
      63             :                 uint8_t type;
      64             :                 union {
      65             :                         struct {
      66             :                                 /* 0: Generic (non-LL) (RFC 2132) */
      67             :                                 uint8_t data[MAX_CLIENT_ID_LEN];
      68             :                         } _packed_ gen;
      69             :                         struct {
      70             :                                 /* 1: Ethernet Link-Layer (RFC 2132) */
      71             :                                 uint8_t haddr[ETH_ALEN];
      72             :                         } _packed_ eth;
      73             :                         struct {
      74             :                                 /* 2 - 254: ARP/Link-Layer (RFC 2132) */
      75             :                                 uint8_t haddr[0];
      76             :                         } _packed_ ll;
      77             :                         struct {
      78             :                                 /* 255: Node-specific (RFC 4361) */
      79             :                                 uint32_t iaid;
      80             :                                 struct duid duid;
      81             :                         } _packed_ ns;
      82             :                         struct {
      83             :                                 uint8_t data[MAX_CLIENT_ID_LEN];
      84             :                         } _packed_ raw;
      85             :                 };
      86             :         } _packed_ client_id;
      87             :         size_t client_id_len;
      88             :         char *hostname;
      89             :         char *vendor_class_identifier;
      90             :         uint32_t mtu;
      91             :         uint32_t xid;
      92             :         usec_t start_time;
      93             :         unsigned int attempt;
      94             :         usec_t request_sent;
      95             :         sd_event_source *timeout_t1;
      96             :         sd_event_source *timeout_t2;
      97             :         sd_event_source *timeout_expire;
      98             :         sd_dhcp_client_cb_t cb;
      99             :         void *userdata;
     100             :         sd_dhcp_lease *lease;
     101             : };
     102             : 
     103             : static const uint8_t default_req_opts[] = {
     104             :         DHCP_OPTION_SUBNET_MASK,
     105             :         DHCP_OPTION_ROUTER,
     106             :         DHCP_OPTION_HOST_NAME,
     107             :         DHCP_OPTION_DOMAIN_NAME,
     108             :         DHCP_OPTION_DOMAIN_NAME_SERVER,
     109             :         DHCP_OPTION_NTP_SERVER,
     110             : };
     111             : 
     112             : static int client_receive_message_raw(sd_event_source *s, int fd,
     113             :                                       uint32_t revents, void *userdata);
     114             : static int client_receive_message_udp(sd_event_source *s, int fd,
     115             :                                       uint32_t revents, void *userdata);
     116             : static void client_stop(sd_dhcp_client *client, int error);
     117             : 
     118           2 : int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
     119             :                                 void *userdata) {
     120           2 :         assert_return(client, -EINVAL);
     121             : 
     122           2 :         client->cb = cb;
     123           2 :         client->userdata = userdata;
     124             : 
     125           2 :         return 0;
     126             : }
     127             : 
     128           0 : int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
     129           0 :         assert_return(client, -EINVAL);
     130             : 
     131           0 :         client->request_broadcast = !!broadcast;
     132             : 
     133           0 :         return 0;
     134             : }
     135             : 
     136          17 : int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
     137             :         size_t i;
     138             : 
     139          17 :         assert_return(client, -EINVAL);
     140          16 :         assert_return (IN_SET(client->state, DHCP_STATE_INIT,
     141             :                               DHCP_STATE_STOPPED), -EBUSY);
     142             : 
     143          16 :         switch(option) {
     144             :         case DHCP_OPTION_PAD:
     145             :         case DHCP_OPTION_OVERLOAD:
     146             :         case DHCP_OPTION_MESSAGE_TYPE:
     147             :         case DHCP_OPTION_PARAMETER_REQUEST_LIST:
     148             :         case DHCP_OPTION_END:
     149           5 :                 return -EINVAL;
     150             : 
     151             :         default:
     152          11 :                 break;
     153             :         }
     154             : 
     155          57 :         for (i = 0; i < client->req_opts_size; i++)
     156          54 :                 if (client->req_opts[i] == option)
     157           8 :                         return -EEXIST;
     158             : 
     159           3 :         if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
     160             :                             client->req_opts_size + 1))
     161           0 :                 return -ENOMEM;
     162             : 
     163           3 :         client->req_opts[client->req_opts_size++] = option;
     164             : 
     165           3 :         return 0;
     166             : }
     167             : 
     168           1 : int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
     169             :                                        const struct in_addr *last_addr) {
     170           1 :         assert_return(client, -EINVAL);
     171           0 :         assert_return (IN_SET(client->state, DHCP_STATE_INIT,
     172             :                               DHCP_STATE_STOPPED), -EBUSY);
     173             : 
     174           0 :         if (last_addr)
     175           0 :                 client->last_addr = last_addr->s_addr;
     176             :         else
     177           0 :                 client->last_addr = INADDR_ANY;
     178             : 
     179           0 :         return 0;
     180             : }
     181             : 
     182           8 : int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
     183           8 :         assert_return(client, -EINVAL);
     184           7 :         assert_return (IN_SET(client->state, DHCP_STATE_INIT,
     185             :                               DHCP_STATE_STOPPED), -EBUSY);
     186           7 :         assert_return(interface_index > 0, -EINVAL);
     187             : 
     188           4 :         client->index = interface_index;
     189             : 
     190           4 :         return 0;
     191             : }
     192             : 
     193           2 : int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
     194             :                            size_t addr_len, uint16_t arp_type) {
     195           4 :         DHCP_CLIENT_DONT_DESTROY(client);
     196           2 :         bool need_restart = false;
     197             : 
     198           2 :         assert_return(client, -EINVAL);
     199           2 :         assert_return(addr, -EINVAL);
     200           2 :         assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
     201           2 :         assert_return(arp_type > 0, -EINVAL);
     202             : 
     203           2 :         if (arp_type == ARPHRD_ETHER)
     204           2 :                 assert_return(addr_len == ETH_ALEN, -EINVAL);
     205           0 :         else if (arp_type == ARPHRD_INFINIBAND)
     206           0 :                 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
     207             :         else
     208           0 :                 return -EINVAL;
     209             : 
     210           2 :         if (client->mac_addr_len == addr_len &&
     211           0 :             memcmp(&client->mac_addr, addr, addr_len) == 0)
     212           0 :                 return 0;
     213             : 
     214           2 :         if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
     215           0 :                 log_dhcp_client(client, "Changing MAC address on running DHCP "
     216             :                                 "client, restarting");
     217           0 :                 need_restart = true;
     218           0 :                 client_stop(client, DHCP_EVENT_STOP);
     219             :         }
     220             : 
     221           2 :         memcpy(&client->mac_addr, addr, addr_len);
     222           2 :         client->mac_addr_len = addr_len;
     223           2 :         client->arp_type = arp_type;
     224             : 
     225           2 :         if (need_restart && client->state != DHCP_STATE_STOPPED)
     226           0 :                 sd_dhcp_client_start(client);
     227             : 
     228           2 :         return 0;
     229             : }
     230             : 
     231           0 : int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
     232             :                                  const uint8_t **data, size_t *data_len) {
     233             : 
     234           0 :         assert_return(client, -EINVAL);
     235           0 :         assert_return(type, -EINVAL);
     236           0 :         assert_return(data, -EINVAL);
     237           0 :         assert_return(data_len, -EINVAL);
     238             : 
     239           0 :         *type = 0;
     240           0 :         *data = NULL;
     241           0 :         *data_len = 0;
     242           0 :         if (client->client_id_len) {
     243           0 :                 *type = client->client_id.type;
     244           0 :                 *data = client->client_id.raw.data;
     245           0 :                 *data_len = client->client_id_len - sizeof(client->client_id.type);
     246             :         }
     247             : 
     248           0 :         return 0;
     249             : }
     250             : 
     251           0 : int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
     252             :                                  const uint8_t *data, size_t data_len) {
     253           0 :         DHCP_CLIENT_DONT_DESTROY(client);
     254           0 :         bool need_restart = false;
     255             : 
     256           0 :         assert_return(client, -EINVAL);
     257           0 :         assert_return(data, -EINVAL);
     258           0 :         assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
     259             : 
     260           0 :         switch (type) {
     261             :         case ARPHRD_ETHER:
     262           0 :                 if (data_len != ETH_ALEN)
     263           0 :                         return -EINVAL;
     264           0 :                 break;
     265             :         case ARPHRD_INFINIBAND:
     266           0 :                 if (data_len != INFINIBAND_ALEN)
     267           0 :                         return -EINVAL;
     268           0 :                 break;
     269             :         default:
     270           0 :                 break;
     271             :         }
     272             : 
     273           0 :         if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
     274           0 :             client->client_id.type == type &&
     275           0 :             memcmp(&client->client_id.raw.data, data, data_len) == 0)
     276           0 :                 return 0;
     277             : 
     278           0 :         if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
     279           0 :                 log_dhcp_client(client, "Changing client ID on running DHCP "
     280             :                                 "client, restarting");
     281           0 :                 need_restart = true;
     282           0 :                 client_stop(client, DHCP_EVENT_STOP);
     283             :         }
     284             : 
     285           0 :         client->client_id.type = type;
     286           0 :         memcpy(&client->client_id.raw.data, data, data_len);
     287           0 :         client->client_id_len = data_len + sizeof (client->client_id.type);
     288             : 
     289           0 :         if (need_restart && client->state != DHCP_STATE_STOPPED)
     290           0 :                 sd_dhcp_client_start(client);
     291             : 
     292           0 :         return 0;
     293             : }
     294             : 
     295           0 : int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
     296             :                                 const char *hostname) {
     297           0 :         char *new_hostname = NULL;
     298             : 
     299           0 :         assert_return(client, -EINVAL);
     300             : 
     301           0 :         if (streq_ptr(client->hostname, hostname))
     302           0 :                 return 0;
     303             : 
     304           0 :         if (hostname) {
     305           0 :                 new_hostname = strdup(hostname);
     306           0 :                 if (!new_hostname)
     307           0 :                         return -ENOMEM;
     308             :         }
     309             : 
     310           0 :         free(client->hostname);
     311           0 :         client->hostname = new_hostname;
     312             : 
     313           0 :         return 0;
     314             : }
     315             : 
     316           0 : int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
     317             :                                                const char *vci) {
     318           0 :         char *new_vci = NULL;
     319             : 
     320           0 :         assert_return(client, -EINVAL);
     321             : 
     322           0 :         new_vci = strdup(vci);
     323           0 :         if (!new_vci)
     324           0 :                 return -ENOMEM;
     325             : 
     326           0 :         free(client->vendor_class_identifier);
     327             : 
     328           0 :         client->vendor_class_identifier = new_vci;
     329             : 
     330           0 :         return 0;
     331             : }
     332             : 
     333           0 : int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
     334           0 :         assert_return(client, -EINVAL);
     335           0 :         assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
     336             : 
     337           0 :         client->mtu = mtu;
     338             : 
     339           0 :         return 0;
     340             : }
     341             : 
     342           1 : int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
     343           1 :         assert_return(client, -EINVAL);
     344           1 :         assert_return(ret, -EINVAL);
     345             : 
     346           1 :         if (client->state != DHCP_STATE_BOUND &&
     347           0 :             client->state != DHCP_STATE_RENEWING &&
     348           0 :             client->state != DHCP_STATE_REBINDING)
     349           0 :                 return -EADDRNOTAVAIL;
     350             : 
     351           1 :         *ret = sd_dhcp_lease_ref(client->lease);
     352             : 
     353           1 :         return 0;
     354             : }
     355             : 
     356           3 : static void client_notify(sd_dhcp_client *client, int event) {
     357           3 :         if (client->cb)
     358           1 :                 client->cb(client, event, client->userdata);
     359           3 : }
     360             : 
     361           7 : static int client_initialize(sd_dhcp_client *client) {
     362           7 :         assert_return(client, -EINVAL);
     363             : 
     364           7 :         client->receive_message =
     365           7 :                 sd_event_source_unref(client->receive_message);
     366             : 
     367           7 :         client->fd = asynchronous_close(client->fd);
     368             : 
     369           7 :         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
     370             : 
     371           7 :         client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
     372           7 :         client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
     373           7 :         client->timeout_expire = sd_event_source_unref(client->timeout_expire);
     374             : 
     375           7 :         client->attempt = 1;
     376             : 
     377           7 :         client->state = DHCP_STATE_INIT;
     378           7 :         client->xid = 0;
     379             : 
     380           7 :         if (client->lease)
     381           1 :                 client->lease = sd_dhcp_lease_unref(client->lease);
     382             : 
     383           7 :         return 0;
     384             : }
     385             : 
     386           2 : static void client_stop(sd_dhcp_client *client, int error) {
     387           2 :         assert(client);
     388             : 
     389           2 :         if (error < 0)
     390           0 :                 log_dhcp_client(client, "STOPPED: %s", strerror(-error));
     391           2 :         else if (error == DHCP_EVENT_STOP)
     392           2 :                 log_dhcp_client(client, "STOPPED");
     393             :         else
     394           0 :                 log_dhcp_client(client, "STOPPED: Unknown event");
     395             : 
     396           2 :         client_notify(client, error);
     397             : 
     398           2 :         client_initialize(client);
     399           2 : }
     400             : 
     401           3 : static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
     402             :                                uint8_t type, size_t *_optlen, size_t *_optoffset) {
     403           3 :         _cleanup_free_ DHCPPacket *packet;
     404             :         size_t optlen, optoffset, size;
     405             :         be16_t max_size;
     406             :         usec_t time_now;
     407             :         uint16_t secs;
     408             :         int r;
     409             : 
     410           3 :         assert(client);
     411           3 :         assert(client->start_time);
     412           3 :         assert(ret);
     413           3 :         assert(_optlen);
     414           3 :         assert(_optoffset);
     415           3 :         assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
     416             : 
     417           3 :         optlen = DHCP_MIN_OPTIONS_SIZE;
     418           3 :         size = sizeof(DHCPPacket) + optlen;
     419             : 
     420           3 :         packet = malloc0(size);
     421           3 :         if (!packet)
     422           0 :                 return -ENOMEM;
     423             : 
     424           3 :         r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
     425           3 :                               client->arp_type, optlen, &optoffset);
     426           3 :         if (r < 0)
     427           0 :                 return r;
     428             : 
     429             :         /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
     430             :            refuse to issue an DHCP lease if 'secs' is set to zero */
     431           3 :         r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
     432           3 :         if (r < 0)
     433           0 :                 return r;
     434           3 :         assert(time_now >= client->start_time);
     435             : 
     436             :         /* seconds between sending first and last DISCOVER
     437             :          * must always be strictly positive to deal with broken servers */
     438           3 :         secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
     439           3 :         packet->dhcp.secs = htobe16(secs);
     440             : 
     441             :         /* RFC2132 section 4.1
     442             :            A client that cannot receive unicast IP datagrams until its protocol
     443             :            software has been configured with an IP address SHOULD set the
     444             :            BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
     445             :            DHCPREQUEST messages that client sends.  The BROADCAST bit will
     446             :            provide a hint to the DHCP server and BOOTP relay agent to broadcast
     447             :            any messages to the client on the client's subnet.
     448             : 
     449             :            Note: some interfaces needs this to be enabled, but some networks
     450             :            needs this to be disabled as broadcasts are filteretd, so this
     451             :            needs to be configurable */
     452           3 :         if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
     453           0 :                 packet->dhcp.flags = htobe16(0x8000);
     454             : 
     455             :         /* RFC2132 section 4.1.1:
     456             :            The client MUST include its hardware address in the ’chaddr’ field, if
     457             :            necessary for delivery of DHCP reply messages.  Non-Ethernet
     458             :            interfaces will leave 'chaddr' empty and use the client identifier
     459             :            instead (eg, RFC 4390 section 2.1).
     460             :          */
     461           3 :         if (client->arp_type == ARPHRD_ETHER)
     462           3 :                 memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
     463             : 
     464             :         /* If no client identifier exists, construct an RFC 4361-compliant one */
     465           3 :         if (client->client_id_len == 0) {
     466             :                 size_t duid_len;
     467             : 
     468           2 :                 client->client_id.type = 255;
     469             : 
     470           2 :                 r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
     471           2 :                 if (r < 0)
     472           0 :                         return r;
     473             : 
     474           2 :                 r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
     475           2 :                 if (r < 0)
     476           0 :                         return r;
     477             : 
     478           2 :                 client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
     479             :         }
     480             : 
     481             :         /* Some DHCP servers will refuse to issue an DHCP lease if the Client
     482             :            Identifier option is not set */
     483           3 :         if (client->client_id_len) {
     484           3 :                 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
     485             :                                        DHCP_OPTION_CLIENT_IDENTIFIER,
     486             :                                        client->client_id_len,
     487           3 :                                        &client->client_id);
     488           3 :                 if (r < 0)
     489           0 :                         return r;
     490             :         }
     491             : 
     492             :         /* RFC2131 section 3.5:
     493             :            in its initial DHCPDISCOVER or DHCPREQUEST message, a
     494             :            client may provide the server with a list of specific
     495             :            parameters the client is interested in. If the client
     496             :            includes a list of parameters in a DHCPDISCOVER message,
     497             :            it MUST include that list in any subsequent DHCPREQUEST
     498             :            messages.
     499             :          */
     500           3 :         r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
     501             :                                DHCP_OPTION_PARAMETER_REQUEST_LIST,
     502           3 :                                client->req_opts_size, client->req_opts);
     503           3 :         if (r < 0)
     504           0 :                 return r;
     505             : 
     506             :         /* RFC2131 section 3.5:
     507             :            The client SHOULD include the ’maximum DHCP message size’ option to
     508             :            let the server know how large the server may make its DHCP messages.
     509             : 
     510             :            Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
     511             :            than the defined default size unless the Maximum Messge Size option
     512             :            is explicitly set
     513             : 
     514             :            RFC3442 "Requirements to Avoid Sizing Constraints":
     515             :            Because a full routing table can be quite large, the standard 576
     516             :            octet maximum size for a DHCP message may be too short to contain
     517             :            some legitimate Classless Static Route options.  Because of this,
     518             :            clients implementing the Classless Static Route option SHOULD send a
     519             :            Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP
     520             :            stack is capable of receiving larger IP datagrams.  In this case, the
     521             :            client SHOULD set the value of this option to at least the MTU of the
     522             :            interface that the client is configuring.  The client MAY set the
     523             :            value of this option higher, up to the size of the largest UDP packet
     524             :            it is prepared to accept.  (Note that the value specified in the
     525             :            Maximum DHCP Message Size option is the total maximum packet size,
     526             :            including IP and UDP headers.)
     527             :          */
     528           3 :         max_size = htobe16(size);
     529           3 :         r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
     530             :                                DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
     531             :                                2, &max_size);
     532           3 :         if (r < 0)
     533           0 :                 return r;
     534             : 
     535           3 :         *_optlen = optlen;
     536           3 :         *_optoffset = optoffset;
     537           3 :         *ret = packet;
     538           3 :         packet = NULL;
     539             : 
     540           3 :         return 0;
     541             : }
     542             : 
     543           3 : static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
     544             :                                 size_t len) {
     545           3 :         dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
     546             :                                       INADDR_BROADCAST, DHCP_PORT_SERVER, len);
     547             : 
     548           3 :         return dhcp_network_send_raw_socket(client->fd, &client->link,
     549             :                                             packet, len);
     550             : }
     551             : 
     552           2 : static int client_send_discover(sd_dhcp_client *client) {
     553           4 :         _cleanup_free_ DHCPPacket *discover = NULL;
     554             :         size_t optoffset, optlen;
     555             :         int r;
     556             : 
     557           2 :         assert(client);
     558           2 :         assert(client->state == DHCP_STATE_INIT ||
     559             :                client->state == DHCP_STATE_SELECTING);
     560             : 
     561           2 :         r = client_message_init(client, &discover, DHCP_DISCOVER,
     562             :                                 &optlen, &optoffset);
     563           2 :         if (r < 0)
     564           0 :                 return r;
     565             : 
     566             :         /* the client may suggest values for the network address
     567             :            and lease time in the DHCPDISCOVER message. The client may include
     568             :            the ’requested IP address’ option to suggest that a particular IP
     569             :            address be assigned, and may include the ’IP address lease time’
     570             :            option to suggest the lease time it would like.
     571             :          */
     572           2 :         if (client->last_addr != INADDR_ANY) {
     573           0 :                 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
     574             :                                        DHCP_OPTION_REQUESTED_IP_ADDRESS,
     575           0 :                                        4, &client->last_addr);
     576           0 :                 if (r < 0)
     577           0 :                         return r;
     578             :         }
     579             : 
     580             :         /* it is unclear from RFC 2131 if client should send hostname in
     581             :            DHCPDISCOVER but dhclient does and so we do as well
     582             :         */
     583           2 :         if (client->hostname) {
     584           0 :                 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
     585             :                                        DHCP_OPTION_HOST_NAME,
     586           0 :                                        strlen(client->hostname), client->hostname);
     587           0 :                 if (r < 0)
     588           0 :                         return r;
     589             :         }
     590             : 
     591           2 :         if (client->vendor_class_identifier) {
     592           0 :                 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
     593             :                                        DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
     594           0 :                                        strlen(client->vendor_class_identifier),
     595           0 :                                        client->vendor_class_identifier);
     596           0 :                 if (r < 0)
     597           0 :                         return r;
     598             :         }
     599             : 
     600           2 :         r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
     601             :                                DHCP_OPTION_END, 0, NULL);
     602           2 :         if (r < 0)
     603           0 :                 return r;
     604             : 
     605             :         /* We currently ignore:
     606             :            The client SHOULD wait a random time between one and ten seconds to
     607             :            desynchronize the use of DHCP at startup.
     608             :          */
     609           2 :         r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
     610           2 :         if (r < 0)
     611           0 :                 return r;
     612             : 
     613           2 :         log_dhcp_client(client, "DISCOVER");
     614             : 
     615           2 :         return 0;
     616             : }
     617             : 
     618           1 : static int client_send_request(sd_dhcp_client *client) {
     619           2 :         _cleanup_free_ DHCPPacket *request = NULL;
     620             :         size_t optoffset, optlen;
     621             :         int r;
     622             : 
     623           1 :         r = client_message_init(client, &request, DHCP_REQUEST,
     624             :                                 &optlen, &optoffset);
     625           1 :         if (r < 0)
     626           0 :                 return r;
     627             : 
     628           1 :         switch (client->state) {
     629             :         /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
     630             :            SELECTING should be REQUESTING)
     631             :          */
     632             : 
     633             :         case DHCP_STATE_REQUESTING:
     634             :                 /* Client inserts the address of the selected server in ’server
     635             :                    identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
     636             :                    filled in with the yiaddr value from the chosen DHCPOFFER.
     637             :                  */
     638             : 
     639           1 :                 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
     640             :                                        DHCP_OPTION_SERVER_IDENTIFIER,
     641           1 :                                        4, &client->lease->server_address);
     642           1 :                 if (r < 0)
     643           0 :                         return r;
     644             : 
     645           1 :                 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
     646             :                                        DHCP_OPTION_REQUESTED_IP_ADDRESS,
     647           1 :                                        4, &client->lease->address);
     648           1 :                 if (r < 0)
     649           0 :                         return r;
     650             : 
     651           1 :                 break;
     652             : 
     653             :         case DHCP_STATE_INIT_REBOOT:
     654             :                 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
     655             :                    option MUST be filled in with client’s notion of its previously
     656             :                    assigned address. ’ciaddr’ MUST be zero.
     657             :                  */
     658           0 :                 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
     659             :                                        DHCP_OPTION_REQUESTED_IP_ADDRESS,
     660           0 :                                        4, &client->last_addr);
     661           0 :                 if (r < 0)
     662           0 :                         return r;
     663           0 :                 break;
     664             : 
     665             :         case DHCP_STATE_RENEWING:
     666             :                 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
     667             :                    option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
     668             :                    client’s IP address.
     669             :                 */
     670             : 
     671             :                 /* fall through */
     672             :         case DHCP_STATE_REBINDING:
     673             :                 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
     674             :                    option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
     675             :                    client’s IP address.
     676             : 
     677             :                    This message MUST be broadcast to the 0xffffffff IP broadcast address.
     678             :                  */
     679           0 :                 request->dhcp.ciaddr = client->lease->address;
     680             : 
     681           0 :                 break;
     682             : 
     683             :         case DHCP_STATE_INIT:
     684             :         case DHCP_STATE_SELECTING:
     685             :         case DHCP_STATE_REBOOTING:
     686             :         case DHCP_STATE_BOUND:
     687             :         case DHCP_STATE_STOPPED:
     688           0 :                 return -EINVAL;
     689             :         }
     690             : 
     691           1 :         if (client->hostname) {
     692           0 :                 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
     693             :                                        DHCP_OPTION_HOST_NAME,
     694           0 :                                        strlen(client->hostname), client->hostname);
     695           0 :                 if (r < 0)
     696           0 :                         return r;
     697             :         }
     698             : 
     699           1 :         r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
     700             :                                DHCP_OPTION_END, 0, NULL);
     701           1 :         if (r < 0)
     702           0 :                 return r;
     703             : 
     704           1 :         if (client->state == DHCP_STATE_RENEWING) {
     705           0 :                 r = dhcp_network_send_udp_socket(client->fd,
     706           0 :                                                  client->lease->server_address,
     707             :                                                  DHCP_PORT_SERVER,
     708           0 :                                                  &request->dhcp,
     709             :                                                  sizeof(DHCPMessage) + optoffset);
     710             :         } else {
     711           1 :                 r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
     712             :         }
     713           1 :         if (r < 0)
     714           0 :                 return r;
     715             : 
     716           1 :         switch (client->state) {
     717             :         case DHCP_STATE_REQUESTING:
     718           1 :                 log_dhcp_client(client, "REQUEST (requesting)");
     719           1 :                 break;
     720             :         case DHCP_STATE_INIT_REBOOT:
     721           0 :                 log_dhcp_client(client, "REQUEST (init-reboot)");
     722           0 :                 break;
     723             :         case DHCP_STATE_RENEWING:
     724           0 :                 log_dhcp_client(client, "REQUEST (renewing)");
     725           0 :                 break;
     726             :         case DHCP_STATE_REBINDING:
     727           0 :                 log_dhcp_client(client, "REQUEST (rebinding)");
     728           0 :                 break;
     729             :         default:
     730           0 :                 log_dhcp_client(client, "REQUEST (invalid)");
     731           0 :                 break;
     732             :         }
     733             : 
     734           1 :         return 0;
     735             : }
     736             : 
     737             : static int client_start(sd_dhcp_client *client);
     738             : 
     739           3 : static int client_timeout_resend(sd_event_source *s, uint64_t usec,
     740             :                                  void *userdata) {
     741           3 :         sd_dhcp_client *client = userdata;
     742           6 :         DHCP_CLIENT_DONT_DESTROY(client);
     743           3 :         usec_t next_timeout = 0;
     744             :         uint64_t time_now;
     745             :         uint32_t time_left;
     746             :         int r;
     747             : 
     748           3 :         assert(s);
     749           3 :         assert(client);
     750           3 :         assert(client->event);
     751             : 
     752           3 :         r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
     753           3 :         if (r < 0)
     754           0 :                 goto error;
     755             : 
     756           3 :         switch (client->state) {
     757             :         case DHCP_STATE_RENEWING:
     758             : 
     759           0 :                 time_left = (client->lease->t2 - client->lease->t1) / 2;
     760           0 :                 if (time_left < 60)
     761           0 :                         time_left = 60;
     762             : 
     763           0 :                 next_timeout = time_now + time_left * USEC_PER_SEC;
     764             : 
     765           0 :                 break;
     766             : 
     767             :         case DHCP_STATE_REBINDING:
     768             : 
     769           0 :                 time_left = (client->lease->lifetime - client->lease->t2) / 2;
     770           0 :                 if (time_left < 60)
     771           0 :                         time_left = 60;
     772             : 
     773           0 :                 next_timeout = time_now + time_left * USEC_PER_SEC;
     774           0 :                 break;
     775             : 
     776             :         case DHCP_STATE_REBOOTING:
     777             :                 /* start over as we did not receive a timely ack or nak */
     778           0 :                 r = client_initialize(client);
     779           0 :                 if (r < 0)
     780           0 :                         goto error;
     781             : 
     782           0 :                 r = client_start(client);
     783           0 :                 if (r < 0)
     784           0 :                         goto error;
     785             :                 else {
     786           0 :                         log_dhcp_client(client, "REBOOTED");
     787           0 :                         return 0;
     788             :                 }
     789             : 
     790             :         case DHCP_STATE_INIT:
     791             :         case DHCP_STATE_INIT_REBOOT:
     792             :         case DHCP_STATE_SELECTING:
     793             :         case DHCP_STATE_REQUESTING:
     794             :         case DHCP_STATE_BOUND:
     795             : 
     796           3 :                 if (client->attempt < 64)
     797           3 :                         client->attempt *= 2;
     798             : 
     799           3 :                 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
     800             : 
     801           3 :                 break;
     802             : 
     803             :         case DHCP_STATE_STOPPED:
     804           0 :                 r = -EINVAL;
     805           0 :                 goto error;
     806             :         }
     807             : 
     808           3 :         next_timeout += (random_u32() & 0x1fffff);
     809             : 
     810           3 :         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
     811             : 
     812           3 :         r = sd_event_add_time(client->event,
     813             :                               &client->timeout_resend,
     814             :                               clock_boottime_or_monotonic(),
     815             :                               next_timeout, 10 * USEC_PER_MSEC,
     816             :                               client_timeout_resend, client);
     817           3 :         if (r < 0)
     818           0 :                 goto error;
     819             : 
     820           3 :         r = sd_event_source_set_priority(client->timeout_resend,
     821           3 :                                          client->event_priority);
     822           3 :         if (r < 0)
     823           0 :                 goto error;
     824             : 
     825           3 :         r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
     826           3 :         if (r < 0)
     827           0 :                 goto error;
     828             : 
     829           3 :         switch (client->state) {
     830             :         case DHCP_STATE_INIT:
     831           2 :                 r = client_send_discover(client);
     832           2 :                 if (r >= 0) {
     833           2 :                         client->state = DHCP_STATE_SELECTING;
     834           2 :                         client->attempt = 1;
     835             :                 } else {
     836           0 :                         if (client->attempt >= 64)
     837           0 :                                 goto error;
     838             :                 }
     839             : 
     840           2 :                 break;
     841             : 
     842             :         case DHCP_STATE_SELECTING:
     843           0 :                 r = client_send_discover(client);
     844           0 :                 if (r < 0 && client->attempt >= 64)
     845           0 :                         goto error;
     846             : 
     847           0 :                 break;
     848             : 
     849             :         case DHCP_STATE_INIT_REBOOT:
     850             :         case DHCP_STATE_REQUESTING:
     851             :         case DHCP_STATE_RENEWING:
     852             :         case DHCP_STATE_REBINDING:
     853           1 :                 r = client_send_request(client);
     854           1 :                 if (r < 0 && client->attempt >= 64)
     855           0 :                          goto error;
     856             : 
     857           1 :                 if (client->state == DHCP_STATE_INIT_REBOOT)
     858           0 :                         client->state = DHCP_STATE_REBOOTING;
     859             : 
     860           1 :                 client->request_sent = time_now;
     861             : 
     862           1 :                 break;
     863             : 
     864             :         case DHCP_STATE_REBOOTING:
     865             :         case DHCP_STATE_BOUND:
     866             : 
     867           0 :                 break;
     868             : 
     869             :         case DHCP_STATE_STOPPED:
     870           0 :                 r = -EINVAL;
     871           0 :                 goto error;
     872             :         }
     873             : 
     874           3 :         return 0;
     875             : 
     876             : error:
     877           0 :         client_stop(client, r);
     878             : 
     879             :         /* Errors were dealt with when stopping the client, don't spill
     880             :            errors into the event loop handler */
     881           0 :         return 0;
     882             : }
     883             : 
     884           3 : static int client_initialize_io_events(sd_dhcp_client *client,
     885             :                                        sd_event_io_handler_t io_callback) {
     886             :         int r;
     887             : 
     888           3 :         assert(client);
     889           3 :         assert(client->event);
     890             : 
     891           3 :         r = sd_event_add_io(client->event, &client->receive_message,
     892             :                             client->fd, EPOLLIN, io_callback,
     893             :                             client);
     894           3 :         if (r < 0)
     895           0 :                 goto error;
     896             : 
     897           3 :         r = sd_event_source_set_priority(client->receive_message,
     898           3 :                                          client->event_priority);
     899           3 :         if (r < 0)
     900           0 :                 goto error;
     901             : 
     902           3 :         r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
     903           3 :         if (r < 0)
     904           0 :                 goto error;
     905             : 
     906             : error:
     907           3 :         if (r < 0)
     908           0 :                 client_stop(client, r);
     909             : 
     910           3 :         return 0;
     911             : }
     912             : 
     913           2 : static int client_initialize_time_events(sd_dhcp_client *client) {
     914             :         int r;
     915             : 
     916           2 :         assert(client);
     917           2 :         assert(client->event);
     918             : 
     919           2 :         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
     920             : 
     921           2 :         r = sd_event_add_time(client->event,
     922             :                               &client->timeout_resend,
     923             :                               clock_boottime_or_monotonic(),
     924             :                               0, 0,
     925             :                               client_timeout_resend, client);
     926           2 :         if (r < 0)
     927           0 :                 goto error;
     928             : 
     929           2 :         r = sd_event_source_set_priority(client->timeout_resend,
     930           2 :                                          client->event_priority);
     931           2 :         if (r < 0)
     932           0 :                 goto error;
     933             : 
     934           2 :         r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
     935           2 :         if (r < 0)
     936           0 :                 goto error;
     937             : 
     938             : error:
     939           2 :         if (r < 0)
     940           0 :                 client_stop(client, r);
     941             : 
     942           2 :         return 0;
     943             : 
     944             : }
     945             : 
     946           2 : static int client_initialize_events(sd_dhcp_client *client,
     947             :                                     sd_event_io_handler_t io_callback) {
     948           2 :         client_initialize_io_events(client, io_callback);
     949           2 :         client_initialize_time_events(client);
     950             : 
     951           2 :         return 0;
     952             : }
     953             : 
     954           2 : static int client_start(sd_dhcp_client *client) {
     955             :         int r;
     956             : 
     957           2 :         assert_return(client, -EINVAL);
     958           2 :         assert_return(client->event, -EINVAL);
     959           2 :         assert_return(client->index > 0, -EINVAL);
     960           2 :         assert_return(client->fd < 0, -EBUSY);
     961           2 :         assert_return(client->xid == 0, -EINVAL);
     962           2 :         assert_return(client->state == DHCP_STATE_INIT ||
     963             :                       client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
     964             : 
     965           2 :         client->xid = random_u32();
     966             : 
     967           4 :         r = dhcp_network_bind_raw_socket(client->index, &client->link,
     968           2 :                                          client->xid, client->mac_addr,
     969           2 :                                          client->mac_addr_len, client->arp_type);
     970           2 :         if (r < 0) {
     971           0 :                 client_stop(client, r);
     972           0 :                 return r;
     973             :         }
     974           2 :         client->fd = r;
     975             : 
     976           2 :         if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT)
     977           2 :                 client->start_time = now(clock_boottime_or_monotonic());
     978             : 
     979           2 :         return client_initialize_events(client, client_receive_message_raw);
     980             : }
     981             : 
     982           0 : static int client_timeout_expire(sd_event_source *s, uint64_t usec,
     983             :                                  void *userdata) {
     984           0 :         sd_dhcp_client *client = userdata;
     985           0 :         DHCP_CLIENT_DONT_DESTROY(client);
     986             : 
     987           0 :         log_dhcp_client(client, "EXPIRED");
     988             : 
     989           0 :         client_notify(client, DHCP_EVENT_EXPIRED);
     990             : 
     991             :         /* lease was lost, start over if not freed or stopped in callback */
     992           0 :         if (client->state != DHCP_STATE_STOPPED) {
     993           0 :                 client_initialize(client);
     994           0 :                 client_start(client);
     995             :         }
     996             : 
     997           0 :         return 0;
     998             : }
     999             : 
    1000           0 : static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
    1001           0 :         sd_dhcp_client *client = userdata;
    1002           0 :         DHCP_CLIENT_DONT_DESTROY(client);
    1003             :         int r;
    1004             : 
    1005           0 :         client->receive_message = sd_event_source_unref(client->receive_message);
    1006           0 :         client->fd = asynchronous_close(client->fd);
    1007             : 
    1008           0 :         client->state = DHCP_STATE_REBINDING;
    1009           0 :         client->attempt = 1;
    1010             : 
    1011           0 :         r = dhcp_network_bind_raw_socket(client->index, &client->link,
    1012           0 :                                          client->xid, client->mac_addr,
    1013           0 :                                          client->mac_addr_len, client->arp_type);
    1014           0 :         if (r < 0) {
    1015           0 :                 client_stop(client, r);
    1016           0 :                 return 0;
    1017             :         }
    1018           0 :         client->fd = r;
    1019             : 
    1020           0 :         return client_initialize_events(client, client_receive_message_raw);
    1021             : }
    1022             : 
    1023           0 : static int client_timeout_t1(sd_event_source *s, uint64_t usec,
    1024             :                              void *userdata) {
    1025           0 :         sd_dhcp_client *client = userdata;
    1026           0 :         DHCP_CLIENT_DONT_DESTROY(client);
    1027             : 
    1028           0 :         client->state = DHCP_STATE_RENEWING;
    1029           0 :         client->attempt = 1;
    1030             : 
    1031           0 :         return client_initialize_time_events(client);
    1032             : }
    1033             : 
    1034           1 : static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
    1035             :                                size_t len) {
    1036           2 :         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
    1037             :         int r;
    1038             : 
    1039           1 :         r = dhcp_lease_new(&lease);
    1040           1 :         if (r < 0)
    1041           0 :                 return r;
    1042             : 
    1043           1 :         if (client->client_id_len) {
    1044           2 :                 r = dhcp_lease_set_client_id(lease,
    1045           1 :                                              (uint8_t *) &client->client_id,
    1046             :                                              client->client_id_len);
    1047           1 :                 if (r < 0)
    1048           0 :                         return r;
    1049             :         }
    1050             : 
    1051           1 :         r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
    1052           1 :         if (r != DHCP_OFFER) {
    1053           0 :                 log_dhcp_client(client, "received message was not an OFFER, ignoring");
    1054           0 :                 return -ENOMSG;
    1055             :         }
    1056             : 
    1057           1 :         lease->next_server = offer->siaddr;
    1058             : 
    1059           1 :         lease->address = offer->yiaddr;
    1060             : 
    1061           2 :         if (lease->address == INADDR_ANY ||
    1062           2 :             lease->server_address == INADDR_ANY ||
    1063           1 :             lease->lifetime == 0) {
    1064           0 :                 log_dhcp_client(client, "received lease lacks address, server "
    1065             :                                 "address or lease lifetime, ignoring");
    1066           0 :                 return -ENOMSG;
    1067             :         }
    1068             : 
    1069           1 :         if (lease->subnet_mask == INADDR_ANY) {
    1070           0 :                 r = dhcp_lease_set_default_subnet_mask(lease);
    1071           0 :                 if (r < 0) {
    1072           0 :                         log_dhcp_client(client, "received lease lacks subnet "
    1073             :                                         "mask, and a fallback one can not be "
    1074             :                                         "generated, ignoring");
    1075           0 :                         return -ENOMSG;
    1076             :                 }
    1077             :         }
    1078             : 
    1079           1 :         sd_dhcp_lease_unref(client->lease);
    1080           1 :         client->lease = lease;
    1081           1 :         lease = NULL;
    1082             : 
    1083           1 :         log_dhcp_client(client, "OFFER");
    1084             : 
    1085           1 :         return 0;
    1086             : }
    1087             : 
    1088           0 : static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
    1089             :                                     size_t len) {
    1090             :         int r;
    1091             : 
    1092           0 :         r = dhcp_option_parse(force, len, NULL, NULL);
    1093           0 :         if (r != DHCP_FORCERENEW)
    1094           0 :                 return -ENOMSG;
    1095             : 
    1096           0 :         log_dhcp_client(client, "FORCERENEW");
    1097             : 
    1098           0 :         return 0;
    1099             : }
    1100             : 
    1101           1 : static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
    1102             :                              size_t len) {
    1103           2 :         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
    1104             :         int r;
    1105             : 
    1106           1 :         r = dhcp_lease_new(&lease);
    1107           1 :         if (r < 0)
    1108           0 :                 return r;
    1109             : 
    1110           1 :         if (client->client_id_len) {
    1111           2 :                 r = dhcp_lease_set_client_id(lease,
    1112           1 :                                              (uint8_t *) &client->client_id,
    1113             :                                              client->client_id_len);
    1114           1 :                 if (r < 0)
    1115           0 :                         return r;
    1116             :         }
    1117             : 
    1118           1 :         r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
    1119           1 :         if (r == DHCP_NAK) {
    1120           0 :                 log_dhcp_client(client, "NAK");
    1121           0 :                 return -EADDRNOTAVAIL;
    1122             :         }
    1123             : 
    1124           1 :         if (r != DHCP_ACK) {
    1125           0 :                 log_dhcp_client(client, "received message was not an ACK, ignoring");
    1126           0 :                 return -ENOMSG;
    1127             :         }
    1128             : 
    1129           1 :         lease->next_server = ack->siaddr;
    1130             : 
    1131           1 :         lease->address = ack->yiaddr;
    1132             : 
    1133           2 :         if (lease->address == INADDR_ANY ||
    1134           2 :             lease->server_address == INADDR_ANY ||
    1135           1 :             lease->lifetime == 0) {
    1136           0 :                 log_dhcp_client(client, "received lease lacks address, server "
    1137             :                                 "address or lease lifetime, ignoring");
    1138           0 :                 return -ENOMSG;
    1139             :         }
    1140             : 
    1141           1 :         if (lease->subnet_mask == INADDR_ANY) {
    1142           0 :                 r = dhcp_lease_set_default_subnet_mask(lease);
    1143           0 :                 if (r < 0) {
    1144           0 :                         log_dhcp_client(client, "received lease lacks subnet "
    1145             :                                         "mask, and a fallback one can not be "
    1146             :                                         "generated, ignoring");
    1147           0 :                         return -ENOMSG;
    1148             :                 }
    1149             :         }
    1150             : 
    1151           1 :         r = DHCP_EVENT_IP_ACQUIRE;
    1152           1 :         if (client->lease) {
    1153           2 :                 if (client->lease->address != lease->address ||
    1154           2 :                     client->lease->subnet_mask != lease->subnet_mask ||
    1155           1 :                     client->lease->router != lease->router) {
    1156           0 :                         r = DHCP_EVENT_IP_CHANGE;
    1157             :                 } else
    1158           1 :                         r = DHCP_EVENT_RENEW;
    1159             : 
    1160           1 :                 client->lease = sd_dhcp_lease_unref(client->lease);
    1161             :         }
    1162             : 
    1163           1 :         client->lease = lease;
    1164           1 :         lease = NULL;
    1165             : 
    1166           1 :         log_dhcp_client(client, "ACK");
    1167             : 
    1168           1 :         return r;
    1169             : }
    1170             : 
    1171           3 : static uint64_t client_compute_timeout(sd_dhcp_client *client,
    1172             :                                        uint32_t lifetime, double factor) {
    1173           3 :         assert(client);
    1174           3 :         assert(client->request_sent);
    1175           3 :         assert(lifetime);
    1176             : 
    1177           6 :         return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
    1178           3 :                 + (random_u32() & 0x1fffff);
    1179             : }
    1180             : 
    1181           1 : static int client_set_lease_timeouts(sd_dhcp_client *client) {
    1182             :         usec_t time_now;
    1183             :         uint64_t lifetime_timeout;
    1184             :         uint64_t t2_timeout;
    1185             :         uint64_t t1_timeout;
    1186             :         char time_string[FORMAT_TIMESPAN_MAX];
    1187             :         int r;
    1188             : 
    1189           1 :         assert(client);
    1190           1 :         assert(client->event);
    1191           1 :         assert(client->lease);
    1192           1 :         assert(client->lease->lifetime);
    1193             : 
    1194           1 :         client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
    1195           1 :         client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
    1196           1 :         client->timeout_expire = sd_event_source_unref(client->timeout_expire);
    1197             : 
    1198             :         /* don't set timers for infinite leases */
    1199           1 :         if (client->lease->lifetime == 0xffffffff)
    1200           0 :                 return 0;
    1201             : 
    1202           1 :         r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
    1203           1 :         if (r < 0)
    1204           0 :                 return r;
    1205           1 :         assert(client->request_sent <= time_now);
    1206             : 
    1207             :         /* convert the various timeouts from relative (secs) to absolute (usecs) */
    1208           1 :         lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
    1209           1 :         if (client->lease->t1 && client->lease->t2) {
    1210             :                 /* both T1 and T2 are given */
    1211           0 :                 if (client->lease->t1 < client->lease->t2 &&
    1212           0 :                     client->lease->t2 < client->lease->lifetime) {
    1213             :                         /* they are both valid */
    1214           0 :                         t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
    1215           0 :                         t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
    1216             :                 } else {
    1217             :                         /* discard both */
    1218           0 :                         t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
    1219           0 :                         client->lease->t2 = (client->lease->lifetime * 7) / 8;
    1220           0 :                         t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
    1221           0 :                         client->lease->t1 = client->lease->lifetime / 2;
    1222             :                 }
    1223           1 :         } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
    1224             :                 /* only T2 is given, and it is valid */
    1225           0 :                 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
    1226           0 :                 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
    1227           0 :                 client->lease->t1 = client->lease->lifetime / 2;
    1228           0 :                 if (t2_timeout <= t1_timeout) {
    1229             :                         /* the computed T1 would be invalid, so discard T2 */
    1230           0 :                         t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
    1231           0 :                         client->lease->t2 = (client->lease->lifetime * 7) / 8;
    1232             :                 }
    1233           1 :         } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
    1234             :                 /* only T1 is given, and it is valid */
    1235           0 :                 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
    1236           0 :                 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
    1237           0 :                 client->lease->t2 = (client->lease->lifetime * 7) / 8;
    1238           0 :                 if (t2_timeout <= t1_timeout) {
    1239             :                         /* the computed T2 would be invalid, so discard T1 */
    1240           0 :                         t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
    1241           0 :                         client->lease->t2 = client->lease->lifetime / 2;
    1242             :                 }
    1243             :         } else {
    1244             :                 /* fall back to the default timeouts */
    1245           1 :                 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
    1246           1 :                 client->lease->t1 = client->lease->lifetime / 2;
    1247           1 :                 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
    1248           1 :                 client->lease->t2 = (client->lease->lifetime * 7) / 8;
    1249             :         }
    1250             : 
    1251             :         /* arm lifetime timeout */
    1252           1 :         r = sd_event_add_time(client->event, &client->timeout_expire,
    1253             :                               clock_boottime_or_monotonic(),
    1254             :                               lifetime_timeout, 10 * USEC_PER_MSEC,
    1255             :                               client_timeout_expire, client);
    1256           1 :         if (r < 0)
    1257           0 :                 return r;
    1258             : 
    1259           1 :         r = sd_event_source_set_priority(client->timeout_expire,
    1260           1 :                                          client->event_priority);
    1261           1 :         if (r < 0)
    1262           0 :                 return r;
    1263             : 
    1264           1 :         r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime");
    1265           1 :         if (r < 0)
    1266           0 :                 return r;
    1267             : 
    1268           1 :         log_dhcp_client(client, "lease expires in %s",
    1269             :                         format_timespan(time_string, FORMAT_TIMESPAN_MAX,
    1270             :                         lifetime_timeout - time_now, 0));
    1271             : 
    1272             :         /* don't arm earlier timeouts if this has already expired */
    1273           1 :         if (lifetime_timeout <= time_now)
    1274           0 :                 return 0;
    1275             : 
    1276             :         /* arm T2 timeout */
    1277           1 :         r = sd_event_add_time(client->event,
    1278             :                               &client->timeout_t2,
    1279             :                               clock_boottime_or_monotonic(),
    1280             :                               t2_timeout,
    1281             :                               10 * USEC_PER_MSEC,
    1282             :                               client_timeout_t2, client);
    1283           1 :         if (r < 0)
    1284           0 :                 return r;
    1285             : 
    1286           1 :         r = sd_event_source_set_priority(client->timeout_t2,
    1287           1 :                                          client->event_priority);
    1288           1 :         if (r < 0)
    1289           0 :                 return r;
    1290             : 
    1291           1 :         r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout");
    1292           1 :         if (r < 0)
    1293           0 :                 return r;
    1294             : 
    1295           1 :         log_dhcp_client(client, "T2 expires in %s",
    1296             :                         format_timespan(time_string, FORMAT_TIMESPAN_MAX,
    1297             :                         t2_timeout - time_now, 0));
    1298             : 
    1299             :         /* don't arm earlier timeout if this has already expired */
    1300           1 :         if (t2_timeout <= time_now)
    1301           0 :                 return 0;
    1302             : 
    1303             :         /* arm T1 timeout */
    1304           1 :         r = sd_event_add_time(client->event,
    1305             :                               &client->timeout_t1,
    1306             :                               clock_boottime_or_monotonic(),
    1307             :                               t1_timeout, 10 * USEC_PER_MSEC,
    1308             :                               client_timeout_t1, client);
    1309           1 :         if (r < 0)
    1310           0 :                 return r;
    1311             : 
    1312           1 :         r = sd_event_source_set_priority(client->timeout_t1,
    1313           1 :                                          client->event_priority);
    1314           1 :         if (r < 0)
    1315           0 :                 return r;
    1316             : 
    1317           1 :         r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer");
    1318           1 :         if (r < 0)
    1319           0 :                 return r;
    1320             : 
    1321           1 :         log_dhcp_client(client, "T1 expires in %s",
    1322             :                         format_timespan(time_string, FORMAT_TIMESPAN_MAX,
    1323             :                         t1_timeout - time_now, 0));
    1324             : 
    1325           1 :         return 0;
    1326             : }
    1327             : 
    1328           2 : static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
    1329             :                                  int len) {
    1330           4 :         DHCP_CLIENT_DONT_DESTROY(client);
    1331           2 :         int r = 0, notify_event = 0;
    1332             : 
    1333           2 :         assert(client);
    1334           2 :         assert(client->event);
    1335           2 :         assert(message);
    1336             : 
    1337           2 :         switch (client->state) {
    1338             :         case DHCP_STATE_SELECTING:
    1339             : 
    1340           1 :                 r = client_handle_offer(client, message, len);
    1341           1 :                 if (r >= 0) {
    1342             : 
    1343           1 :                         client->timeout_resend =
    1344           1 :                                 sd_event_source_unref(client->timeout_resend);
    1345             : 
    1346           1 :                         client->state = DHCP_STATE_REQUESTING;
    1347           1 :                         client->attempt = 1;
    1348             : 
    1349           1 :                         r = sd_event_add_time(client->event,
    1350             :                                               &client->timeout_resend,
    1351             :                                               clock_boottime_or_monotonic(),
    1352             :                                               0, 0,
    1353             :                                               client_timeout_resend, client);
    1354           1 :                         if (r < 0)
    1355           0 :                                 goto error;
    1356             : 
    1357           1 :                         r = sd_event_source_set_priority(client->timeout_resend,
    1358           1 :                                                          client->event_priority);
    1359           1 :                         if (r < 0)
    1360           0 :                                 goto error;
    1361             : 
    1362           1 :                         r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
    1363           1 :                         if (r < 0)
    1364           0 :                                 goto error;
    1365           0 :                 } else if (r == -ENOMSG)
    1366             :                         /* invalid message, let's ignore it */
    1367           0 :                         return 0;
    1368             : 
    1369           1 :                 break;
    1370             : 
    1371             :         case DHCP_STATE_REBOOTING:
    1372             :         case DHCP_STATE_REQUESTING:
    1373             :         case DHCP_STATE_RENEWING:
    1374             :         case DHCP_STATE_REBINDING:
    1375             : 
    1376           1 :                 r = client_handle_ack(client, message, len);
    1377           1 :                 if (r >= 0) {
    1378           1 :                         client->timeout_resend =
    1379           1 :                                 sd_event_source_unref(client->timeout_resend);
    1380           1 :                         client->receive_message =
    1381           1 :                                 sd_event_source_unref(client->receive_message);
    1382           1 :                         client->fd = asynchronous_close(client->fd);
    1383             : 
    1384           1 :                         if (IN_SET(client->state, DHCP_STATE_REQUESTING,
    1385             :                                    DHCP_STATE_REBOOTING))
    1386           1 :                                 notify_event = DHCP_EVENT_IP_ACQUIRE;
    1387           0 :                         else if (r != DHCP_EVENT_IP_ACQUIRE)
    1388           0 :                                 notify_event = r;
    1389             : 
    1390           1 :                         client->state = DHCP_STATE_BOUND;
    1391           1 :                         client->attempt = 1;
    1392             : 
    1393           1 :                         client->last_addr = client->lease->address;
    1394             : 
    1395           1 :                         r = client_set_lease_timeouts(client);
    1396           1 :                         if (r < 0) {
    1397           0 :                                 log_dhcp_client(client, "could not set lease timeouts");
    1398           0 :                                 goto error;
    1399             :                         }
    1400             : 
    1401           1 :                         r = dhcp_network_bind_udp_socket(client->lease->address,
    1402             :                                                          DHCP_PORT_CLIENT);
    1403           1 :                         if (r < 0) {
    1404           0 :                                 log_dhcp_client(client, "could not bind UDP socket");
    1405           0 :                                 goto error;
    1406             :                         }
    1407             : 
    1408           1 :                         client->fd = r;
    1409             : 
    1410           1 :                         client_initialize_io_events(client, client_receive_message_udp);
    1411             : 
    1412           1 :                         if (notify_event) {
    1413           1 :                                 client_notify(client, notify_event);
    1414           1 :                                 if (client->state == DHCP_STATE_STOPPED)
    1415           0 :                                         return 0;
    1416             :                         }
    1417             : 
    1418           0 :                 } else if (r == -EADDRNOTAVAIL) {
    1419             :                         /* got a NAK, let's restart the client */
    1420           0 :                         client->timeout_resend =
    1421           0 :                                 sd_event_source_unref(client->timeout_resend);
    1422             : 
    1423           0 :                         r = client_initialize(client);
    1424           0 :                         if (r < 0)
    1425           0 :                                 goto error;
    1426             : 
    1427           0 :                         r = client_start(client);
    1428           0 :                         if (r < 0)
    1429           0 :                                 goto error;
    1430             : 
    1431           0 :                         log_dhcp_client(client, "REBOOTED");
    1432             : 
    1433           0 :                         return 0;
    1434           0 :                 } else if (r == -ENOMSG)
    1435             :                         /* invalid message, let's ignore it */
    1436           0 :                         return 0;
    1437             : 
    1438           1 :                 break;
    1439             : 
    1440             :         case DHCP_STATE_BOUND:
    1441           0 :                 r = client_handle_forcerenew(client, message, len);
    1442           0 :                 if (r >= 0) {
    1443           0 :                         r = client_timeout_t1(NULL, 0, client);
    1444           0 :                         if (r < 0)
    1445           0 :                                 goto error;
    1446           0 :                 } else if (r == -ENOMSG)
    1447             :                         /* invalid message, let's ignore it */
    1448           0 :                         return 0;
    1449             : 
    1450           0 :                 break;
    1451             : 
    1452             :         case DHCP_STATE_INIT:
    1453             :         case DHCP_STATE_INIT_REBOOT:
    1454             : 
    1455           0 :                 break;
    1456             : 
    1457             :         case DHCP_STATE_STOPPED:
    1458           0 :                 r = -EINVAL;
    1459           0 :                 goto error;
    1460             :         }
    1461             : 
    1462             : error:
    1463           2 :         if (r < 0)
    1464           0 :                 client_stop(client, r);
    1465             : 
    1466           2 :         return r;
    1467             : }
    1468             : 
    1469           0 : static int client_receive_message_udp(sd_event_source *s, int fd,
    1470             :                                       uint32_t revents, void *userdata) {
    1471           0 :         sd_dhcp_client *client = userdata;
    1472           0 :         _cleanup_free_ DHCPMessage *message = NULL;
    1473           0 :         int buflen = 0, len, r;
    1474           0 :         const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
    1475           0 :         const struct ether_addr *expected_chaddr = NULL;
    1476           0 :         uint8_t expected_hlen = 0;
    1477             : 
    1478           0 :         assert(s);
    1479           0 :         assert(client);
    1480             : 
    1481           0 :         r = ioctl(fd, FIONREAD, &buflen);
    1482           0 :         if (r < 0)
    1483           0 :                 return r;
    1484             : 
    1485           0 :         if (buflen < 0)
    1486             :                 /* this can't be right */
    1487           0 :                 return -EIO;
    1488             : 
    1489           0 :         message = malloc0(buflen);
    1490           0 :         if (!message)
    1491           0 :                 return -ENOMEM;
    1492             : 
    1493           0 :         len = read(fd, message, buflen);
    1494           0 :         if (len < 0) {
    1495           0 :                 log_dhcp_client(client, "could not receive message from UDP "
    1496             :                                 "socket: %m");
    1497           0 :                 return 0;
    1498           0 :         } else if ((size_t)len < sizeof(DHCPMessage)) {
    1499           0 :                 log_dhcp_client(client, "too small to be a DHCP message: ignoring");
    1500           0 :                 return 0;
    1501             :         }
    1502             : 
    1503           0 :         if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
    1504           0 :                 log_dhcp_client(client, "not a DHCP message: ignoring");
    1505           0 :                 return 0;
    1506             :         }
    1507             : 
    1508           0 :         if (message->op != BOOTREPLY) {
    1509           0 :                 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
    1510           0 :                 return 0;
    1511             :         }
    1512             : 
    1513           0 :         if (message->htype != client->arp_type) {
    1514           0 :                 log_dhcp_client(client, "packet type does not match client type");
    1515           0 :                 return 0;
    1516             :         }
    1517             : 
    1518           0 :         if (client->arp_type == ARPHRD_ETHER) {
    1519           0 :                 expected_hlen = ETH_ALEN;
    1520           0 :                 expected_chaddr = (const struct ether_addr *) &client->mac_addr;
    1521             :         } else {
    1522             :                /* Non-ethernet links expect zero chaddr */
    1523           0 :                expected_hlen = 0;
    1524           0 :                expected_chaddr = &zero_mac;
    1525             :         }
    1526             : 
    1527           0 :         if (message->hlen != expected_hlen) {
    1528           0 :                 log_dhcp_client(client, "unexpected packet hlen %d", message->hlen);
    1529           0 :                 return 0;
    1530             :         }
    1531             : 
    1532           0 :         if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) {
    1533           0 :                 log_dhcp_client(client, "received chaddr does not match "
    1534             :                                 "expected: ignoring");
    1535           0 :                 return 0;
    1536             :         }
    1537             : 
    1538           0 :         if (client->state != DHCP_STATE_BOUND &&
    1539           0 :             be32toh(message->xid) != client->xid) {
    1540             :                 /* in BOUND state, we may receive FORCERENEW with xid set by server,
    1541             :                    so ignore the xid in this case */
    1542           0 :                 log_dhcp_client(client, "received xid (%u) does not match "
    1543             :                                 "expected (%u): ignoring",
    1544             :                                 be32toh(message->xid), client->xid);
    1545           0 :                 return 0;
    1546             :         }
    1547             : 
    1548           0 :         return client_handle_message(client, message, len);
    1549             : }
    1550             : 
    1551           2 : static int client_receive_message_raw(sd_event_source *s, int fd,
    1552             :                                       uint32_t revents, void *userdata) {
    1553           2 :         sd_dhcp_client *client = userdata;
    1554           4 :         _cleanup_free_ DHCPPacket *packet = NULL;
    1555             :         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
    1556           2 :         struct iovec iov = {};
    1557           2 :         struct msghdr msg = {
    1558             :                 .msg_iov = &iov,
    1559             :                 .msg_iovlen = 1,
    1560             :                 .msg_control = cmsgbuf,
    1561             :                 .msg_controllen = sizeof(cmsgbuf),
    1562             :         };
    1563             :         struct cmsghdr *cmsg;
    1564           2 :         bool checksum = true;
    1565           2 :         int buflen = 0, len, r;
    1566             : 
    1567           2 :         assert(s);
    1568           2 :         assert(client);
    1569             : 
    1570           2 :         r = ioctl(fd, FIONREAD, &buflen);
    1571           2 :         if (r < 0)
    1572           0 :                 return r;
    1573             : 
    1574           2 :         if (buflen < 0)
    1575             :                 /* this can't be right */
    1576           0 :                 return -EIO;
    1577             : 
    1578           2 :         packet = malloc0(buflen);
    1579           2 :         if (!packet)
    1580           0 :                 return -ENOMEM;
    1581             : 
    1582           2 :         iov.iov_base = packet;
    1583           2 :         iov.iov_len = buflen;
    1584             : 
    1585           2 :         len = recvmsg(fd, &msg, 0);
    1586           2 :         if (len < 0) {
    1587           0 :                 log_dhcp_client(client, "could not receive message from raw "
    1588             :                                 "socket: %m");
    1589           0 :                 return 0;
    1590           2 :         } else if ((size_t)len < sizeof(DHCPPacket))
    1591           0 :                 return 0;
    1592             : 
    1593           2 :         CMSG_FOREACH(cmsg, &msg) {
    1594           0 :                 if (cmsg->cmsg_level == SOL_PACKET &&
    1595           0 :                     cmsg->cmsg_type == PACKET_AUXDATA &&
    1596           0 :                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
    1597           0 :                         struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
    1598             : 
    1599           0 :                         checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
    1600           0 :                         break;
    1601             :                 }
    1602             :         }
    1603             : 
    1604           2 :         r = dhcp_packet_verify_headers(packet, len, checksum);
    1605           2 :         if (r < 0)
    1606           0 :                 return 0;
    1607             : 
    1608           2 :         len -= DHCP_IP_UDP_SIZE;
    1609             : 
    1610           2 :         return client_handle_message(client, &packet->dhcp, len);
    1611             : }
    1612             : 
    1613           2 : int sd_dhcp_client_start(sd_dhcp_client *client) {
    1614             :         int r;
    1615             : 
    1616           2 :         assert_return(client, -EINVAL);
    1617             : 
    1618           2 :         r = client_initialize(client);
    1619           2 :         if (r < 0)
    1620           0 :                 return r;
    1621             : 
    1622           2 :         if (client->last_addr)
    1623           0 :                 client->state = DHCP_STATE_INIT_REBOOT;
    1624             : 
    1625           2 :         r = client_start(client);
    1626           2 :         if (r >= 0)
    1627           2 :                 log_dhcp_client(client, "STARTED on ifindex %i", client->index);
    1628             : 
    1629           2 :         return r;
    1630             : }
    1631             : 
    1632           2 : int sd_dhcp_client_stop(sd_dhcp_client *client) {
    1633           4 :         DHCP_CLIENT_DONT_DESTROY(client);
    1634             : 
    1635           2 :         assert_return(client, -EINVAL);
    1636             : 
    1637           2 :         client_stop(client, DHCP_EVENT_STOP);
    1638           2 :         client->state = DHCP_STATE_STOPPED;
    1639             : 
    1640           2 :         return 0;
    1641             : }
    1642             : 
    1643           3 : int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
    1644             :                                 int priority) {
    1645             :         int r;
    1646             : 
    1647           3 :         assert_return(client, -EINVAL);
    1648           3 :         assert_return(!client->event, -EBUSY);
    1649             : 
    1650           3 :         if (event)
    1651           3 :                 client->event = sd_event_ref(event);
    1652             :         else {
    1653           0 :                 r = sd_event_default(&client->event);
    1654           0 :                 if (r < 0)
    1655           0 :                         return 0;
    1656             :         }
    1657             : 
    1658           3 :         client->event_priority = priority;
    1659             : 
    1660           3 :         return 0;
    1661             : }
    1662             : 
    1663           3 : int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
    1664           3 :         assert_return(client, -EINVAL);
    1665             : 
    1666           3 :         client->event = sd_event_unref(client->event);
    1667             : 
    1668           3 :         return 0;
    1669             : }
    1670             : 
    1671           0 : sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
    1672           0 :         if (!client)
    1673           0 :                 return NULL;
    1674             : 
    1675           0 :         return client->event;
    1676             : }
    1677             : 
    1678           9 : sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
    1679           9 :         if (client)
    1680           9 :                 assert_se(REFCNT_INC(client->n_ref) >= 2);
    1681             : 
    1682           9 :         return client;
    1683             : }
    1684             : 
    1685          16 : sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
    1686          16 :         if (client && REFCNT_DEC(client->n_ref) == 0) {
    1687           3 :                 log_dhcp_client(client, "FREE");
    1688             : 
    1689           3 :                 client_initialize(client);
    1690             : 
    1691           3 :                 client->receive_message =
    1692           3 :                         sd_event_source_unref(client->receive_message);
    1693             : 
    1694           3 :                 sd_dhcp_client_detach_event(client);
    1695             : 
    1696           3 :                 sd_dhcp_lease_unref(client->lease);
    1697             : 
    1698           3 :                 free(client->req_opts);
    1699           3 :                 free(client->hostname);
    1700           3 :                 free(client->vendor_class_identifier);
    1701           3 :                 free(client);
    1702             :         }
    1703             : 
    1704          16 :         return NULL;
    1705             : }
    1706             : 
    1707           3 : int sd_dhcp_client_new(sd_dhcp_client **ret) {
    1708           6 :         _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
    1709             : 
    1710           3 :         assert_return(ret, -EINVAL);
    1711             : 
    1712           3 :         client = new0(sd_dhcp_client, 1);
    1713           3 :         if (!client)
    1714           0 :                 return -ENOMEM;
    1715             : 
    1716           3 :         client->n_ref = REFCNT_INIT;
    1717           3 :         client->state = DHCP_STATE_INIT;
    1718           3 :         client->index = -1;
    1719           3 :         client->fd = -1;
    1720           3 :         client->attempt = 1;
    1721           3 :         client->mtu = DHCP_DEFAULT_MIN_SIZE;
    1722             : 
    1723           3 :         client->req_opts_size = ELEMENTSOF(default_req_opts);
    1724             : 
    1725           3 :         client->req_opts = memdup(default_req_opts, client->req_opts_size);
    1726           3 :         if (!client->req_opts)
    1727           0 :                 return -ENOMEM;
    1728             : 
    1729           3 :         *ret = client;
    1730           3 :         client = NULL;
    1731             : 
    1732           3 :         return 0;
    1733             : }

Generated by: LCOV version 1.11