LCOV - code coverage report
Current view: top level - libsystemd-network - sd-dhcp-server.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 97 609 15.9 %
Date: 2019-08-22 15:41:25 Functions: 10 38 26.3 %

          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           6 : 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           6 :         assert_return(server, -EINVAL);
      42           6 :         assert_return(address, -EINVAL);
      43           6 :         assert_return(address->s_addr != INADDR_ANY, -EINVAL);
      44           4 :         assert_return(prefixlen <= 32, -ERANGE);
      45             : 
      46           3 :         assert_se(in4_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
      47           3 :         netmask = netmask_addr.s_addr;
      48             : 
      49           3 :         server_off = be32toh(address->s_addr & ~netmask);
      50           3 :         broadcast_off = be32toh(~netmask);
      51             : 
      52             :         /* the server address cannot be the subnet address */
      53           3 :         assert_return(server_off != 0, -ERANGE);
      54             : 
      55             :         /* nor the broadcast address */
      56           3 :         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           3 :         if (offset == 0)
      61           3 :                 offset = 1;
      62             : 
      63           3 :         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           3 :         assert_return(size_max >= 1, -ERANGE);
      69             : 
      70           3 :         if (size != 0)
      71           1 :                 assert_return(size <= size_max, -ERANGE);
      72             :         else
      73           2 :                 size = size_max;
      74             : 
      75           3 :         if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) {
      76             : 
      77           2 :                 free(server->bound_leases);
      78           2 :                 server->bound_leases = new0(DHCPLease*, size);
      79           2 :                 if (!server->bound_leases)
      80           0 :                         return -ENOMEM;
      81             : 
      82           2 :                 server->pool_offset = offset;
      83           2 :                 server->pool_size = size;
      84             : 
      85           2 :                 server->address = address->s_addr;
      86           2 :                 server->netmask = netmask;
      87           2 :                 server->subnet = address->s_addr & netmask;
      88             : 
      89           2 :                 if (server_off >= offset && server_off - offset < size)
      90           2 :                         server->bound_leases[server_off - offset] = &server->invalid_lease;
      91             : 
      92             :                 /* Drop any leases associated with the old address range */
      93           2 :                 hashmap_clear(server->leases_by_client_id);
      94             :         }
      95             : 
      96           3 :         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           3 : static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
     131           3 :         assert(server);
     132             : 
     133           3 :         log_dhcp_server(server, "UNREF");
     134             : 
     135           3 :         sd_dhcp_server_stop(server);
     136             : 
     137           3 :         sd_event_unref(server->event);
     138             : 
     139           3 :         free(server->timezone);
     140           3 :         free(server->dns);
     141           3 :         free(server->ntp);
     142             : 
     143           3 :         hashmap_free(server->leases_by_client_id);
     144             : 
     145           3 :         free(server->bound_leases);
     146           3 :         return mfree(server);
     147             : }
     148             : 
     149          11 : DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_server, sd_dhcp_server, dhcp_server_free);
     150             : 
     151           3 : int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
     152           3 :         _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
     153             : 
     154           3 :         assert_return(ret, -EINVAL);
     155           3 :         assert_return(ifindex > 0, -EINVAL);
     156             : 
     157           3 :         server = new0(sd_dhcp_server, 1);
     158           3 :         if (!server)
     159           0 :                 return -ENOMEM;
     160             : 
     161           3 :         server->n_ref = 1;
     162           3 :         server->fd_raw = -1;
     163           3 :         server->fd = -1;
     164           3 :         server->address = htobe32(INADDR_ANY);
     165           3 :         server->netmask = htobe32(INADDR_ANY);
     166           3 :         server->ifindex = ifindex;
     167             : 
     168           3 :         server->leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
     169           3 :         if (!server->leases_by_client_id)
     170           0 :                 return -ENOMEM;
     171             : 
     172           3 :         server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC);
     173           3 :         server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC);
     174             : 
     175           3 :         *ret = TAKE_PTR(server);
     176             : 
     177           3 :         return 0;
     178             : }
     179             : 
     180           4 : int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) {
     181             :         int r;
     182             : 
     183           4 :         assert_return(server, -EINVAL);
     184           4 :         assert_return(!server->event, -EBUSY);
     185             : 
     186           2 :         if (event)
     187           1 :                 server->event = sd_event_ref(event);
     188             :         else {
     189           1 :                 r = sd_event_default(&server->event);
     190           1 :                 if (r < 0)
     191           0 :                         return r;
     192             :         }
     193             : 
     194           2 :         server->event_priority = priority;
     195             : 
     196           2 :         return 0;
     197             : }
     198             : 
     199           1 : int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
     200           1 :         assert_return(server, -EINVAL);
     201             : 
     202           1 :         server->event = sd_event_unref(server->event);
     203             : 
     204           1 :         return 0;
     205             : }
     206             : 
     207           2 : sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
     208           2 :         assert_return(server, NULL);
     209             : 
     210           2 :         return server->event;
     211             : }
     212             : 
     213           4 : int sd_dhcp_server_stop(sd_dhcp_server *server) {
     214           4 :         assert_return(server, -EINVAL);
     215             : 
     216           4 :         server->receive_message =
     217           4 :                 sd_event_source_unref(server->receive_message);
     218             : 
     219           4 :         server->fd_raw = safe_close(server->fd_raw);
     220           4 :         server->fd = safe_close(server->fd);
     221             : 
     222           4 :         log_dhcp_server(server, "STOPPED");
     223             : 
     224           4 :         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           2 : int sd_dhcp_server_start(sd_dhcp_server *server) {
     970             :         int r;
     971             : 
     972           2 :         assert_return(server, -EINVAL);
     973           2 :         assert_return(server->event, -EINVAL);
     974           2 :         assert_return(!server->receive_message, -EBUSY);
     975           2 :         assert_return(server->fd_raw < 0, -EBUSY);
     976           2 :         assert_return(server->fd < 0, -EBUSY);
     977           2 :         assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
     978             : 
     979           1 :         r = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
     980           1 :         if (r < 0) {
     981           1 :                 r = -errno;
     982           1 :                 sd_dhcp_server_stop(server);
     983           1 :                 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