1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2013 Intel Corporation. All rights reserved.
7 Copyright (C) 2014 Tom Gundersen
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.
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.
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/>.
23 #include <sys/ioctl.h>
24 #include <netinet/if_ether.h>
26 #include "siphash24.h"
28 #include "sd-dhcp-server.h"
29 #include "dhcp-server-internal.h"
30 #include "dhcp-internal.h"
32 #define DHCP_DEFAULT_LEASE_TIME 3600 /* one hour */
34 int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *address,
36 assert_return(server, -EINVAL);
37 assert_return(address, -EINVAL);
38 assert_return(address->s_addr, -EINVAL);
39 assert_return(size, -EINVAL);
40 assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
41 assert_return(!server->pool_size, -EBUSY);
42 assert_return(!server->bound_leases, -EBUSY);
44 server->bound_leases = new0(DHCPLease*, size);
45 if (!server->bound_leases)
48 server->pool_start = address->s_addr;
49 server->pool_size = size;
54 int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address) {
55 assert_return(server, -EINVAL);
56 assert_return(address, -EINVAL);
57 assert_return(address->s_addr, -EINVAL);
58 assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
60 server->address = address->s_addr;
65 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
67 assert_se(REFCNT_INC(server->n_ref) >= 2);
72 unsigned long client_id_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
74 const DHCPClientId *id = p;
80 siphash24((uint8_t*) &u, id->data, id->length, hash_key);
82 return (unsigned long) u;
85 int client_id_compare_func(const void *_a, const void *_b) {
86 const DHCPClientId *a, *b;
91 assert(!a->length || a->data);
92 assert(!b->length || b->data);
94 if (a->length != b->length)
95 return a->length < b->length ? -1 : 1;
97 return memcmp(a->data, b->data, a->length);
100 static void dhcp_lease_free(DHCPLease *lease) {
104 free(lease->client_id.data);
108 sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
114 if (REFCNT_DEC(server->n_ref) > 0)
117 log_dhcp_server(server, "UNREF");
119 sd_dhcp_server_stop(server);
121 sd_event_unref(server->event);
123 while ((lease = hashmap_steal_first(server->leases_by_client_id)))
124 dhcp_lease_free(lease);
125 hashmap_free(server->leases_by_client_id);
127 free(server->bound_leases);
133 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
134 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
136 assert_return(ret, -EINVAL);
137 assert_return(ifindex > 0, -EINVAL);
139 server = new0(sd_dhcp_server, 1);
143 server->n_ref = REFCNT_INIT;
146 server->address = htobe32(INADDR_ANY);
147 server->index = ifindex;
148 server->leases_by_client_id = hashmap_new(client_id_hash_func, client_id_compare_func);
156 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
159 assert_return(server, -EINVAL);
160 assert_return(!server->event, -EBUSY);
163 server->event = sd_event_ref(event);
165 r = sd_event_default(&server->event);
170 server->event_priority = priority;
175 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
176 assert_return(server, -EINVAL);
178 server->event = sd_event_unref(server->event);
183 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
184 assert_return(server, NULL);
186 return server->event;
189 int sd_dhcp_server_stop(sd_dhcp_server *server) {
190 assert_return(server, -EINVAL);
192 server->receive_message =
193 sd_event_source_unref(server->receive_message);
195 server->fd_raw = safe_close(server->fd_raw);
196 server->fd = safe_close(server->fd);
198 log_dhcp_server(server, "STOPPED");
203 static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
205 union sockaddr_union link = {
206 .ll.sll_family = AF_PACKET,
207 .ll.sll_protocol = htons(ETH_P_IP),
208 .ll.sll_ifindex = server->index,
209 .ll.sll_halen = ETH_ALEN,
214 assert(server->index > 0);
215 assert(server->address);
217 assert(len > sizeof(DHCPPacket));
219 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
221 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
222 packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
224 r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
231 static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
232 DHCPMessage *message, size_t len) {
233 union sockaddr_union dest = {
234 .in.sin_family = AF_INET,
235 .in.sin_port = htobe16(DHCP_PORT_CLIENT),
236 .in.sin_addr.s_addr = destination,
242 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
243 struct msghdr msg = {
245 .msg_namelen = sizeof(dest.in),
248 .msg_control = cmsgbuf,
249 .msg_controllen = sizeof(cmsgbuf),
251 struct cmsghdr *cmsg;
252 struct in_pktinfo *pktinfo;
256 assert(server->fd > 0);
258 assert(len > sizeof(DHCPMessage));
260 cmsg = CMSG_FIRSTHDR(&msg);
263 cmsg->cmsg_level = IPPROTO_IP;
264 cmsg->cmsg_type = IP_PKTINFO;
265 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
267 /* we attach source interface and address info to the message
268 rather than binding the socket. This will be mostly useful
269 when we gain support for arbitrary number of server addresses
271 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
274 pktinfo->ipi_ifindex = server->index;
275 pktinfo->ipi_spec_dst.s_addr = server->address;
277 r = sendmsg(server->fd, &msg, 0);
284 static bool requested_broadcast(DHCPRequest *req) {
287 return req->message->flags & htobe16(0x8000);
290 int dhcp_server_send_packet(sd_dhcp_server *server,
291 DHCPRequest *req, DHCPPacket *packet,
292 int type, size_t optoffset) {
293 be32_t destination = INADDR_ANY;
298 assert(req->max_optlen);
299 assert(optoffset <= req->max_optlen);
302 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
303 DHCP_OPTION_SERVER_IDENTIFIER,
304 4, &server->address);
308 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
309 DHCP_OPTION_END, 0, NULL);
313 /* RFC 2131 Section 4.1
315 If the ’giaddr’ field in a DHCP message from a client is non-zero,
316 the server sends any return messages to the ’DHCP server’ port on the
317 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
318 field is zero and the ’ciaddr’ field is nonzero, then the server
319 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
320 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
321 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
322 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
323 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
324 messages to the client’s hardware address and ’yiaddr’ address. In
325 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
326 messages to 0xffffffff.
330 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
331 different subnet. The server MUST set the broadcast bit in the
332 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
333 client, because the client may not have a correct network address
334 or subnet mask, and the client may not be answering ARP requests.
336 if (req->message->giaddr) {
337 destination = req->message->giaddr;
338 if (type == DHCP_NAK)
339 packet->dhcp.flags = htobe16(0x8000);
340 } else if (req->message->ciaddr && type != DHCP_NAK)
341 destination = req->message->ciaddr;
343 if (destination != INADDR_ANY)
344 return dhcp_server_send_udp(server, destination, &packet->dhcp,
345 sizeof(DHCPMessage) + optoffset);
346 else if (requested_broadcast(req) || type == DHCP_NAK)
347 return dhcp_server_send_udp(server, INADDR_BROADCAST, &packet->dhcp,
348 sizeof(DHCPMessage) + optoffset);
350 /* we cannot send UDP packet to specific MAC address when the address is
351 not yet configured, so must fall back to raw packets */
352 return dhcp_server_send_unicast_raw(server, packet,
353 sizeof(DHCPPacket) + optoffset);
356 static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
357 uint8_t type, size_t *_optoffset, DHCPRequest *req) {
358 _cleanup_free_ DHCPPacket *packet = NULL;
365 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
367 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
371 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
372 type, req->max_optlen, &optoffset);
376 packet->dhcp.flags = req->message->flags;
377 packet->dhcp.giaddr = req->message->giaddr;
378 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
380 *_optoffset = optoffset;
387 static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
388 _cleanup_free_ DHCPPacket *packet = NULL;
393 r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
397 packet->dhcp.yiaddr = address;
399 lease_time = htobe32(req->lifetime);
400 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
401 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
405 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
412 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
413 _cleanup_free_ DHCPPacket *packet = NULL;
418 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
422 packet->dhcp.yiaddr = address;
424 lease_time = htobe32(req->lifetime);
425 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
426 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
430 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
437 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
438 _cleanup_free_ DHCPPacket *packet = NULL;
442 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
446 r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
453 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
455 DHCPRequest *req = user_data;
460 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
462 req->lifetime = be32toh(*(be32_t*)option);
465 case DHCP_OPTION_REQUESTED_IP_ADDRESS:
467 req->requested_ip = *(be32_t*)option;
470 case DHCP_OPTION_SERVER_IDENTIFIER:
472 req->server_id = *(be32_t*)option;
475 case DHCP_OPTION_CLIENT_IDENTIFIER:
479 data = memdup(option, len);
483 free(req->client_id.data);
484 req->client_id.data = data;
485 req->client_id.length = len;
489 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
491 req->max_optlen = be16toh(*(be16_t*)option) -
492 - sizeof(DHCPPacket);
500 static void dhcp_request_free(DHCPRequest *req) {
504 free(req->client_id.data);
508 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
509 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
511 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
515 req->message = message;
517 /* set client id based on mac address if client did not send an explicit one */
518 if (!req->client_id.data) {
521 data = new0(uint8_t, ETH_ALEN + 1);
525 req->client_id.length = ETH_ALEN + 1;
526 req->client_id.data = data;
527 req->client_id.data[0] = 0x01;
528 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
531 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
532 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
535 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
540 static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
543 if (!server->pool_size)
546 if (be32toh(requested_ip) < be32toh(server->pool_start) ||
547 be32toh(requested_ip) >= be32toh(server->pool_start) +
551 return be32toh(requested_ip) - be32toh(server->pool_start);
554 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
556 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
557 DHCPLease *existing_lease;
563 if (message->op != BOOTREQUEST ||
564 message->htype != ARPHRD_ETHER ||
565 message->hlen != ETHER_ADDR_LEN)
568 req = new0(DHCPRequest, 1);
572 type = dhcp_option_parse(message, length, parse_request, req);
576 r = ensure_sane_request(req, message);
578 /* this only fails on critical errors */
581 existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
586 be32_t address = INADDR_ANY;
589 log_dhcp_server(server, "DISCOVER (0x%x)",
590 be32toh(req->message->xid));
592 if (!server->pool_size)
593 /* no pool allocated */
596 /* for now pick a random free address from the pool */
598 address = existing_lease->address;
600 for (i = 0; i < server->pool_size; i++) {
601 if (!server->bound_leases[server->next_offer]) {
602 address = htobe32(be32toh(server->pool_start) + server->next_offer);
605 server->next_offer = (server->next_offer + 1) % server->pool_size;
609 if (address == INADDR_ANY)
610 /* no free addresses left */
613 r = server_send_offer(server, req, address);
615 /* this only fails on critical errors */
616 log_dhcp_server(server, "could not send offer: %s",
620 log_dhcp_server(server, "OFFER (0x%x)",
621 be32toh(req->message->xid));
628 log_dhcp_server(server, "DECLINE (0x%x)",
629 be32toh(req->message->xid));
631 /* TODO: make sure we don't offer this address again */
639 bool init_reboot = false;
642 /* see RFC 2131, section 4.3.2 */
644 if (req->server_id) {
645 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
646 be32toh(req->message->xid));
649 if (req->server_id != server->address)
650 /* client did not pick us */
653 if (req->message->ciaddr)
654 /* this MUST be zero */
657 if (!req->requested_ip)
658 /* this must be filled in with the yiaddr
659 from the chosen OFFER */
662 address = req->requested_ip;
663 } else if (req->requested_ip) {
664 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
665 be32toh(req->message->xid));
668 if (req->message->ciaddr)
669 /* this MUST be zero */
672 /* TODO: check more carefully if IP is correct */
673 address = req->requested_ip;
676 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
677 be32toh(req->message->xid));
679 /* REBINDING / RENEWING */
680 if (!req->message->ciaddr)
681 /* this MUST be filled in with clients IP address */
684 address = req->message->ciaddr;
687 pool_offset = get_pool_offset(server, address);
689 /* verify that the requested address is from the pool, and either
690 owned by the current client or free */
691 if (pool_offset >= 0 &&
692 server->bound_leases[pool_offset] == existing_lease) {
696 if (!existing_lease) {
697 lease = new0(DHCPLease, 1);
698 lease->address = req->requested_ip;
699 lease->client_id.data = memdup(req->client_id.data,
700 req->client_id.length);
701 if (!lease->client_id.data) {
705 lease->client_id.length = req->client_id.length;
707 lease = existing_lease;
709 r = sd_event_now(server->event, CLOCK_MONOTONIC, &time_now);
711 time_now = now(CLOCK_MONOTONIC);
712 lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
714 r = server_send_ack(server, req, address);
716 /* this only fails on critical errors */
717 log_dhcp_server(server, "could not send ack: %s",
721 dhcp_lease_free(lease);
725 log_dhcp_server(server, "ACK (0x%x)",
726 be32toh(req->message->xid));
728 server->bound_leases[pool_offset] = lease;
729 hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
733 } else if (init_reboot) {
734 r = server_send_nak(server, req);
736 /* this only fails on critical errors */
737 log_dhcp_server(server, "could not send nak: %s",
741 log_dhcp_server(server, "NAK (0x%x)",
742 be32toh(req->message->xid));
752 log_dhcp_server(server, "RELEASE (0x%x)",
753 be32toh(req->message->xid));
758 if (existing_lease->address != req->message->ciaddr)
761 pool_offset = get_pool_offset(server, req->message->ciaddr);
765 if (server->bound_leases[pool_offset] == existing_lease) {
766 server->bound_leases[pool_offset] = NULL;
767 hashmap_remove(server->leases_by_client_id, existing_lease);
768 dhcp_lease_free(existing_lease);
779 static int server_receive_message(sd_event_source *s, int fd,
780 uint32_t revents, void *userdata) {
781 _cleanup_free_ DHCPMessage *message = NULL;
782 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
783 sd_dhcp_server *server = userdata;
784 struct iovec iov = {};
785 struct msghdr msg = {
788 .msg_control = cmsgbuf,
789 .msg_controllen = sizeof(cmsgbuf),
791 struct cmsghdr *cmsg;
792 int buflen = 0, len, r;
796 r = ioctl(fd, FIONREAD, &buflen);
802 message = malloc0(buflen);
806 iov.iov_base = message;
807 iov.iov_len = buflen;
809 len = recvmsg(fd, &msg, 0);
812 else if ((size_t)len < sizeof(DHCPMessage))
815 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
816 if (cmsg->cmsg_level == IPPROTO_IP &&
817 cmsg->cmsg_type == IP_PKTINFO &&
818 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
819 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
821 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
822 if (server->index != info->ipi_ifindex)
829 return dhcp_server_handle_message(server, message, (size_t)len);
832 int sd_dhcp_server_start(sd_dhcp_server *server) {
835 assert_return(server, -EINVAL);
836 assert_return(server->event, -EINVAL);
837 assert_return(!server->receive_message, -EBUSY);
838 assert_return(server->fd_raw == -1, -EBUSY);
839 assert_return(server->fd == -1, -EBUSY);
840 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
842 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
845 sd_dhcp_server_stop(server);
850 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
852 sd_dhcp_server_stop(server);
857 r = sd_event_add_io(server->event, &server->receive_message,
859 server_receive_message, server);
861 sd_dhcp_server_stop(server);
865 r = sd_event_source_set_priority(server->receive_message,
866 server->event_priority);
868 sd_dhcp_server_stop(server);
872 log_dhcp_server(server, "STARTED");