LCOV - code coverage report
Current view: top level - libsystemd-network - sd-dhcp-server.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 92 513 17.9 %
Date: 2015-07-29 18:47:03 Functions: 10 31 32.3 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright (C) 2013 Intel Corporation. All rights reserved.
       7             :   Copyright (C) 2014 Tom Gundersen
       8             : 
       9             :   systemd is free software; you can redistribute it and/or modify it
      10             :   under the terms of the GNU Lesser General Public License as published by
      11             :   the Free Software Foundation; either version 2.1 of the License, or
      12             :   (at your option) any later version.
      13             : 
      14             :   systemd is distributed in the hope that it will be useful, but
      15             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      17             :   Lesser General Public License for more details.
      18             : 
      19             :   You should have received a copy of the GNU Lesser General Public License
      20             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      21             : ***/
      22             : 
      23             : #include <sys/ioctl.h>
      24             : 
      25             : #include "siphash24.h"
      26             : 
      27             : #include "sd-dhcp-server.h"
      28             : #include "dhcp-server-internal.h"
      29             : #include "dhcp-internal.h"
      30             : 
      31             : #define DHCP_DEFAULT_LEASE_TIME         3600 /* one hour */
      32             : 
      33           4 : int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server,
      34             :                                   struct in_addr *address,
      35             :                                   size_t size) {
      36           4 :         assert_return(server, -EINVAL);
      37           4 :         assert_return(address, -EINVAL);
      38           4 :         assert_return(address->s_addr, -EINVAL);
      39           3 :         assert_return(size, -EINVAL);
      40           2 :         assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
      41           1 :         assert_return(!server->pool_size, -EBUSY);
      42           1 :         assert_return(!server->bound_leases, -EBUSY);
      43             : 
      44           1 :         server->bound_leases = new0(DHCPLease*, size);
      45           1 :         if (!server->bound_leases)
      46           0 :                 return -ENOMEM;
      47             : 
      48           1 :         server->pool_start = address->s_addr;
      49           1 :         server->pool_size = size;
      50             : 
      51           1 :         return 0;
      52             : }
      53             : 
      54           4 : int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address,
      55             :                                unsigned char prefixlen) {
      56           4 :         assert_return(server, -EINVAL);
      57           4 :         assert_return(address, -EINVAL);
      58           4 :         assert_return(address->s_addr, -EINVAL);
      59           3 :         assert_return(prefixlen <= 32, -ERANGE);
      60           2 :         assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
      61           1 :         assert_return(server->netmask == htobe32(INADDR_ANY), -EBUSY);
      62             : 
      63           1 :         server->address = address->s_addr;
      64           1 :         server->netmask = htobe32(0xfffffffflu << (32 - prefixlen));
      65             : 
      66           1 :         return 0;
      67             : }
      68             : 
      69           0 : bool sd_dhcp_server_is_running(sd_dhcp_server *server) {
      70           0 :         assert_return(server, -EINVAL);
      71             : 
      72           0 :         return !!server->receive_message;
      73             : }
      74             : 
      75           1 : sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
      76           1 :         if (server)
      77           1 :                 assert_se(REFCNT_INC(server->n_ref) >= 2);
      78             : 
      79           1 :         return server;
      80             : }
      81             : 
      82           0 : unsigned long client_id_hash_func(const void *p,
      83             :                                   const uint8_t hash_key[HASH_KEY_SIZE]) {
      84             :         uint64_t u;
      85           0 :         const DHCPClientId *id = p;
      86             : 
      87           0 :         assert(id);
      88           0 :         assert(id->length);
      89           0 :         assert(id->data);
      90             : 
      91           0 :         siphash24((uint8_t*) &u, id->data, id->length, hash_key);
      92             : 
      93           0 :         return (unsigned long) u;
      94             : }
      95             : 
      96           0 : int client_id_compare_func(const void *_a, const void *_b) {
      97             :         const DHCPClientId *a, *b;
      98             : 
      99           0 :         a = _a;
     100           0 :         b = _b;
     101             : 
     102           0 :         assert(!a->length || a->data);
     103           0 :         assert(!b->length || b->data);
     104             : 
     105           0 :         if (a->length != b->length)
     106           0 :                 return a->length < b->length ? -1 : 1;
     107             : 
     108           0 :         return memcmp(a->data, b->data, a->length);
     109             : }
     110             : 
     111             : static const struct hash_ops client_id_hash_ops = {
     112             :         .hash = client_id_hash_func,
     113             :         .compare = client_id_compare_func
     114             : };
     115             : 
     116           0 : static void dhcp_lease_free(DHCPLease *lease) {
     117           0 :         if (!lease)
     118           0 :                 return;
     119             : 
     120           0 :         free(lease->client_id.data);
     121           0 :         free(lease);
     122             : }
     123             : 
     124           6 : sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
     125             :         DHCPLease *lease;
     126             : 
     127           6 :         if (!server)
     128           4 :                 return NULL;
     129             : 
     130           2 :         if (REFCNT_DEC(server->n_ref) > 0)
     131           1 :                 return NULL;
     132             : 
     133           1 :         log_dhcp_server(server, "UNREF");
     134             : 
     135           1 :         sd_dhcp_server_stop(server);
     136             : 
     137           1 :         sd_event_unref(server->event);
     138             : 
     139           2 :         while ((lease = hashmap_steal_first(server->leases_by_client_id)))
     140           0 :                 dhcp_lease_free(lease);
     141           1 :         hashmap_free(server->leases_by_client_id);
     142             : 
     143           1 :         free(server->bound_leases);
     144           1 :         free(server);
     145             : 
     146           1 :         return NULL;
     147             : }
     148             : 
     149           1 : int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
     150           2 :         _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
     151             : 
     152           1 :         assert_return(ret, -EINVAL);
     153           1 :         assert_return(ifindex > 0, -EINVAL);
     154             : 
     155           1 :         server = new0(sd_dhcp_server, 1);
     156           1 :         if (!server)
     157           0 :                 return -ENOMEM;
     158             : 
     159           1 :         server->n_ref = REFCNT_INIT;
     160           1 :         server->fd_raw = -1;
     161           1 :         server->fd = -1;
     162           1 :         server->address = htobe32(INADDR_ANY);
     163           1 :         server->netmask = htobe32(INADDR_ANY);
     164           1 :         server->index = ifindex;
     165           1 :         server->leases_by_client_id = hashmap_new(&client_id_hash_ops);
     166             : 
     167           1 :         *ret = server;
     168           1 :         server = NULL;
     169             : 
     170           1 :         return 0;
     171             : }
     172             : 
     173           4 : int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event,
     174             :                                 int priority) {
     175             :         int r;
     176             : 
     177           4 :         assert_return(server, -EINVAL);
     178           4 :         assert_return(!server->event, -EBUSY);
     179             : 
     180           2 :         if (event)
     181           1 :                 server->event = sd_event_ref(event);
     182             :         else {
     183           1 :                 r = sd_event_default(&server->event);
     184           1 :                 if (r < 0)
     185           0 :                         return r;
     186             :         }
     187             : 
     188           2 :         server->event_priority = priority;
     189             : 
     190           2 :         return 0;
     191             : }
     192             : 
     193           1 : int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
     194           1 :         assert_return(server, -EINVAL);
     195             : 
     196           1 :         server->event = sd_event_unref(server->event);
     197             : 
     198           1 :         return 0;
     199             : }
     200             : 
     201           2 : sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
     202           2 :         assert_return(server, NULL);
     203             : 
     204           2 :         return server->event;
     205             : }
     206             : 
     207           2 : int sd_dhcp_server_stop(sd_dhcp_server *server) {
     208           2 :         assert_return(server, -EINVAL);
     209             : 
     210           2 :         server->receive_message =
     211           2 :                 sd_event_source_unref(server->receive_message);
     212             : 
     213           2 :         server->fd_raw = safe_close(server->fd_raw);
     214           2 :         server->fd = safe_close(server->fd);
     215             : 
     216           2 :         log_dhcp_server(server, "STOPPED");
     217             : 
     218           2 :         return 0;
     219             : }
     220             : 
     221           0 : static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
     222             :                                         DHCPPacket *packet, size_t len) {
     223           0 :         union sockaddr_union link = {
     224             :                 .ll.sll_family = AF_PACKET,
     225           0 :                 .ll.sll_protocol = htons(ETH_P_IP),
     226           0 :                 .ll.sll_ifindex = server->index,
     227             :                 .ll.sll_halen = ETH_ALEN,
     228             :         };
     229             :         int r;
     230             : 
     231           0 :         assert(server);
     232           0 :         assert(server->index > 0);
     233           0 :         assert(server->address);
     234           0 :         assert(packet);
     235           0 :         assert(len > sizeof(DHCPPacket));
     236             : 
     237           0 :         memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
     238             : 
     239           0 :         dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
     240             :                                       packet->dhcp.yiaddr,
     241             :                                       DHCP_PORT_CLIENT, len);
     242             : 
     243           0 :         r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
     244           0 :         if (r < 0)
     245           0 :                 return r;
     246             : 
     247           0 :         return 0;
     248             : }
     249             : 
     250           0 : static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
     251             :                                 DHCPMessage *message, size_t len) {
     252           0 :         union sockaddr_union dest = {
     253             :                 .in.sin_family = AF_INET,
     254           0 :                 .in.sin_port = htobe16(DHCP_PORT_CLIENT),
     255             :                 .in.sin_addr.s_addr = destination,
     256             :         };
     257           0 :         struct iovec iov = {
     258             :                 .iov_base = message,
     259             :                 .iov_len = len,
     260             :         };
     261           0 :         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
     262           0 :         struct msghdr msg = {
     263             :                 .msg_name = &dest,
     264             :                 .msg_namelen = sizeof(dest.in),
     265             :                 .msg_iov = &iov,
     266             :                 .msg_iovlen = 1,
     267             :                 .msg_control = cmsgbuf,
     268             :                 .msg_controllen = sizeof(cmsgbuf),
     269             :         };
     270             :         struct cmsghdr *cmsg;
     271             :         struct in_pktinfo *pktinfo;
     272             :         int r;
     273             : 
     274           0 :         assert(server);
     275           0 :         assert(server->fd > 0);
     276           0 :         assert(message);
     277           0 :         assert(len > sizeof(DHCPMessage));
     278             : 
     279           0 :         cmsg = CMSG_FIRSTHDR(&msg);
     280           0 :         assert(cmsg);
     281             : 
     282           0 :         cmsg->cmsg_level = IPPROTO_IP;
     283           0 :         cmsg->cmsg_type = IP_PKTINFO;
     284           0 :         cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
     285             : 
     286             :         /* we attach source interface and address info to the message
     287             :            rather than binding the socket. This will be mostly useful
     288             :            when we gain support for arbitrary number of server addresses
     289             :          */
     290           0 :         pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
     291           0 :         assert(pktinfo);
     292             : 
     293           0 :         pktinfo->ipi_ifindex = server->index;
     294           0 :         pktinfo->ipi_spec_dst.s_addr = server->address;
     295             : 
     296           0 :         r = sendmsg(server->fd, &msg, 0);
     297           0 :         if (r < 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             :         int r;
     314             : 
     315           0 :         assert(server);
     316           0 :         assert(req);
     317           0 :         assert(req->max_optlen);
     318           0 :         assert(optoffset <= req->max_optlen);
     319           0 :         assert(packet);
     320             : 
     321           0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
     322             :                                DHCP_OPTION_SERVER_IDENTIFIER,
     323           0 :                                4, &server->address);
     324           0 :         if (r < 0)
     325           0 :                 return r;
     326             : 
     327           0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
     328             :                                DHCP_OPTION_END, 0, NULL);
     329           0 :         if (r < 0)
     330           0 :                 return r;
     331             : 
     332             :         /* RFC 2131 Section 4.1
     333             : 
     334             :            If the ’giaddr’ field in a DHCP message from a client is non-zero,
     335             :            the server sends any return messages to the ’DHCP server’ port on the
     336             :            BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
     337             :            field is zero and the ’ciaddr’ field is nonzero, then the server
     338             :            unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
     339             :            If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
     340             :            set, then the server broadcasts DHCPOFFER and DHCPACK messages to
     341             :            0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
     342             :            ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
     343             :            messages to the client’s hardware address and ’yiaddr’ address. In
     344             :            all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
     345             :            messages to 0xffffffff.
     346             : 
     347             :            Section 4.3.2
     348             : 
     349             :            If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
     350             :            different subnet. The server MUST set the broadcast bit in the
     351             :            DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
     352             :            client, because the client may not have a correct network address
     353             :            or subnet mask, and the client may not be answering ARP requests.
     354             :          */
     355           0 :         if (req->message->giaddr) {
     356           0 :                 destination = req->message->giaddr;
     357           0 :                 if (type == DHCP_NAK)
     358           0 :                         packet->dhcp.flags = htobe16(0x8000);
     359           0 :         } else if (req->message->ciaddr && type != DHCP_NAK)
     360           0 :                 destination = req->message->ciaddr;
     361             : 
     362           0 :         if (destination != INADDR_ANY)
     363           0 :                 return dhcp_server_send_udp(server, destination, &packet->dhcp,
     364             :                                             sizeof(DHCPMessage) + optoffset);
     365           0 :         else if (requested_broadcast(req) || type == DHCP_NAK)
     366           0 :                 return dhcp_server_send_udp(server, INADDR_BROADCAST,
     367             :                                             &packet->dhcp,
     368             :                                             sizeof(DHCPMessage) + optoffset);
     369             :         else
     370             :                 /* we cannot send UDP packet to specific MAC address when the
     371             :                    address is not yet configured, so must fall back to raw
     372             :                    packets */
     373           0 :                 return dhcp_server_send_unicast_raw(server, packet,
     374             :                                                     sizeof(DHCPPacket) + optoffset);
     375             : }
     376             : 
     377           0 : static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
     378             :                                uint8_t type, size_t *_optoffset,
     379             :                                DHCPRequest *req) {
     380           0 :         _cleanup_free_ DHCPPacket *packet = NULL;
     381           0 :         size_t optoffset = 0;
     382             :         int r;
     383             : 
     384           0 :         assert(server);
     385           0 :         assert(ret);
     386           0 :         assert(_optoffset);
     387           0 :         assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
     388             : 
     389           0 :         packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
     390           0 :         if (!packet)
     391           0 :                 return -ENOMEM;
     392             : 
     393           0 :         r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
     394           0 :                               be32toh(req->message->xid), type, ARPHRD_ETHER,
     395             :                               req->max_optlen, &optoffset);
     396           0 :         if (r < 0)
     397           0 :                 return r;
     398             : 
     399           0 :         packet->dhcp.flags = req->message->flags;
     400           0 :         packet->dhcp.giaddr = req->message->giaddr;
     401           0 :         memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
     402             : 
     403           0 :         *_optoffset = optoffset;
     404           0 :         *ret = packet;
     405           0 :         packet = NULL;
     406             : 
     407           0 :         return 0;
     408             : }
     409             : 
     410           0 : static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
     411             :                              be32_t address) {
     412           0 :         _cleanup_free_ DHCPPacket *packet = NULL;
     413             :         size_t offset;
     414             :         be32_t lease_time;
     415             :         int r;
     416             : 
     417           0 :         r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
     418           0 :         if (r < 0)
     419           0 :                 return r;
     420             : 
     421           0 :         packet->dhcp.yiaddr = address;
     422             : 
     423           0 :         lease_time = htobe32(req->lifetime);
     424           0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     425             :                                DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
     426             :                                &lease_time);
     427           0 :         if (r < 0)
     428           0 :                 return r;
     429             : 
     430           0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     431           0 :                                DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
     432           0 :         if (r < 0)
     433           0 :                 return r;
     434             : 
     435           0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     436           0 :                                DHCP_OPTION_ROUTER, 4, &server->address);
     437           0 :         if (r < 0)
     438           0 :                 return r;
     439             : 
     440           0 :         r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
     441           0 :         if (r < 0)
     442           0 :                 return r;
     443             : 
     444           0 :         return 0;
     445             : }
     446             : 
     447           0 : static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
     448             :                            be32_t address) {
     449           0 :         _cleanup_free_ DHCPPacket *packet = NULL;
     450             :         size_t offset;
     451             :         be32_t lease_time;
     452             :         int r;
     453             : 
     454           0 :         r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
     455           0 :         if (r < 0)
     456           0 :                 return r;
     457             : 
     458           0 :         packet->dhcp.yiaddr = address;
     459             : 
     460           0 :         lease_time = htobe32(req->lifetime);
     461           0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     462             :                                DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
     463             :                                &lease_time);
     464           0 :         if (r < 0)
     465           0 :                 return r;
     466             : 
     467           0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     468           0 :                                DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
     469           0 :         if (r < 0)
     470           0 :                 return r;
     471             : 
     472           0 :         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
     473           0 :                                DHCP_OPTION_ROUTER, 4, &server->address);
     474           0 :         if (r < 0)
     475           0 :                 return r;
     476             : 
     477           0 :         r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
     478           0 :         if (r < 0)
     479           0 :                 return r;
     480             : 
     481           0 :         return 0;
     482             : }
     483             : 
     484           0 : static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
     485           0 :         _cleanup_free_ DHCPPacket *packet = NULL;
     486             :         size_t offset;
     487             :         int r;
     488             : 
     489           0 :         r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
     490           0 :         if (r < 0)
     491           0 :                 return r;
     492             : 
     493           0 :         r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
     494           0 :         if (r < 0)
     495           0 :                 return r;
     496             : 
     497           0 :         return 0;
     498             : }
     499             : 
     500           0 : static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
     501             :                                   be32_t gateway, uint8_t chaddr[]) {
     502           0 :         _cleanup_free_ DHCPPacket *packet = NULL;
     503           0 :         size_t optoffset = 0;
     504             :         int r;
     505             : 
     506           0 :         assert(server);
     507           0 :         assert(address != INADDR_ANY);
     508           0 :         assert(chaddr);
     509             : 
     510           0 :         packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE);
     511           0 :         if (!packet)
     512           0 :                 return -ENOMEM;
     513             : 
     514           0 :         r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
     515             :                               DHCP_FORCERENEW, ARPHRD_ETHER,
     516             :                               DHCP_MIN_OPTIONS_SIZE, &optoffset);
     517           0 :         if (r < 0)
     518           0 :                 return r;
     519             : 
     520           0 :         r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
     521             :                                &optoffset, 0, DHCP_OPTION_END, 0, NULL);
     522           0 :         if (r < 0)
     523           0 :                 return r;
     524             : 
     525           0 :         memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
     526             : 
     527           0 :         r = dhcp_server_send_udp(server, address, &packet->dhcp,
     528             :                                  sizeof(DHCPMessage) + optoffset);
     529           0 :         if (r < 0)
     530           0 :                 return r;
     531             : 
     532           0 :         return 0;
     533             : }
     534             : 
     535           0 : static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
     536             :                          void *user_data) {
     537           0 :         DHCPRequest *req = user_data;
     538             : 
     539           0 :         assert(req);
     540             : 
     541           0 :         switch(code) {
     542             :         case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
     543           0 :                 if (len == 4)
     544           0 :                         req->lifetime = be32toh(*(be32_t*)option);
     545             : 
     546           0 :                 break;
     547             :         case DHCP_OPTION_REQUESTED_IP_ADDRESS:
     548           0 :                 if (len == 4)
     549           0 :                         req->requested_ip = *(be32_t*)option;
     550             : 
     551           0 :                 break;
     552             :         case DHCP_OPTION_SERVER_IDENTIFIER:
     553           0 :                 if (len == 4)
     554           0 :                         req->server_id = *(be32_t*)option;
     555             : 
     556           0 :                 break;
     557             :         case DHCP_OPTION_CLIENT_IDENTIFIER:
     558           0 :                 if (len >= 2) {
     559             :                         uint8_t *data;
     560             : 
     561           0 :                         data = memdup(option, len);
     562           0 :                         if (!data)
     563           0 :                                 return -ENOMEM;
     564             : 
     565           0 :                         free(req->client_id.data);
     566           0 :                         req->client_id.data = data;
     567           0 :                         req->client_id.length = len;
     568             :                 }
     569             : 
     570           0 :                 break;
     571             :         case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
     572           0 :                 if (len == 2)
     573           0 :                         req->max_optlen = be16toh(*(be16_t*)option) -
     574             :                                           - sizeof(DHCPPacket);
     575             : 
     576           0 :                 break;
     577             :         }
     578             : 
     579           0 :         return 0;
     580             : }
     581             : 
     582           0 : static void dhcp_request_free(DHCPRequest *req) {
     583           0 :         if (!req)
     584           0 :                 return;
     585             : 
     586           0 :         free(req->client_id.data);
     587           0 :         free(req);
     588             : }
     589             : 
     590           0 : DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
     591             : #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
     592             : 
     593           0 : static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
     594           0 :         assert(req);
     595           0 :         assert(message);
     596             : 
     597           0 :         req->message = message;
     598             : 
     599             :         /* set client id based on MAC address if client did not send an explicit
     600             :            one */
     601           0 :         if (!req->client_id.data) {
     602             :                 uint8_t *data;
     603             : 
     604           0 :                 data = new0(uint8_t, ETH_ALEN + 1);
     605           0 :                 if (!data)
     606           0 :                         return -ENOMEM;
     607             : 
     608           0 :                 req->client_id.length = ETH_ALEN + 1;
     609           0 :                 req->client_id.data = data;
     610           0 :                 req->client_id.data[0] = 0x01;
     611           0 :                 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
     612             :         }
     613             : 
     614           0 :         if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
     615           0 :                 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
     616             : 
     617           0 :         if (!req->lifetime)
     618           0 :                 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
     619             : 
     620           0 :         return 0;
     621             : }
     622             : 
     623           0 : static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
     624           0 :         assert(server);
     625             : 
     626           0 :         if (!server->pool_size)
     627           0 :                 return -EINVAL;
     628             : 
     629           0 :         if (be32toh(requested_ip) < be32toh(server->pool_start) ||
     630           0 :             be32toh(requested_ip) >= be32toh(server->pool_start) +
     631           0 :                                              + server->pool_size)
     632           0 :                 return -EINVAL;
     633             : 
     634           0 :         return be32toh(requested_ip) - be32toh(server->pool_start);
     635             : }
     636             : 
     637           0 : int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
     638             :                                size_t length) {
     639           0 :         _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
     640             :         DHCPLease *existing_lease;
     641             :         int type, r;
     642             : 
     643           0 :         assert(server);
     644           0 :         assert(message);
     645             : 
     646           0 :         if (message->op != BOOTREQUEST ||
     647           0 :             message->htype != ARPHRD_ETHER ||
     648           0 :             message->hlen != ETHER_ADDR_LEN)
     649           0 :                 return 0;
     650             : 
     651           0 :         req = new0(DHCPRequest, 1);
     652           0 :         if (!req)
     653           0 :                 return -ENOMEM;
     654             : 
     655           0 :         type = dhcp_option_parse(message, length, parse_request, req);
     656           0 :         if (type < 0)
     657           0 :                 return 0;
     658             : 
     659           0 :         r = ensure_sane_request(req, message);
     660           0 :         if (r < 0)
     661             :                 /* this only fails on critical errors */
     662           0 :                 return r;
     663             : 
     664           0 :         existing_lease = hashmap_get(server->leases_by_client_id,
     665           0 :                                      &req->client_id);
     666             : 
     667           0 :         switch(type) {
     668             :         case DHCP_DISCOVER:
     669             :         {
     670           0 :                 be32_t address = INADDR_ANY;
     671             :                 unsigned i;
     672             : 
     673           0 :                 log_dhcp_server(server, "DISCOVER (0x%x)",
     674             :                                 be32toh(req->message->xid));
     675             : 
     676           0 :                 if (!server->pool_size)
     677             :                         /* no pool allocated */
     678           0 :                         return 0;
     679             : 
     680             :                 /* for now pick a random free address from the pool */
     681           0 :                 if (existing_lease)
     682           0 :                         address = existing_lease->address;
     683             :                 else {
     684           0 :                         for (i = 0; i < server->pool_size; i++) {
     685           0 :                                 if (!server->bound_leases[server->next_offer]) {
     686           0 :                                         address = htobe32(be32toh(server->pool_start) + server->next_offer);
     687           0 :                                         break;
     688             :                                 } else
     689           0 :                                         server->next_offer = (server->next_offer + 1) % server->pool_size;
     690             :                         }
     691             :                 }
     692             : 
     693           0 :                 if (address == INADDR_ANY)
     694             :                         /* no free addresses left */
     695           0 :                         return 0;
     696             : 
     697           0 :                 r = server_send_offer(server, req, address);
     698           0 :                 if (r < 0) {
     699             :                         /* this only fails on critical errors */
     700           0 :                         log_dhcp_server(server, "could not send offer: %s",
     701             :                                         strerror(-r));
     702           0 :                         return r;
     703             :                 } else {
     704           0 :                         log_dhcp_server(server, "OFFER (0x%x)",
     705             :                                         be32toh(req->message->xid));
     706           0 :                         return DHCP_OFFER;
     707             :                 }
     708             : 
     709             :                 break;
     710             :         }
     711             :         case DHCP_DECLINE:
     712           0 :                 log_dhcp_server(server, "DECLINE (0x%x)",
     713             :                                 be32toh(req->message->xid));
     714             : 
     715             :                 /* TODO: make sure we don't offer this address again */
     716             : 
     717           0 :                 return 1;
     718             : 
     719             :                 break;
     720             :         case DHCP_REQUEST:
     721             :         {
     722             :                 be32_t address;
     723           0 :                 bool init_reboot = false;
     724             :                 int pool_offset;
     725             : 
     726             :                 /* see RFC 2131, section 4.3.2 */
     727             : 
     728           0 :                 if (req->server_id) {
     729           0 :                         log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
     730             :                                         be32toh(req->message->xid));
     731             : 
     732             :                         /* SELECTING */
     733           0 :                         if (req->server_id != server->address)
     734             :                                 /* client did not pick us */
     735           0 :                                 return 0;
     736             : 
     737           0 :                         if (req->message->ciaddr)
     738             :                                 /* this MUST be zero */
     739           0 :                                 return 0;
     740             : 
     741           0 :                         if (!req->requested_ip)
     742             :                                 /* this must be filled in with the yiaddr
     743             :                                    from the chosen OFFER */
     744           0 :                                 return 0;
     745             : 
     746           0 :                         address = req->requested_ip;
     747           0 :                 } else if (req->requested_ip) {
     748           0 :                         log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
     749             :                                         be32toh(req->message->xid));
     750             : 
     751             :                         /* INIT-REBOOT */
     752           0 :                         if (req->message->ciaddr)
     753             :                                 /* this MUST be zero */
     754           0 :                                 return 0;
     755             : 
     756             :                         /* TODO: check more carefully if IP is correct */
     757           0 :                         address = req->requested_ip;
     758           0 :                         init_reboot = true;
     759             :                 } else {
     760           0 :                         log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
     761             :                                         be32toh(req->message->xid));
     762             : 
     763             :                         /* REBINDING / RENEWING */
     764           0 :                         if (!req->message->ciaddr)
     765             :                                 /* this MUST be filled in with clients IP address */
     766           0 :                                 return 0;
     767             : 
     768           0 :                         address = req->message->ciaddr;
     769             :                 }
     770             : 
     771           0 :                 pool_offset = get_pool_offset(server, address);
     772             : 
     773             :                 /* verify that the requested address is from the pool, and either
     774             :                    owned by the current client or free */
     775           0 :                 if (pool_offset >= 0 &&
     776           0 :                     server->bound_leases[pool_offset] == existing_lease) {
     777             :                         DHCPLease *lease;
     778           0 :                         usec_t time_now = 0;
     779             : 
     780           0 :                         if (!existing_lease) {
     781           0 :                                 lease = new0(DHCPLease, 1);
     782           0 :                                 lease->address = req->requested_ip;
     783           0 :                                 lease->client_id.data = memdup(req->client_id.data,
     784           0 :                                                                req->client_id.length);
     785           0 :                                 if (!lease->client_id.data) {
     786           0 :                                         free(lease);
     787           0 :                                         return -ENOMEM;
     788             :                                 }
     789           0 :                                 lease->client_id.length = req->client_id.length;
     790           0 :                                 memcpy(&lease->chaddr, &req->message->chaddr,
     791             :                                        ETH_ALEN);
     792           0 :                                 lease->gateway = req->message->giaddr;
     793             :                         } else
     794           0 :                                 lease = existing_lease;
     795             : 
     796           0 :                         r = sd_event_now(server->event,
     797             :                                          clock_boottime_or_monotonic(),
     798             :                                          &time_now);
     799           0 :                         if (r < 0)
     800           0 :                                 time_now = now(clock_boottime_or_monotonic());
     801           0 :                         lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
     802             : 
     803           0 :                         r = server_send_ack(server, req, address);
     804           0 :                         if (r < 0) {
     805             :                                 /* this only fails on critical errors */
     806           0 :                                 log_dhcp_server(server, "could not send ack: %s",
     807             :                                                 strerror(-r));
     808             : 
     809           0 :                                 if (!existing_lease)
     810           0 :                                         dhcp_lease_free(lease);
     811             : 
     812           0 :                                 return r;
     813             :                         } else {
     814           0 :                                 log_dhcp_server(server, "ACK (0x%x)",
     815             :                                                 be32toh(req->message->xid));
     816             : 
     817           0 :                                 server->bound_leases[pool_offset] = lease;
     818           0 :                                 hashmap_put(server->leases_by_client_id,
     819           0 :                                             &lease->client_id, lease);
     820             : 
     821           0 :                                 return DHCP_ACK;
     822             :                         }
     823           0 :                 } else if (init_reboot) {
     824           0 :                         r = server_send_nak(server, req);
     825           0 :                         if (r < 0) {
     826             :                                 /* this only fails on critical errors */
     827           0 :                                 log_dhcp_server(server, "could not send nak: %s",
     828             :                                                 strerror(-r));
     829           0 :                                 return r;
     830             :                         } else {
     831           0 :                                 log_dhcp_server(server, "NAK (0x%x)",
     832             :                                                 be32toh(req->message->xid));
     833           0 :                                 return DHCP_NAK;
     834             :                         }
     835             :                 }
     836             : 
     837           0 :                 break;
     838             :         }
     839             :         case DHCP_RELEASE: {
     840             :                 int pool_offset;
     841             : 
     842           0 :                 log_dhcp_server(server, "RELEASE (0x%x)",
     843             :                                 be32toh(req->message->xid));
     844             : 
     845           0 :                 if (!existing_lease)
     846           0 :                         return 0;
     847             : 
     848           0 :                 if (existing_lease->address != req->message->ciaddr)
     849           0 :                         return 0;
     850             : 
     851           0 :                 pool_offset = get_pool_offset(server, req->message->ciaddr);
     852           0 :                 if (pool_offset < 0)
     853           0 :                         return 0;
     854             : 
     855           0 :                 if (server->bound_leases[pool_offset] == existing_lease) {
     856           0 :                         server->bound_leases[pool_offset] = NULL;
     857           0 :                         hashmap_remove(server->leases_by_client_id, existing_lease);
     858           0 :                         dhcp_lease_free(existing_lease);
     859             : 
     860           0 :                         return 1;
     861             :                 } else
     862           0 :                         return 0;
     863             :         }
     864             :         }
     865             : 
     866           0 :         return 0;
     867             : }
     868             : 
     869           0 : static int server_receive_message(sd_event_source *s, int fd,
     870             :                                   uint32_t revents, void *userdata) {
     871           0 :         _cleanup_free_ DHCPMessage *message = NULL;
     872             :         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
     873           0 :         sd_dhcp_server *server = userdata;
     874           0 :         struct iovec iov = {};
     875           0 :         struct msghdr msg = {
     876             :                 .msg_iov = &iov,
     877             :                 .msg_iovlen = 1,
     878             :                 .msg_control = cmsgbuf,
     879             :                 .msg_controllen = sizeof(cmsgbuf),
     880             :         };
     881             :         struct cmsghdr *cmsg;
     882           0 :         int buflen = 0, len, r;
     883             : 
     884           0 :         assert(server);
     885             : 
     886           0 :         r = ioctl(fd, FIONREAD, &buflen);
     887           0 :         if (r < 0)
     888           0 :                 return r;
     889           0 :         if (buflen < 0)
     890           0 :                 return -EIO;
     891             : 
     892           0 :         message = malloc0(buflen);
     893           0 :         if (!message)
     894           0 :                 return -ENOMEM;
     895             : 
     896           0 :         iov.iov_base = message;
     897           0 :         iov.iov_len = buflen;
     898             : 
     899           0 :         len = recvmsg(fd, &msg, 0);
     900           0 :         if (len < buflen)
     901           0 :                 return 0;
     902           0 :         else if ((size_t)len < sizeof(DHCPMessage))
     903           0 :                 return 0;
     904             : 
     905           0 :         CMSG_FOREACH(cmsg, &msg) {
     906           0 :                 if (cmsg->cmsg_level == IPPROTO_IP &&
     907           0 :                     cmsg->cmsg_type == IP_PKTINFO &&
     908           0 :                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
     909           0 :                         struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
     910             : 
     911             :                         /* TODO figure out if this can be done as a filter on
     912             :                          * the socket, like for IPv6 */
     913           0 :                         if (server->index != info->ipi_ifindex)
     914           0 :                                 return 0;
     915             : 
     916           0 :                         break;
     917             :                 }
     918             :         }
     919             : 
     920           0 :         return dhcp_server_handle_message(server, message, (size_t)len);
     921             : }
     922             : 
     923           2 : int sd_dhcp_server_start(sd_dhcp_server *server) {
     924             :         int r;
     925             : 
     926           2 :         assert_return(server, -EINVAL);
     927           2 :         assert_return(server->event, -EINVAL);
     928           2 :         assert_return(!server->receive_message, -EBUSY);
     929           2 :         assert_return(server->fd_raw == -1, -EBUSY);
     930           2 :         assert_return(server->fd == -1, -EBUSY);
     931           2 :         assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
     932             : 
     933           1 :         r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
     934           1 :         if (r < 0) {
     935           1 :                 r = -errno;
     936           1 :                 sd_dhcp_server_stop(server);
     937           1 :                 return r;
     938             :         }
     939           0 :         server->fd_raw = r;
     940             : 
     941           0 :         r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
     942           0 :         if (r < 0) {
     943           0 :                 sd_dhcp_server_stop(server);
     944           0 :                 return r;
     945             :         }
     946           0 :         server->fd = r;
     947             : 
     948           0 :         r = sd_event_add_io(server->event, &server->receive_message,
     949             :                             server->fd, EPOLLIN,
     950             :                             server_receive_message, server);
     951           0 :         if (r < 0) {
     952           0 :                 sd_dhcp_server_stop(server);
     953           0 :                 return r;
     954             :         }
     955             : 
     956           0 :         r = sd_event_source_set_priority(server->receive_message,
     957           0 :                                          server->event_priority);
     958           0 :         if (r < 0) {
     959           0 :                 sd_dhcp_server_stop(server);
     960           0 :                 return r;
     961             :         }
     962             : 
     963           0 :         log_dhcp_server(server, "STARTED");
     964             : 
     965           0 :         return 0;
     966             : }
     967             : 
     968           0 : int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
     969             :         unsigned i;
     970           0 :         int r = 0;
     971             : 
     972           0 :         assert_return(server, -EINVAL);
     973           0 :         assert(server->bound_leases);
     974             : 
     975           0 :         for (i = 0; i < server->pool_size; i++) {
     976           0 :                 DHCPLease *lease = server->bound_leases[i];
     977             : 
     978           0 :                 if (!lease)
     979           0 :                         continue;
     980             : 
     981           0 :                 r = server_send_forcerenew(server, lease->address,
     982             :                                            lease->gateway,
     983           0 :                                            lease->chaddr);
     984           0 :                 if (r < 0)
     985           0 :                         return r;
     986             :                 else
     987           0 :                         log_dhcp_server(server, "FORCERENEW");
     988             :         }
     989             : 
     990           0 :         return r;
     991             : }

Generated by: LCOV version 1.11