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 bool sd_dhcp_server_is_running(sd_dhcp_server *server) {
66 assert_return(server, -EINVAL);
68 return !!server->receive_message;
71 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
73 assert_se(REFCNT_INC(server->n_ref) >= 2);
78 unsigned long client_id_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
80 const DHCPClientId *id = p;
86 siphash24((uint8_t*) &u, id->data, id->length, hash_key);
88 return (unsigned long) u;
91 int client_id_compare_func(const void *_a, const void *_b) {
92 const DHCPClientId *a, *b;
97 assert(!a->length || a->data);
98 assert(!b->length || b->data);
100 if (a->length != b->length)
101 return a->length < b->length ? -1 : 1;
103 return memcmp(a->data, b->data, a->length);
106 static void dhcp_lease_free(DHCPLease *lease) {
110 free(lease->client_id.data);
114 sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
120 if (REFCNT_DEC(server->n_ref) > 0)
123 log_dhcp_server(server, "UNREF");
125 sd_dhcp_server_stop(server);
127 sd_event_unref(server->event);
129 while ((lease = hashmap_steal_first(server->leases_by_client_id)))
130 dhcp_lease_free(lease);
131 hashmap_free(server->leases_by_client_id);
133 free(server->bound_leases);
139 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
140 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
142 assert_return(ret, -EINVAL);
143 assert_return(ifindex > 0, -EINVAL);
145 server = new0(sd_dhcp_server, 1);
149 server->n_ref = REFCNT_INIT;
152 server->address = htobe32(INADDR_ANY);
153 server->index = ifindex;
154 server->leases_by_client_id = hashmap_new(client_id_hash_func, client_id_compare_func);
162 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
165 assert_return(server, -EINVAL);
166 assert_return(!server->event, -EBUSY);
169 server->event = sd_event_ref(event);
171 r = sd_event_default(&server->event);
176 server->event_priority = priority;
181 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
182 assert_return(server, -EINVAL);
184 server->event = sd_event_unref(server->event);
189 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
190 assert_return(server, NULL);
192 return server->event;
195 int sd_dhcp_server_stop(sd_dhcp_server *server) {
196 assert_return(server, -EINVAL);
198 server->receive_message =
199 sd_event_source_unref(server->receive_message);
201 server->fd_raw = safe_close(server->fd_raw);
202 server->fd = safe_close(server->fd);
204 log_dhcp_server(server, "STOPPED");
209 static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
211 union sockaddr_union link = {
212 .ll.sll_family = AF_PACKET,
213 .ll.sll_protocol = htons(ETH_P_IP),
214 .ll.sll_ifindex = server->index,
215 .ll.sll_halen = ETH_ALEN,
220 assert(server->index > 0);
221 assert(server->address);
223 assert(len > sizeof(DHCPPacket));
225 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
227 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
228 packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
230 r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
237 static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
238 DHCPMessage *message, size_t len) {
239 union sockaddr_union dest = {
240 .in.sin_family = AF_INET,
241 .in.sin_port = htobe16(DHCP_PORT_CLIENT),
242 .in.sin_addr.s_addr = destination,
248 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
249 struct msghdr msg = {
251 .msg_namelen = sizeof(dest.in),
254 .msg_control = cmsgbuf,
255 .msg_controllen = sizeof(cmsgbuf),
257 struct cmsghdr *cmsg;
258 struct in_pktinfo *pktinfo;
262 assert(server->fd > 0);
264 assert(len > sizeof(DHCPMessage));
266 cmsg = CMSG_FIRSTHDR(&msg);
269 cmsg->cmsg_level = IPPROTO_IP;
270 cmsg->cmsg_type = IP_PKTINFO;
271 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
273 /* we attach source interface and address info to the message
274 rather than binding the socket. This will be mostly useful
275 when we gain support for arbitrary number of server addresses
277 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
280 pktinfo->ipi_ifindex = server->index;
281 pktinfo->ipi_spec_dst.s_addr = server->address;
283 r = sendmsg(server->fd, &msg, 0);
290 static bool requested_broadcast(DHCPRequest *req) {
293 return req->message->flags & htobe16(0x8000);
296 int dhcp_server_send_packet(sd_dhcp_server *server,
297 DHCPRequest *req, DHCPPacket *packet,
298 int type, size_t optoffset) {
299 be32_t destination = INADDR_ANY;
304 assert(req->max_optlen);
305 assert(optoffset <= req->max_optlen);
308 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
309 DHCP_OPTION_SERVER_IDENTIFIER,
310 4, &server->address);
314 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
315 DHCP_OPTION_END, 0, NULL);
319 /* RFC 2131 Section 4.1
321 If the ’giaddr’ field in a DHCP message from a client is non-zero,
322 the server sends any return messages to the ’DHCP server’ port on the
323 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
324 field is zero and the ’ciaddr’ field is nonzero, then the server
325 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
326 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
327 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
328 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
329 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
330 messages to the client’s hardware address and ’yiaddr’ address. In
331 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
332 messages to 0xffffffff.
336 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
337 different subnet. The server MUST set the broadcast bit in the
338 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
339 client, because the client may not have a correct network address
340 or subnet mask, and the client may not be answering ARP requests.
342 if (req->message->giaddr) {
343 destination = req->message->giaddr;
344 if (type == DHCP_NAK)
345 packet->dhcp.flags = htobe16(0x8000);
346 } else if (req->message->ciaddr && type != DHCP_NAK)
347 destination = req->message->ciaddr;
349 if (destination != INADDR_ANY)
350 return dhcp_server_send_udp(server, destination, &packet->dhcp,
351 sizeof(DHCPMessage) + optoffset);
352 else if (requested_broadcast(req) || type == DHCP_NAK)
353 return dhcp_server_send_udp(server, INADDR_BROADCAST, &packet->dhcp,
354 sizeof(DHCPMessage) + optoffset);
356 /* we cannot send UDP packet to specific MAC address when the address is
357 not yet configured, so must fall back to raw packets */
358 return dhcp_server_send_unicast_raw(server, packet,
359 sizeof(DHCPPacket) + optoffset);
362 static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
363 uint8_t type, size_t *_optoffset, DHCPRequest *req) {
364 _cleanup_free_ DHCPPacket *packet = NULL;
365 size_t optoffset = 0;
371 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
373 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
377 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
378 type, req->max_optlen, &optoffset);
382 packet->dhcp.flags = req->message->flags;
383 packet->dhcp.giaddr = req->message->giaddr;
384 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
386 *_optoffset = optoffset;
393 static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
394 _cleanup_free_ DHCPPacket *packet = NULL;
399 r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
403 packet->dhcp.yiaddr = address;
405 lease_time = htobe32(req->lifetime);
406 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
407 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
411 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
418 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
419 _cleanup_free_ DHCPPacket *packet = NULL;
424 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
428 packet->dhcp.yiaddr = address;
430 lease_time = htobe32(req->lifetime);
431 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
432 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
436 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
443 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
444 _cleanup_free_ DHCPPacket *packet = NULL;
448 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
452 r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
459 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
461 DHCPRequest *req = user_data;
466 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
468 req->lifetime = be32toh(*(be32_t*)option);
471 case DHCP_OPTION_REQUESTED_IP_ADDRESS:
473 req->requested_ip = *(be32_t*)option;
476 case DHCP_OPTION_SERVER_IDENTIFIER:
478 req->server_id = *(be32_t*)option;
481 case DHCP_OPTION_CLIENT_IDENTIFIER:
485 data = memdup(option, len);
489 free(req->client_id.data);
490 req->client_id.data = data;
491 req->client_id.length = len;
495 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
497 req->max_optlen = be16toh(*(be16_t*)option) -
498 - sizeof(DHCPPacket);
506 static void dhcp_request_free(DHCPRequest *req) {
510 free(req->client_id.data);
514 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
515 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
517 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
521 req->message = message;
523 /* set client id based on mac address if client did not send an explicit one */
524 if (!req->client_id.data) {
527 data = new0(uint8_t, ETH_ALEN + 1);
531 req->client_id.length = ETH_ALEN + 1;
532 req->client_id.data = data;
533 req->client_id.data[0] = 0x01;
534 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
537 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
538 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
541 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
546 static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
549 if (!server->pool_size)
552 if (be32toh(requested_ip) < be32toh(server->pool_start) ||
553 be32toh(requested_ip) >= be32toh(server->pool_start) +
557 return be32toh(requested_ip) - be32toh(server->pool_start);
560 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
562 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
563 DHCPLease *existing_lease;
569 if (message->op != BOOTREQUEST ||
570 message->htype != ARPHRD_ETHER ||
571 message->hlen != ETHER_ADDR_LEN)
574 req = new0(DHCPRequest, 1);
578 type = dhcp_option_parse(message, length, parse_request, req);
582 r = ensure_sane_request(req, message);
584 /* this only fails on critical errors */
587 existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
592 be32_t address = INADDR_ANY;
595 log_dhcp_server(server, "DISCOVER (0x%x)",
596 be32toh(req->message->xid));
598 if (!server->pool_size)
599 /* no pool allocated */
602 /* for now pick a random free address from the pool */
604 address = existing_lease->address;
606 for (i = 0; i < server->pool_size; i++) {
607 if (!server->bound_leases[server->next_offer]) {
608 address = htobe32(be32toh(server->pool_start) + server->next_offer);
611 server->next_offer = (server->next_offer + 1) % server->pool_size;
615 if (address == INADDR_ANY)
616 /* no free addresses left */
619 r = server_send_offer(server, req, address);
621 /* this only fails on critical errors */
622 log_dhcp_server(server, "could not send offer: %s",
626 log_dhcp_server(server, "OFFER (0x%x)",
627 be32toh(req->message->xid));
634 log_dhcp_server(server, "DECLINE (0x%x)",
635 be32toh(req->message->xid));
637 /* TODO: make sure we don't offer this address again */
645 bool init_reboot = false;
648 /* see RFC 2131, section 4.3.2 */
650 if (req->server_id) {
651 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
652 be32toh(req->message->xid));
655 if (req->server_id != server->address)
656 /* client did not pick us */
659 if (req->message->ciaddr)
660 /* this MUST be zero */
663 if (!req->requested_ip)
664 /* this must be filled in with the yiaddr
665 from the chosen OFFER */
668 address = req->requested_ip;
669 } else if (req->requested_ip) {
670 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
671 be32toh(req->message->xid));
674 if (req->message->ciaddr)
675 /* this MUST be zero */
678 /* TODO: check more carefully if IP is correct */
679 address = req->requested_ip;
682 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
683 be32toh(req->message->xid));
685 /* REBINDING / RENEWING */
686 if (!req->message->ciaddr)
687 /* this MUST be filled in with clients IP address */
690 address = req->message->ciaddr;
693 pool_offset = get_pool_offset(server, address);
695 /* verify that the requested address is from the pool, and either
696 owned by the current client or free */
697 if (pool_offset >= 0 &&
698 server->bound_leases[pool_offset] == existing_lease) {
702 if (!existing_lease) {
703 lease = new0(DHCPLease, 1);
704 lease->address = req->requested_ip;
705 lease->client_id.data = memdup(req->client_id.data,
706 req->client_id.length);
707 if (!lease->client_id.data) {
711 lease->client_id.length = req->client_id.length;
713 lease = existing_lease;
715 r = sd_event_now(server->event, CLOCK_MONOTONIC, &time_now);
717 time_now = now(CLOCK_MONOTONIC);
718 lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
720 r = server_send_ack(server, req, address);
722 /* this only fails on critical errors */
723 log_dhcp_server(server, "could not send ack: %s",
727 dhcp_lease_free(lease);
731 log_dhcp_server(server, "ACK (0x%x)",
732 be32toh(req->message->xid));
734 server->bound_leases[pool_offset] = lease;
735 hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
739 } else if (init_reboot) {
740 r = server_send_nak(server, req);
742 /* this only fails on critical errors */
743 log_dhcp_server(server, "could not send nak: %s",
747 log_dhcp_server(server, "NAK (0x%x)",
748 be32toh(req->message->xid));
758 log_dhcp_server(server, "RELEASE (0x%x)",
759 be32toh(req->message->xid));
764 if (existing_lease->address != req->message->ciaddr)
767 pool_offset = get_pool_offset(server, req->message->ciaddr);
771 if (server->bound_leases[pool_offset] == existing_lease) {
772 server->bound_leases[pool_offset] = NULL;
773 hashmap_remove(server->leases_by_client_id, existing_lease);
774 dhcp_lease_free(existing_lease);
785 static int server_receive_message(sd_event_source *s, int fd,
786 uint32_t revents, void *userdata) {
787 _cleanup_free_ DHCPMessage *message = NULL;
788 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
789 sd_dhcp_server *server = userdata;
790 struct iovec iov = {};
791 struct msghdr msg = {
794 .msg_control = cmsgbuf,
795 .msg_controllen = sizeof(cmsgbuf),
797 struct cmsghdr *cmsg;
798 int buflen = 0, len, r;
802 r = ioctl(fd, FIONREAD, &buflen);
808 message = malloc0(buflen);
812 iov.iov_base = message;
813 iov.iov_len = buflen;
815 len = recvmsg(fd, &msg, 0);
818 else if ((size_t)len < sizeof(DHCPMessage))
821 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
822 if (cmsg->cmsg_level == IPPROTO_IP &&
823 cmsg->cmsg_type == IP_PKTINFO &&
824 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
825 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
827 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
828 if (server->index != info->ipi_ifindex)
835 return dhcp_server_handle_message(server, message, (size_t)len);
838 int sd_dhcp_server_start(sd_dhcp_server *server) {
841 assert_return(server, -EINVAL);
842 assert_return(server->event, -EINVAL);
843 assert_return(!server->receive_message, -EBUSY);
844 assert_return(server->fd_raw == -1, -EBUSY);
845 assert_return(server->fd == -1, -EBUSY);
846 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
848 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
851 sd_dhcp_server_stop(server);
856 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
858 sd_dhcp_server_stop(server);
863 r = sd_event_add_io(server->event, &server->receive_message,
865 server_receive_message, server);
867 sd_dhcp_server_stop(server);
871 r = sd_event_source_set_priority(server->receive_message,
872 server->event_priority);
874 sd_dhcp_server_stop(server);
878 log_dhcp_server(server, "STARTED");