LCOV - code coverage report
Current view: top level - libsystemd-network - sd-dhcp-server.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 97 609 15.9 %
Date: 2019-08-23 13:36:53 Functions: 10 38 26.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 75 511 14.7 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : /***
       3                 :            :   Copyright © 2013 Intel Corporation. All rights reserved.
       4                 :            : ***/
       5                 :            : 
       6                 :            : #include <sys/ioctl.h>
       7                 :            : 
       8                 :            : #include "sd-dhcp-server.h"
       9                 :            : 
      10                 :            : #include "alloc-util.h"
      11                 :            : #include "dhcp-internal.h"
      12                 :            : #include "dhcp-server-internal.h"
      13                 :            : #include "fd-util.h"
      14                 :            : #include "in-addr-util.h"
      15                 :            : #include "io-util.h"
      16                 :            : #include "sd-id128.h"
      17                 :            : #include "siphash24.h"
      18                 :            : #include "string-util.h"
      19                 :            : #include "unaligned.h"
      20                 :            : 
      21                 :            : #define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
      22                 :            : #define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
      23                 :            : 
      24                 :          0 : static DHCPLease *dhcp_lease_free(DHCPLease *lease) {
      25         [ #  # ]:          0 :         if (!lease)
      26                 :          0 :                 return NULL;
      27                 :            : 
      28                 :          0 :         free(lease->client_id.data);
      29                 :          0 :         return mfree(lease);
      30                 :            : }
      31                 :            : 
      32                 :            : /* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
      33                 :            :  * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
      34                 :            :  * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
      35                 :            :  * accidentally hand it out */
      36                 :         24 : int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
      37                 :            :         struct in_addr netmask_addr;
      38                 :            :         be32_t netmask;
      39                 :            :         uint32_t server_off, broadcast_off, size_max;
      40                 :            : 
      41   [ -  +  -  + ]:         24 :         assert_return(server, -EINVAL);
      42   [ -  +  -  + ]:         24 :         assert_return(address, -EINVAL);
      43   [ +  +  +  + ]:         24 :         assert_return(address->s_addr != INADDR_ANY, -EINVAL);
      44   [ +  +  +  + ]:         16 :         assert_return(prefixlen <= 32, -ERANGE);
      45                 :            : 
      46         [ -  + ]:         12 :         assert_se(in4_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
      47                 :         12 :         netmask = netmask_addr.s_addr;
      48                 :            : 
      49                 :         12 :         server_off = be32toh(address->s_addr & ~netmask);
      50                 :         12 :         broadcast_off = be32toh(~netmask);
      51                 :            : 
      52                 :            :         /* the server address cannot be the subnet address */
      53   [ -  +  -  + ]:         12 :         assert_return(server_off != 0, -ERANGE);
      54                 :            : 
      55                 :            :         /* nor the broadcast address */
      56   [ -  +  -  + ]:         12 :         assert_return(server_off != broadcast_off, -ERANGE);
      57                 :            : 
      58                 :            :         /* 0 offset means we should set a default, we skip the first (subnet) address
      59                 :            :            and take the next one */
      60         [ +  - ]:         12 :         if (offset == 0)
      61                 :         12 :                 offset = 1;
      62                 :            : 
      63                 :         12 :         size_max = (broadcast_off + 1) /* the number of addresses in the subnet */
      64                 :            :                    - offset /* exclude the addresses before the offset */
      65                 :            :                    - 1; /* exclude the last (broadcast) address */
      66                 :            : 
      67                 :            :         /* The pool must contain at least one address */
      68   [ -  +  -  + ]:         12 :         assert_return(size_max >= 1, -ERANGE);
      69                 :            : 
      70         [ +  + ]:         12 :         if (size != 0)
      71   [ -  +  -  + ]:          4 :                 assert_return(size <= size_max, -ERANGE);
      72                 :            :         else
      73                 :          8 :                 size = size_max;
      74                 :            : 
      75   [ +  +  +  -  :         12 :         if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) {
             +  -  -  + ]
      76                 :            : 
      77                 :          8 :                 free(server->bound_leases);
      78         [ +  - ]:          8 :                 server->bound_leases = new0(DHCPLease*, size);
      79         [ -  + ]:          8 :                 if (!server->bound_leases)
      80                 :          0 :                         return -ENOMEM;
      81                 :            : 
      82                 :          8 :                 server->pool_offset = offset;
      83                 :          8 :                 server->pool_size = size;
      84                 :            : 
      85                 :          8 :                 server->address = address->s_addr;
      86                 :          8 :                 server->netmask = netmask;
      87                 :          8 :                 server->subnet = address->s_addr & netmask;
      88                 :            : 
      89   [ +  -  +  - ]:          8 :                 if (server_off >= offset && server_off - offset < size)
      90                 :          8 :                         server->bound_leases[server_off - offset] = &server->invalid_lease;
      91                 :            : 
      92                 :            :                 /* Drop any leases associated with the old address range */
      93                 :          8 :                 hashmap_clear(server->leases_by_client_id);
      94                 :            :         }
      95                 :            : 
      96                 :         12 :         return 0;
      97                 :            : }
      98                 :            : 
      99                 :          0 : int sd_dhcp_server_is_running(sd_dhcp_server *server) {
     100   [ #  #  #  # ]:          0 :         assert_return(server, false);
     101                 :            : 
     102                 :          0 :         return !!server->receive_message;
     103                 :            : }
     104                 :            : 
     105                 :          0 : void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
     106         [ #  # ]:          0 :         assert(id);
     107         [ #  # ]:          0 :         assert(id->length);
     108         [ #  # ]:          0 :         assert(id->data);
     109                 :            : 
     110                 :          0 :         siphash24_compress(&id->length, sizeof(id->length), state);
     111                 :          0 :         siphash24_compress(id->data, id->length, state);
     112                 :          0 : }
     113                 :            : 
     114                 :          0 : int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
     115                 :            :         int r;
     116                 :            : 
     117   [ #  #  #  # ]:          0 :         assert(!a->length || a->data);
     118   [ #  #  #  # ]:          0 :         assert(!b->length || b->data);
     119                 :            : 
     120         [ #  # ]:          0 :         r = CMP(a->length, b->length);
     121         [ #  # ]:          0 :         if (r != 0)
     122                 :          0 :                 return r;
     123                 :            : 
     124                 :          0 :         return memcmp(a->data, b->data, a->length);
     125                 :            : }
     126                 :            : 
     127                 :          0 : DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(dhcp_lease_hash_ops, DHCPClientId, client_id_hash_func, client_id_compare_func,
     128                 :            :                                               DHCPLease, dhcp_lease_free);
     129                 :            : 
     130                 :         12 : static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
     131         [ -  + ]:         12 :         assert(server);
     132                 :            : 
     133                 :         12 :         log_dhcp_server(server, "UNREF");
     134                 :            : 
     135                 :         12 :         sd_dhcp_server_stop(server);
     136                 :            : 
     137                 :         12 :         sd_event_unref(server->event);
     138                 :            : 
     139                 :         12 :         free(server->timezone);
     140                 :         12 :         free(server->dns);
     141                 :         12 :         free(server->ntp);
     142                 :            : 
     143                 :         12 :         hashmap_free(server->leases_by_client_id);
     144                 :            : 
     145                 :         12 :         free(server->bound_leases);
     146                 :         12 :         return mfree(server);
     147                 :            : }
     148                 :            : 
     149   [ +  +  -  +  :         48 : DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_server, sd_dhcp_server, dhcp_server_free);
                   +  + ]
     150                 :            : 
     151                 :         12 : int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
     152                 :         12 :         _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
     153                 :            : 
     154   [ -  +  -  + ]:         12 :         assert_return(ret, -EINVAL);
     155   [ -  +  -  + ]:         12 :         assert_return(ifindex > 0, -EINVAL);
     156                 :            : 
     157                 :         12 :         server = new0(sd_dhcp_server, 1);
     158         [ -  + ]:         12 :         if (!server)
     159                 :          0 :                 return -ENOMEM;
     160                 :            : 
     161                 :         12 :         server->n_ref = 1;
     162                 :         12 :         server->fd_raw = -1;
     163                 :         12 :         server->fd = -1;
     164                 :         12 :         server->address = htobe32(INADDR_ANY);
     165                 :         12 :         server->netmask = htobe32(INADDR_ANY);
     166                 :         12 :         server->ifindex = ifindex;
     167                 :            : 
     168                 :         12 :         server->leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
     169         [ -  + ]:         12 :         if (!server->leases_by_client_id)
     170                 :          0 :                 return -ENOMEM;
     171                 :            : 
     172                 :         12 :         server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC);
     173                 :         12 :         server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC);
     174                 :            : 
     175                 :         12 :         *ret = TAKE_PTR(server);
     176                 :            : 
     177                 :         12 :         return 0;
     178                 :            : }
     179                 :            : 
     180                 :         16 : int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) {
     181                 :            :         int r;
     182                 :            : 
     183   [ -  +  -  + ]:         16 :         assert_return(server, -EINVAL);
     184   [ +  +  +  + ]:         16 :         assert_return(!server->event, -EBUSY);
     185                 :            : 
     186         [ +  + ]:          8 :         if (event)
     187                 :          4 :                 server->event = sd_event_ref(event);
     188                 :            :         else {
     189                 :          4 :                 r = sd_event_default(&server->event);
     190         [ -  + ]:          4 :                 if (r < 0)
     191                 :          0 :                         return r;
     192                 :            :         }
     193                 :            : 
     194                 :          8 :         server->event_priority = priority;
     195                 :            : 
     196                 :          8 :         return 0;
     197                 :            : }
     198                 :            : 
     199                 :          4 : int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
     200   [ -  +  -  + ]:          4 :         assert_return(server, -EINVAL);
     201                 :            : 
     202                 :          4 :         server->event = sd_event_unref(server->event);
     203                 :            : 
     204                 :          4 :         return 0;
     205                 :            : }
     206                 :            : 
     207                 :          8 : sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
     208   [ -  +  -  + ]:          8 :         assert_return(server, NULL);
     209                 :            : 
     210                 :          8 :         return server->event;
     211                 :            : }
     212                 :            : 
     213                 :         16 : int sd_dhcp_server_stop(sd_dhcp_server *server) {
     214   [ -  +  -  + ]:         16 :         assert_return(server, -EINVAL);
     215                 :            : 
     216                 :         16 :         server->receive_message =
     217                 :         16 :                 sd_event_source_unref(server->receive_message);
     218                 :            : 
     219                 :         16 :         server->fd_raw = safe_close(server->fd_raw);
     220                 :         16 :         server->fd = safe_close(server->fd);
     221                 :            : 
     222                 :         16 :         log_dhcp_server(server, "STOPPED");
     223                 :            : 
     224                 :         16 :         return 0;
     225                 :            : }
     226                 :            : 
     227                 :          0 : static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
     228                 :            :                                         DHCPPacket *packet, size_t len) {
     229                 :          0 :         union sockaddr_union link = {
     230                 :            :                 .ll.sll_family = AF_PACKET,
     231                 :          0 :                 .ll.sll_protocol = htobe16(ETH_P_IP),
     232                 :          0 :                 .ll.sll_ifindex = server->ifindex,
     233                 :            :                 .ll.sll_halen = ETH_ALEN,
     234                 :            :         };
     235                 :            : 
     236         [ #  # ]:          0 :         assert(server);
     237         [ #  # ]:          0 :         assert(server->ifindex > 0);
     238         [ #  # ]:          0 :         assert(server->address);
     239         [ #  # ]:          0 :         assert(packet);
     240         [ #  # ]:          0 :         assert(len > sizeof(DHCPPacket));
     241                 :            : 
     242                 :          0 :         memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
     243                 :            : 
     244                 :          0 :         dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
     245                 :            :                                       packet->dhcp.yiaddr,
     246                 :            :                                       DHCP_PORT_CLIENT, len);
     247                 :            : 
     248                 :          0 :         return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
     249                 :            : }
     250                 :            : 
     251                 :          0 : static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
     252                 :            :                                 uint16_t destination_port,
     253                 :            :                                 DHCPMessage *message, size_t len) {
     254                 :          0 :         union sockaddr_union dest = {
     255                 :            :                 .in.sin_family = AF_INET,
     256                 :          0 :                 .in.sin_port = htobe16(destination_port),
     257                 :            :                 .in.sin_addr.s_addr = destination,
     258                 :            :         };
     259                 :          0 :         struct iovec iov = {
     260                 :            :                 .iov_base = message,
     261                 :            :                 .iov_len = len,
     262                 :            :         };
     263                 :          0 :         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
     264                 :          0 :         struct msghdr msg = {
     265                 :            :                 .msg_name = &dest,
     266                 :            :                 .msg_namelen = sizeof(dest.in),
     267                 :            :                 .msg_iov = &iov,
     268                 :            :                 .msg_iovlen = 1,
     269                 :            :                 .msg_control = cmsgbuf,
     270                 :            :                 .msg_controllen = sizeof(cmsgbuf),
     271                 :            :         };
     272                 :            :         struct cmsghdr *cmsg;
     273                 :            :         struct in_pktinfo *pktinfo;
     274                 :            : 
     275         [ #  # ]:          0 :         assert(server);
     276         [ #  # ]:          0 :         assert(server->fd >= 0);
     277         [ #  # ]:          0 :         assert(message);
     278         [ #  # ]:          0 :         assert(len > sizeof(DHCPMessage));
     279                 :            : 
     280         [ #  # ]:          0 :         cmsg = CMSG_FIRSTHDR(&msg);
     281         [ #  # ]:          0 :         assert(cmsg);
     282                 :            : 
     283                 :          0 :         cmsg->cmsg_level = IPPROTO_IP;
     284                 :          0 :         cmsg->cmsg_type = IP_PKTINFO;
     285                 :          0 :         cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
     286                 :            : 
     287                 :            :         /* we attach source interface and address info to the message
     288                 :            :            rather than binding the socket. This will be mostly useful
     289                 :            :            when we gain support for arbitrary number of server addresses
     290                 :            :          */
     291                 :          0 :         pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
     292         [ #  # ]:          0 :         assert(pktinfo);
     293                 :            : 
     294                 :          0 :         pktinfo->ipi_ifindex = server->ifindex;
     295                 :          0 :         pktinfo->ipi_spec_dst.s_addr = server->address;
     296                 :            : 
     297         [ #  # ]:          0 :         if (sendmsg(server->fd, &msg, 0) < 0)
     298                 :          0 :                 return -errno;
     299                 :            : 
     300                 :          0 :         return 0;
     301                 :            : }
     302                 :            : 
     303                 :          0 : static bool requested_broadcast(DHCPRequest *req) {
     304         [ #  # ]:          0 :         assert(req);
     305                 :            : 
     306                 :          0 :         return req->message->flags & htobe16(0x8000);
     307                 :            : }
     308                 :            : 
     309                 :          0 : int dhcp_server_send_packet(sd_dhcp_server *server,
     310                 :            :                             DHCPRequest *req, DHCPPacket *packet,
     311                 :            :                             int type, size_t optoffset) {
     312                 :          0 :         be32_t destination = INADDR_ANY;
     313                 :          0 :         uint16_t destination_port = DHCP_PORT_CLIENT;
     314                 :            :         int r;
     315                 :            : 
     316         [ #  # ]:          0 :         assert(server);
     317         [ #  # ]:          0 :         assert(req);
     318         [ #  # ]:          0 :         assert(req->max_optlen);
     319         [ #  # ]:          0 :         assert(optoffset <= req->max_optlen);
     320         [ #  # ]:          0 :         assert(packet);
     321                 :            : 
     322                 :          0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
     323                 :            :                                SD_DHCP_OPTION_SERVER_IDENTIFIER,
     324                 :          0 :                                4, &server->address);
     325         [ #  # ]:          0 :         if (r < 0)
     326                 :          0 :                 return r;
     327                 :            : 
     328                 :          0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
     329                 :            :                                SD_DHCP_OPTION_END, 0, NULL);
     330         [ #  # ]:          0 :         if (r < 0)
     331                 :          0 :                 return r;
     332                 :            : 
     333                 :            :         /* RFC 2131 Section 4.1
     334                 :            : 
     335                 :            :            If the ’giaddr’ field in a DHCP message from a client is non-zero,
     336                 :            :            the server sends any return messages to the ’DHCP server’ port on the
     337                 :            :            BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
     338                 :            :            field is zero and the ’ciaddr’ field is nonzero, then the server
     339                 :            :            unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
     340                 :            :            If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
     341                 :            :            set, then the server broadcasts DHCPOFFER and DHCPACK messages to
     342                 :            :            0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
     343                 :            :            ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
     344                 :            :            messages to the client’s hardware address and ’yiaddr’ address. In
     345                 :            :            all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
     346                 :            :            messages to 0xffffffff.
     347                 :            : 
     348                 :            :            Section 4.3.2
     349                 :            : 
     350                 :            :            If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
     351                 :            :            different subnet. The server MUST set the broadcast bit in the
     352                 :            :            DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
     353                 :            :            client, because the client may not have a correct network address
     354                 :            :            or subnet mask, and the client may not be answering ARP requests.
     355                 :            :          */
     356         [ #  # ]:          0 :         if (req->message->giaddr) {
     357                 :          0 :                 destination = req->message->giaddr;
     358                 :          0 :                 destination_port = DHCP_PORT_SERVER;
     359         [ #  # ]:          0 :                 if (type == DHCP_NAK)
     360                 :          0 :                         packet->dhcp.flags = htobe16(0x8000);
     361   [ #  #  #  # ]:          0 :         } else if (req->message->ciaddr && type != DHCP_NAK)
     362                 :          0 :                 destination = req->message->ciaddr;
     363                 :            : 
     364         [ #  # ]:          0 :         if (destination != INADDR_ANY)
     365                 :          0 :                 return dhcp_server_send_udp(server, destination,
     366                 :            :                                             destination_port, &packet->dhcp,
     367                 :            :                                             sizeof(DHCPMessage) + optoffset);
     368   [ #  #  #  # ]:          0 :         else if (requested_broadcast(req) || type == DHCP_NAK)
     369                 :          0 :                 return dhcp_server_send_udp(server, INADDR_BROADCAST,
     370                 :            :                                             destination_port, &packet->dhcp,
     371                 :            :                                             sizeof(DHCPMessage) + optoffset);
     372                 :            :         else
     373                 :            :                 /* we cannot send UDP packet to specific MAC address when the
     374                 :            :                    address is not yet configured, so must fall back to raw
     375                 :            :                    packets */
     376                 :          0 :                 return dhcp_server_send_unicast_raw(server, packet,
     377                 :            :                                                     sizeof(DHCPPacket) + optoffset);
     378                 :            : }
     379                 :            : 
     380                 :          0 : static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
     381                 :            :                                uint8_t type, size_t *_optoffset,
     382                 :            :                                DHCPRequest *req) {
     383                 :          0 :         _cleanup_free_ DHCPPacket *packet = NULL;
     384                 :          0 :         size_t optoffset = 0;
     385                 :            :         int r;
     386                 :            : 
     387         [ #  # ]:          0 :         assert(server);
     388         [ #  # ]:          0 :         assert(ret);
     389         [ #  # ]:          0 :         assert(_optoffset);
     390   [ #  #  #  # ]:          0 :         assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
     391                 :            : 
     392                 :          0 :         packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
     393         [ #  # ]:          0 :         if (!packet)
     394                 :          0 :                 return -ENOMEM;
     395                 :            : 
     396                 :          0 :         r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
     397                 :          0 :                               be32toh(req->message->xid), type, ARPHRD_ETHER,
     398                 :            :                               req->max_optlen, &optoffset);
     399         [ #  # ]:          0 :         if (r < 0)
     400                 :          0 :                 return r;
     401                 :            : 
     402                 :          0 :         packet->dhcp.flags = req->message->flags;
     403                 :          0 :         packet->dhcp.giaddr = req->message->giaddr;
     404                 :          0 :         memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
     405                 :            : 
     406                 :          0 :         *_optoffset = optoffset;
     407                 :          0 :         *ret = TAKE_PTR(packet);
     408                 :            : 
     409                 :          0 :         return 0;
     410                 :            : }
     411                 :            : 
     412                 :          0 : static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
     413                 :            :                              be32_t address) {
     414                 :          0 :         _cleanup_free_ DHCPPacket *packet = NULL;
     415                 :            :         size_t offset;
     416                 :            :         be32_t lease_time;
     417                 :            :         int r;
     418                 :            : 
     419                 :          0 :         r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
     420         [ #  # ]:          0 :         if (r < 0)
     421                 :          0 :                 return r;
     422                 :            : 
     423                 :          0 :         packet->dhcp.yiaddr = address;
     424                 :            : 
     425                 :          0 :         lease_time = htobe32(req->lifetime);
     426                 :          0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     427                 :            :                                SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
     428                 :            :                                &lease_time);
     429         [ #  # ]:          0 :         if (r < 0)
     430                 :          0 :                 return r;
     431                 :            : 
     432                 :          0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     433                 :          0 :                                SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
     434         [ #  # ]:          0 :         if (r < 0)
     435                 :          0 :                 return r;
     436                 :            : 
     437         [ #  # ]:          0 :         if (server->emit_router) {
     438                 :          0 :                 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     439                 :          0 :                                        SD_DHCP_OPTION_ROUTER, 4, &server->address);
     440         [ #  # ]:          0 :                 if (r < 0)
     441                 :          0 :                         return r;
     442                 :            :         }
     443                 :            : 
     444                 :          0 :         r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
     445         [ #  # ]:          0 :         if (r < 0)
     446                 :          0 :                 return r;
     447                 :            : 
     448                 :          0 :         return 0;
     449                 :            : }
     450                 :            : 
     451                 :          0 : static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
     452                 :            :                            be32_t address) {
     453                 :          0 :         _cleanup_free_ DHCPPacket *packet = NULL;
     454                 :            :         size_t offset;
     455                 :            :         be32_t lease_time;
     456                 :            :         int r;
     457                 :            : 
     458                 :          0 :         r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
     459         [ #  # ]:          0 :         if (r < 0)
     460                 :          0 :                 return r;
     461                 :            : 
     462                 :          0 :         packet->dhcp.yiaddr = address;
     463                 :            : 
     464                 :          0 :         lease_time = htobe32(req->lifetime);
     465                 :          0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     466                 :            :                                SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
     467                 :            :                                &lease_time);
     468         [ #  # ]:          0 :         if (r < 0)
     469                 :          0 :                 return r;
     470                 :            : 
     471                 :          0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     472                 :          0 :                                SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
     473         [ #  # ]:          0 :         if (r < 0)
     474                 :          0 :                 return r;
     475                 :            : 
     476         [ #  # ]:          0 :         if (server->emit_router) {
     477                 :          0 :                 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     478                 :          0 :                                        SD_DHCP_OPTION_ROUTER, 4, &server->address);
     479         [ #  # ]:          0 :                 if (r < 0)
     480                 :          0 :                         return r;
     481                 :            :         }
     482                 :            : 
     483         [ #  # ]:          0 :         if (server->n_dns > 0) {
     484                 :          0 :                 r = dhcp_option_append(
     485                 :          0 :                                 &packet->dhcp, req->max_optlen, &offset, 0,
     486                 :            :                                 SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
     487                 :          0 :                                 sizeof(struct in_addr) * server->n_dns, server->dns);
     488         [ #  # ]:          0 :                 if (r < 0)
     489                 :          0 :                         return r;
     490                 :            :         }
     491                 :            : 
     492         [ #  # ]:          0 :         if (server->n_ntp > 0) {
     493                 :          0 :                 r = dhcp_option_append(
     494                 :          0 :                                 &packet->dhcp, req->max_optlen, &offset, 0,
     495                 :            :                                 SD_DHCP_OPTION_NTP_SERVER,
     496                 :          0 :                                 sizeof(struct in_addr) * server->n_ntp, server->ntp);
     497         [ #  # ]:          0 :                 if (r < 0)
     498                 :          0 :                         return r;
     499                 :            :         }
     500                 :            : 
     501         [ #  # ]:          0 :         if (server->timezone) {
     502                 :          0 :                 r = dhcp_option_append(
     503                 :          0 :                                 &packet->dhcp, req->max_optlen, &offset, 0,
     504                 :            :                                 SD_DHCP_OPTION_NEW_TZDB_TIMEZONE,
     505                 :          0 :                                 strlen(server->timezone), server->timezone);
     506         [ #  # ]:          0 :                 if (r < 0)
     507                 :          0 :                         return r;
     508                 :            :         }
     509                 :            : 
     510                 :          0 :         r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
     511         [ #  # ]:          0 :         if (r < 0)
     512                 :          0 :                 return r;
     513                 :            : 
     514                 :          0 :         return 0;
     515                 :            : }
     516                 :            : 
     517                 :          0 : static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
     518                 :          0 :         _cleanup_free_ DHCPPacket *packet = NULL;
     519                 :            :         size_t offset;
     520                 :            :         int r;
     521                 :            : 
     522                 :          0 :         r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
     523         [ #  # ]:          0 :         if (r < 0)
     524                 :          0 :                 return r;
     525                 :            : 
     526                 :          0 :         return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
     527                 :            : }
     528                 :            : 
     529                 :          0 : static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
     530                 :            :                                   be32_t gateway, uint8_t chaddr[]) {
     531                 :          0 :         _cleanup_free_ DHCPPacket *packet = NULL;
     532                 :          0 :         size_t optoffset = 0;
     533                 :            :         int r;
     534                 :            : 
     535         [ #  # ]:          0 :         assert(server);
     536         [ #  # ]:          0 :         assert(address != INADDR_ANY);
     537         [ #  # ]:          0 :         assert(chaddr);
     538                 :            : 
     539                 :          0 :         packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE);
     540         [ #  # ]:          0 :         if (!packet)
     541                 :          0 :                 return -ENOMEM;
     542                 :            : 
     543                 :          0 :         r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
     544                 :            :                               DHCP_FORCERENEW, ARPHRD_ETHER,
     545                 :            :                               DHCP_MIN_OPTIONS_SIZE, &optoffset);
     546         [ #  # ]:          0 :         if (r < 0)
     547                 :          0 :                 return r;
     548                 :            : 
     549                 :          0 :         r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
     550                 :            :                                &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL);
     551         [ #  # ]:          0 :         if (r < 0)
     552                 :          0 :                 return r;
     553                 :            : 
     554                 :          0 :         memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
     555                 :            : 
     556                 :          0 :         r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
     557                 :          0 :                                  &packet->dhcp,
     558                 :            :                                  sizeof(DHCPMessage) + optoffset);
     559         [ #  # ]:          0 :         if (r < 0)
     560                 :          0 :                 return r;
     561                 :            : 
     562                 :          0 :         return 0;
     563                 :            : }
     564                 :            : 
     565                 :          0 : static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
     566                 :          0 :         DHCPRequest *req = userdata;
     567                 :            : 
     568         [ #  # ]:          0 :         assert(req);
     569                 :            : 
     570   [ #  #  #  #  :          0 :         switch(code) {
                   #  # ]
     571                 :          0 :         case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
     572         [ #  # ]:          0 :                 if (len == 4)
     573                 :          0 :                         req->lifetime = unaligned_read_be32(option);
     574                 :            : 
     575                 :          0 :                 break;
     576                 :          0 :         case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS:
     577         [ #  # ]:          0 :                 if (len == 4)
     578                 :          0 :                         memcpy(&req->requested_ip, option, sizeof(be32_t));
     579                 :            : 
     580                 :          0 :                 break;
     581                 :          0 :         case SD_DHCP_OPTION_SERVER_IDENTIFIER:
     582         [ #  # ]:          0 :                 if (len == 4)
     583                 :          0 :                         memcpy(&req->server_id, option, sizeof(be32_t));
     584                 :            : 
     585                 :          0 :                 break;
     586                 :          0 :         case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
     587         [ #  # ]:          0 :                 if (len >= 2) {
     588                 :            :                         uint8_t *data;
     589                 :            : 
     590                 :          0 :                         data = memdup(option, len);
     591         [ #  # ]:          0 :                         if (!data)
     592                 :          0 :                                 return -ENOMEM;
     593                 :            : 
     594                 :          0 :                         free(req->client_id.data);
     595                 :          0 :                         req->client_id.data = data;
     596                 :          0 :                         req->client_id.length = len;
     597                 :            :                 }
     598                 :            : 
     599                 :          0 :                 break;
     600                 :          0 :         case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
     601                 :            : 
     602   [ #  #  #  # ]:          0 :                 if (len == 2 && unaligned_read_be16(option) >= sizeof(DHCPPacket))
     603                 :          0 :                         req->max_optlen = unaligned_read_be16(option) - sizeof(DHCPPacket);
     604                 :            : 
     605                 :          0 :                 break;
     606                 :            :         }
     607                 :            : 
     608                 :          0 :         return 0;
     609                 :            : }
     610                 :            : 
     611                 :          0 : static void dhcp_request_free(DHCPRequest *req) {
     612         [ #  # ]:          0 :         if (!req)
     613                 :          0 :                 return;
     614                 :            : 
     615                 :          0 :         free(req->client_id.data);
     616                 :          0 :         free(req);
     617                 :            : }
     618                 :            : 
     619         [ #  # ]:          0 : DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
     620                 :            : 
     621                 :          0 : static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
     622         [ #  # ]:          0 :         assert(req);
     623         [ #  # ]:          0 :         assert(message);
     624                 :            : 
     625                 :          0 :         req->message = message;
     626                 :            : 
     627                 :            :         /* set client id based on MAC address if client did not send an explicit
     628                 :            :            one */
     629         [ #  # ]:          0 :         if (!req->client_id.data) {
     630                 :            :                 void *data;
     631                 :            : 
     632                 :          0 :                 data = malloc0(ETH_ALEN + 1);
     633         [ #  # ]:          0 :                 if (!data)
     634                 :          0 :                         return -ENOMEM;
     635                 :            : 
     636                 :          0 :                 ((uint8_t*) data)[0] = 0x01;
     637                 :          0 :                 memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN);
     638                 :            : 
     639                 :          0 :                 req->client_id.length = ETH_ALEN + 1;
     640                 :          0 :                 req->client_id.data = data;
     641                 :            :         }
     642                 :            : 
     643         [ #  # ]:          0 :         if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
     644                 :          0 :                 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
     645                 :            : 
     646         [ #  # ]:          0 :         if (req->lifetime <= 0)
     647                 :          0 :                 req->lifetime = MAX(1ULL, server->default_lease_time);
     648                 :            : 
     649   [ #  #  #  # ]:          0 :         if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time)
     650                 :          0 :                 req->lifetime = server->max_lease_time;
     651                 :            : 
     652                 :          0 :         return 0;
     653                 :            : }
     654                 :            : 
     655                 :          0 : static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
     656         [ #  # ]:          0 :         assert(server);
     657                 :            : 
     658         [ #  # ]:          0 :         if (!server->pool_size)
     659                 :          0 :                 return -EINVAL;
     660                 :            : 
     661         [ #  # ]:          0 :         if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) ||
     662         [ #  # ]:          0 :             be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
     663                 :          0 :                 return -ERANGE;
     664                 :            : 
     665                 :          0 :         return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
     666                 :            : }
     667                 :            : 
     668                 :            : #define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
     669                 :            : 
     670                 :          0 : int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
     671                 :            :                                size_t length) {
     672                 :          0 :         _cleanup_(dhcp_request_freep) DHCPRequest *req = NULL;
     673                 :          0 :         _cleanup_free_ char *error_message = NULL;
     674                 :            :         DHCPLease *existing_lease;
     675                 :            :         int type, r;
     676                 :            : 
     677         [ #  # ]:          0 :         assert(server);
     678         [ #  # ]:          0 :         assert(message);
     679                 :            : 
     680         [ #  # ]:          0 :         if (message->op != BOOTREQUEST ||
     681         [ #  # ]:          0 :             message->htype != ARPHRD_ETHER ||
     682         [ #  # ]:          0 :             message->hlen != ETHER_ADDR_LEN)
     683                 :          0 :                 return 0;
     684                 :            : 
     685                 :          0 :         req = new0(DHCPRequest, 1);
     686         [ #  # ]:          0 :         if (!req)
     687                 :          0 :                 return -ENOMEM;
     688                 :            : 
     689                 :          0 :         type = dhcp_option_parse(message, length, parse_request, req, &error_message);
     690         [ #  # ]:          0 :         if (type < 0)
     691                 :          0 :                 return 0;
     692                 :            : 
     693                 :          0 :         r = ensure_sane_request(server, req, message);
     694         [ #  # ]:          0 :         if (r < 0)
     695                 :            :                 /* this only fails on critical errors */
     696                 :          0 :                 return r;
     697                 :            : 
     698                 :          0 :         existing_lease = hashmap_get(server->leases_by_client_id,
     699                 :          0 :                                      &req->client_id);
     700                 :            : 
     701   [ #  #  #  #  :          0 :         switch(type) {
                      # ]
     702                 :            : 
     703                 :          0 :         case DHCP_DISCOVER: {
     704                 :          0 :                 be32_t address = INADDR_ANY;
     705                 :            :                 unsigned i;
     706                 :            : 
     707                 :          0 :                 log_dhcp_server(server, "DISCOVER (0x%x)",
     708                 :            :                                 be32toh(req->message->xid));
     709                 :            : 
     710         [ #  # ]:          0 :                 if (!server->pool_size)
     711                 :            :                         /* no pool allocated */
     712                 :          0 :                         return 0;
     713                 :            : 
     714                 :            :                 /* for now pick a random free address from the pool */
     715         [ #  # ]:          0 :                 if (existing_lease)
     716                 :          0 :                         address = existing_lease->address;
     717                 :            :                 else {
     718                 :            :                         struct siphash state;
     719                 :            :                         uint64_t hash;
     720                 :            :                         uint32_t next_offer;
     721                 :            : 
     722                 :            :                         /* even with no persistence of leases, we try to offer the same client
     723                 :            :                            the same IP address. we do this by using the hash of the client id
     724                 :            :                            as the offset into the pool of leases when finding the next free one */
     725                 :            : 
     726                 :          0 :                         siphash24_init(&state, HASH_KEY.bytes);
     727                 :          0 :                         client_id_hash_func(&req->client_id, &state);
     728                 :          0 :                         hash = htole64(siphash24_finalize(&state));
     729                 :          0 :                         next_offer = hash % server->pool_size;
     730                 :            : 
     731         [ #  # ]:          0 :                         for (i = 0; i < server->pool_size; i++) {
     732         [ #  # ]:          0 :                                 if (!server->bound_leases[next_offer]) {
     733                 :          0 :                                         address = server->subnet | htobe32(server->pool_offset + next_offer);
     734                 :          0 :                                         break;
     735                 :            :                                 }
     736                 :            : 
     737                 :          0 :                                 next_offer = (next_offer + 1) % server->pool_size;
     738                 :            :                         }
     739                 :            :                 }
     740                 :            : 
     741         [ #  # ]:          0 :                 if (address == INADDR_ANY)
     742                 :            :                         /* no free addresses left */
     743                 :          0 :                         return 0;
     744                 :            : 
     745                 :          0 :                 r = server_send_offer(server, req, address);
     746         [ #  # ]:          0 :                 if (r < 0)
     747                 :            :                         /* this only fails on critical errors */
     748                 :          0 :                         return log_dhcp_server_errno(server, r, "Could not send offer: %m");
     749                 :            : 
     750                 :          0 :                 log_dhcp_server(server, "OFFER (0x%x)", be32toh(req->message->xid));
     751                 :          0 :                 return DHCP_OFFER;
     752                 :            :         }
     753                 :          0 :         case DHCP_DECLINE:
     754                 :          0 :                 log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
     755                 :            : 
     756                 :            :                 /* TODO: make sure we don't offer this address again */
     757                 :            : 
     758                 :          0 :                 return 1;
     759                 :            : 
     760                 :          0 :         case DHCP_REQUEST: {
     761                 :            :                 be32_t address;
     762                 :          0 :                 bool init_reboot = false;
     763                 :            :                 int pool_offset;
     764                 :            : 
     765                 :            :                 /* see RFC 2131, section 4.3.2 */
     766                 :            : 
     767         [ #  # ]:          0 :                 if (req->server_id) {
     768                 :          0 :                         log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
     769                 :            :                                         be32toh(req->message->xid));
     770                 :            : 
     771                 :            :                         /* SELECTING */
     772         [ #  # ]:          0 :                         if (req->server_id != server->address)
     773                 :            :                                 /* client did not pick us */
     774                 :          0 :                                 return 0;
     775                 :            : 
     776         [ #  # ]:          0 :                         if (req->message->ciaddr)
     777                 :            :                                 /* this MUST be zero */
     778                 :          0 :                                 return 0;
     779                 :            : 
     780         [ #  # ]:          0 :                         if (!req->requested_ip)
     781                 :            :                                 /* this must be filled in with the yiaddr
     782                 :            :                                    from the chosen OFFER */
     783                 :          0 :                                 return 0;
     784                 :            : 
     785                 :          0 :                         address = req->requested_ip;
     786         [ #  # ]:          0 :                 } else if (req->requested_ip) {
     787                 :          0 :                         log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
     788                 :            :                                         be32toh(req->message->xid));
     789                 :            : 
     790                 :            :                         /* INIT-REBOOT */
     791         [ #  # ]:          0 :                         if (req->message->ciaddr)
     792                 :            :                                 /* this MUST be zero */
     793                 :          0 :                                 return 0;
     794                 :            : 
     795                 :            :                         /* TODO: check more carefully if IP is correct */
     796                 :          0 :                         address = req->requested_ip;
     797                 :          0 :                         init_reboot = true;
     798                 :            :                 } else {
     799                 :          0 :                         log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
     800                 :            :                                         be32toh(req->message->xid));
     801                 :            : 
     802                 :            :                         /* REBINDING / RENEWING */
     803         [ #  # ]:          0 :                         if (!req->message->ciaddr)
     804                 :            :                                 /* this MUST be filled in with clients IP address */
     805                 :          0 :                                 return 0;
     806                 :            : 
     807                 :          0 :                         address = req->message->ciaddr;
     808                 :            :                 }
     809                 :            : 
     810                 :          0 :                 pool_offset = get_pool_offset(server, address);
     811                 :            : 
     812                 :            :                 /* verify that the requested address is from the pool, and either
     813                 :            :                    owned by the current client or free */
     814         [ #  # ]:          0 :                 if (pool_offset >= 0 &&
     815         [ #  # ]:          0 :                     server->bound_leases[pool_offset] == existing_lease) {
     816                 :            :                         DHCPLease *lease;
     817                 :          0 :                         usec_t time_now = 0;
     818                 :            : 
     819         [ #  # ]:          0 :                         if (!existing_lease) {
     820                 :          0 :                                 lease = new0(DHCPLease, 1);
     821         [ #  # ]:          0 :                                 if (!lease)
     822                 :          0 :                                         return -ENOMEM;
     823                 :          0 :                                 lease->address = address;
     824                 :          0 :                                 lease->client_id.data = memdup(req->client_id.data,
     825                 :          0 :                                                                req->client_id.length);
     826         [ #  # ]:          0 :                                 if (!lease->client_id.data) {
     827                 :          0 :                                         free(lease);
     828                 :          0 :                                         return -ENOMEM;
     829                 :            :                                 }
     830                 :          0 :                                 lease->client_id.length = req->client_id.length;
     831                 :          0 :                                 memcpy(&lease->chaddr, &req->message->chaddr,
     832                 :            :                                        ETH_ALEN);
     833                 :          0 :                                 lease->gateway = req->message->giaddr;
     834                 :            :                         } else
     835                 :          0 :                                 lease = existing_lease;
     836                 :            : 
     837                 :          0 :                         r = sd_event_now(server->event,
     838                 :            :                                          clock_boottime_or_monotonic(),
     839                 :            :                                          &time_now);
     840         [ #  # ]:          0 :                         if (r < 0) {
     841         [ #  # ]:          0 :                                 if (!existing_lease)
     842                 :          0 :                                         dhcp_lease_free(lease);
     843                 :          0 :                                 return r;
     844                 :            :                         }
     845                 :            : 
     846                 :          0 :                         lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
     847                 :            : 
     848                 :          0 :                         r = server_send_ack(server, req, address);
     849         [ #  # ]:          0 :                         if (r < 0) {
     850                 :            :                                 /* this only fails on critical errors */
     851                 :          0 :                                 log_dhcp_server_errno(server, r, "Could not send ack: %m");
     852                 :            : 
     853         [ #  # ]:          0 :                                 if (!existing_lease)
     854                 :          0 :                                         dhcp_lease_free(lease);
     855                 :            : 
     856                 :          0 :                                 return r;
     857                 :            :                         } else {
     858                 :          0 :                                 log_dhcp_server(server, "ACK (0x%x)",
     859                 :            :                                                 be32toh(req->message->xid));
     860                 :            : 
     861                 :          0 :                                 server->bound_leases[pool_offset] = lease;
     862                 :          0 :                                 hashmap_put(server->leases_by_client_id,
     863                 :          0 :                                             &lease->client_id, lease);
     864                 :            : 
     865                 :          0 :                                 return DHCP_ACK;
     866                 :            :                         }
     867                 :            : 
     868         [ #  # ]:          0 :                 } else if (init_reboot) {
     869                 :          0 :                         r = server_send_nak(server, req);
     870         [ #  # ]:          0 :                         if (r < 0)
     871                 :            :                                 /* this only fails on critical errors */
     872                 :          0 :                                 return log_dhcp_server_errno(server, r, "Could not send nak: %m");
     873                 :            : 
     874                 :          0 :                         log_dhcp_server(server, "NAK (0x%x)", be32toh(req->message->xid));
     875                 :          0 :                         return DHCP_NAK;
     876                 :            :                 }
     877                 :            : 
     878                 :          0 :                 break;
     879                 :            :         }
     880                 :            : 
     881                 :          0 :         case DHCP_RELEASE: {
     882                 :            :                 int pool_offset;
     883                 :            : 
     884                 :          0 :                 log_dhcp_server(server, "RELEASE (0x%x)",
     885                 :            :                                 be32toh(req->message->xid));
     886                 :            : 
     887         [ #  # ]:          0 :                 if (!existing_lease)
     888                 :          0 :                         return 0;
     889                 :            : 
     890         [ #  # ]:          0 :                 if (existing_lease->address != req->message->ciaddr)
     891                 :          0 :                         return 0;
     892                 :            : 
     893                 :          0 :                 pool_offset = get_pool_offset(server, req->message->ciaddr);
     894         [ #  # ]:          0 :                 if (pool_offset < 0)
     895                 :          0 :                         return 0;
     896                 :            : 
     897         [ #  # ]:          0 :                 if (server->bound_leases[pool_offset] == existing_lease) {
     898                 :          0 :                         server->bound_leases[pool_offset] = NULL;
     899                 :          0 :                         hashmap_remove(server->leases_by_client_id, existing_lease);
     900                 :          0 :                         dhcp_lease_free(existing_lease);
     901                 :            :                 }
     902                 :            : 
     903                 :          0 :                 return 0;
     904                 :            :         }}
     905                 :            : 
     906                 :          0 :         return 0;
     907                 :            : }
     908                 :            : 
     909                 :          0 : static int server_receive_message(sd_event_source *s, int fd,
     910                 :            :                                   uint32_t revents, void *userdata) {
     911                 :          0 :         _cleanup_free_ DHCPMessage *message = NULL;
     912                 :            :         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
     913                 :          0 :         sd_dhcp_server *server = userdata;
     914                 :          0 :         struct iovec iov = {};
     915                 :          0 :         struct msghdr msg = {
     916                 :            :                 .msg_iov = &iov,
     917                 :            :                 .msg_iovlen = 1,
     918                 :            :                 .msg_control = cmsgbuf,
     919                 :            :                 .msg_controllen = sizeof(cmsgbuf),
     920                 :            :         };
     921                 :            :         struct cmsghdr *cmsg;
     922                 :            :         ssize_t buflen, len;
     923                 :            :         int r;
     924                 :            : 
     925         [ #  # ]:          0 :         assert(server);
     926                 :            : 
     927                 :          0 :         buflen = next_datagram_size_fd(fd);
     928         [ #  # ]:          0 :         if (buflen < 0)
     929                 :          0 :                 return buflen;
     930                 :            : 
     931                 :          0 :         message = malloc(buflen);
     932         [ #  # ]:          0 :         if (!message)
     933                 :          0 :                 return -ENOMEM;
     934                 :            : 
     935                 :          0 :         iov = IOVEC_MAKE(message, buflen);
     936                 :            : 
     937                 :          0 :         len = recvmsg(fd, &msg, 0);
     938         [ #  # ]:          0 :         if (len < 0) {
     939   [ #  #  #  # ]:          0 :                 if (IN_SET(errno, EAGAIN, EINTR))
     940                 :          0 :                         return 0;
     941                 :            : 
     942                 :          0 :                 return -errno;
     943                 :            :         }
     944         [ #  # ]:          0 :         if ((size_t)len < sizeof(DHCPMessage))
     945                 :          0 :                 return 0;
     946                 :            : 
     947   [ #  #  #  # ]:          0 :         CMSG_FOREACH(cmsg, &msg) {
     948         [ #  # ]:          0 :                 if (cmsg->cmsg_level == IPPROTO_IP &&
     949         [ #  # ]:          0 :                     cmsg->cmsg_type == IP_PKTINFO &&
     950         [ #  # ]:          0 :                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
     951                 :          0 :                         struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
     952                 :            : 
     953                 :            :                         /* TODO figure out if this can be done as a filter on
     954                 :            :                          * the socket, like for IPv6 */
     955         [ #  # ]:          0 :                         if (server->ifindex != info->ipi_ifindex)
     956                 :          0 :                                 return 0;
     957                 :            : 
     958                 :          0 :                         break;
     959                 :            :                 }
     960                 :            :         }
     961                 :            : 
     962                 :          0 :         r = dhcp_server_handle_message(server, message, (size_t) len);
     963         [ #  # ]:          0 :         if (r < 0)
     964                 :          0 :                 log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m");
     965                 :            : 
     966                 :          0 :         return 0;
     967                 :            : }
     968                 :            : 
     969                 :          8 : int sd_dhcp_server_start(sd_dhcp_server *server) {
     970                 :            :         int r;
     971                 :            : 
     972   [ -  +  -  + ]:          8 :         assert_return(server, -EINVAL);
     973   [ -  +  -  + ]:          8 :         assert_return(server->event, -EINVAL);
     974   [ -  +  -  + ]:          8 :         assert_return(!server->receive_message, -EBUSY);
     975   [ -  +  -  + ]:          8 :         assert_return(server->fd_raw < 0, -EBUSY);
     976   [ -  +  -  + ]:          8 :         assert_return(server->fd < 0, -EBUSY);
     977   [ +  +  +  + ]:          8 :         assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
     978                 :            : 
     979                 :          4 :         r = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
     980         [ +  - ]:          4 :         if (r < 0) {
     981                 :          4 :                 r = -errno;
     982                 :          4 :                 sd_dhcp_server_stop(server);
     983                 :          4 :                 return r;
     984                 :            :         }
     985                 :          0 :         server->fd_raw = r;
     986                 :            : 
     987                 :          0 :         r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER);
     988         [ #  # ]:          0 :         if (r < 0) {
     989                 :          0 :                 sd_dhcp_server_stop(server);
     990                 :          0 :                 return r;
     991                 :            :         }
     992                 :          0 :         server->fd = r;
     993                 :            : 
     994                 :          0 :         r = sd_event_add_io(server->event, &server->receive_message,
     995                 :            :                             server->fd, EPOLLIN,
     996                 :            :                             server_receive_message, server);
     997         [ #  # ]:          0 :         if (r < 0) {
     998                 :          0 :                 sd_dhcp_server_stop(server);
     999                 :          0 :                 return r;
    1000                 :            :         }
    1001                 :            : 
    1002                 :          0 :         r = sd_event_source_set_priority(server->receive_message,
    1003                 :          0 :                                          server->event_priority);
    1004         [ #  # ]:          0 :         if (r < 0) {
    1005                 :          0 :                 sd_dhcp_server_stop(server);
    1006                 :          0 :                 return r;
    1007                 :            :         }
    1008                 :            : 
    1009                 :          0 :         log_dhcp_server(server, "STARTED");
    1010                 :            : 
    1011                 :          0 :         return 0;
    1012                 :            : }
    1013                 :            : 
    1014                 :          0 : int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
    1015                 :            :         unsigned i;
    1016                 :          0 :         int r = 0;
    1017                 :            : 
    1018   [ #  #  #  # ]:          0 :         assert_return(server, -EINVAL);
    1019         [ #  # ]:          0 :         assert(server->bound_leases);
    1020                 :            : 
    1021         [ #  # ]:          0 :         for (i = 0; i < server->pool_size; i++) {
    1022                 :          0 :                 DHCPLease *lease = server->bound_leases[i];
    1023                 :            : 
    1024   [ #  #  #  # ]:          0 :                 if (!lease || lease == &server->invalid_lease)
    1025                 :          0 :                         continue;
    1026                 :            : 
    1027                 :          0 :                 r = server_send_forcerenew(server, lease->address,
    1028                 :            :                                            lease->gateway,
    1029                 :          0 :                                            lease->chaddr);
    1030         [ #  # ]:          0 :                 if (r < 0)
    1031                 :          0 :                         return r;
    1032                 :            : 
    1033                 :          0 :                 log_dhcp_server(server, "FORCERENEW");
    1034                 :            :         }
    1035                 :            : 
    1036                 :          0 :         return r;
    1037                 :            : }
    1038                 :            : 
    1039                 :          0 : int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
    1040                 :            :         int r;
    1041                 :            : 
    1042   [ #  #  #  # ]:          0 :         assert_return(server, -EINVAL);
    1043   [ #  #  #  # ]:          0 :         assert_return(timezone_is_valid(tz, LOG_DEBUG), -EINVAL);
    1044                 :            : 
    1045         [ #  # ]:          0 :         if (streq_ptr(tz, server->timezone))
    1046                 :          0 :                 return 0;
    1047                 :            : 
    1048                 :          0 :         r = free_and_strdup(&server->timezone, tz);
    1049         [ #  # ]:          0 :         if (r < 0)
    1050                 :          0 :                 return r;
    1051                 :            : 
    1052                 :          0 :         return 1;
    1053                 :            : }
    1054                 :            : 
    1055                 :          0 : int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) {
    1056   [ #  #  #  # ]:          0 :         assert_return(server, -EINVAL);
    1057                 :            : 
    1058         [ #  # ]:          0 :         if (t == server->max_lease_time)
    1059                 :          0 :                 return 0;
    1060                 :            : 
    1061                 :          0 :         server->max_lease_time = t;
    1062                 :          0 :         return 1;
    1063                 :            : }
    1064                 :            : 
    1065                 :          0 : int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) {
    1066   [ #  #  #  # ]:          0 :         assert_return(server, -EINVAL);
    1067                 :            : 
    1068         [ #  # ]:          0 :         if (t == server->default_lease_time)
    1069                 :          0 :                 return 0;
    1070                 :            : 
    1071                 :          0 :         server->default_lease_time = t;
    1072                 :          0 :         return 1;
    1073                 :            : }
    1074                 :            : 
    1075                 :          0 : int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) {
    1076   [ #  #  #  # ]:          0 :         assert_return(server, -EINVAL);
    1077   [ #  #  #  #  :          0 :         assert_return(dns || n <= 0, -EINVAL);
                   #  # ]
    1078                 :            : 
    1079         [ #  # ]:          0 :         if (server->n_dns == n &&
    1080         [ #  # ]:          0 :             memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0)
    1081                 :          0 :                 return 0;
    1082                 :            : 
    1083         [ #  # ]:          0 :         if (n <= 0) {
    1084                 :          0 :                 server->dns = mfree(server->dns);
    1085                 :          0 :                 server->n_dns = 0;
    1086                 :            :         } else {
    1087                 :            :                 struct in_addr *c;
    1088                 :            : 
    1089                 :          0 :                 c = newdup(struct in_addr, dns, n);
    1090         [ #  # ]:          0 :                 if (!c)
    1091                 :          0 :                         return -ENOMEM;
    1092                 :            : 
    1093                 :          0 :                 free(server->dns);
    1094                 :          0 :                 server->dns = c;
    1095                 :          0 :                 server->n_dns = n;
    1096                 :            :         }
    1097                 :            : 
    1098                 :          0 :         return 1;
    1099                 :            : }
    1100                 :            : 
    1101                 :          0 : int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) {
    1102   [ #  #  #  # ]:          0 :         assert_return(server, -EINVAL);
    1103   [ #  #  #  #  :          0 :         assert_return(ntp || n <= 0, -EINVAL);
                   #  # ]
    1104                 :            : 
    1105         [ #  # ]:          0 :         if (server->n_ntp == n &&
    1106         [ #  # ]:          0 :             memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0)
    1107                 :          0 :                 return 0;
    1108                 :            : 
    1109         [ #  # ]:          0 :         if (n <= 0) {
    1110                 :          0 :                 server->ntp = mfree(server->ntp);
    1111                 :          0 :                 server->n_ntp = 0;
    1112                 :            :         } else {
    1113                 :            :                 struct in_addr *c;
    1114                 :            : 
    1115                 :          0 :                 c = newdup(struct in_addr, ntp, n);
    1116         [ #  # ]:          0 :                 if (!c)
    1117                 :          0 :                         return -ENOMEM;
    1118                 :            : 
    1119                 :          0 :                 free(server->ntp);
    1120                 :          0 :                 server->ntp = c;
    1121                 :          0 :                 server->n_ntp = n;
    1122                 :            :         }
    1123                 :            : 
    1124                 :          0 :         return 1;
    1125                 :            : }
    1126                 :            : 
    1127                 :          0 : int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) {
    1128   [ #  #  #  # ]:          0 :         assert_return(server, -EINVAL);
    1129                 :            : 
    1130         [ #  # ]:          0 :         if (enabled == server->emit_router)
    1131                 :          0 :                 return 0;
    1132                 :            : 
    1133                 :          0 :         server->emit_router = enabled;
    1134                 :            : 
    1135                 :          0 :         return 1;
    1136                 :            : }

Generated by: LCOV version 1.14