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 60
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 || requested_broadcast(req) || type == DHCP_NAK)
344 return dhcp_server_send_udp(server, destination, &packet->dhcp,
345 sizeof(DHCPMessage) + optoffset);
347 /* we cannot send UDP packet to specific MAC address when the address is
348 not yet configured, so must fall back to raw packets */
349 return dhcp_server_send_unicast_raw(server, packet,
350 sizeof(DHCPPacket) + optoffset);
353 static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
354 uint8_t type, size_t *_optoffset, DHCPRequest *req) {
355 _cleanup_free_ DHCPPacket *packet = NULL;
362 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
364 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
368 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
369 type, req->max_optlen, &optoffset);
373 packet->dhcp.flags = req->message->flags;
374 packet->dhcp.giaddr = req->message->giaddr;
375 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
377 *_optoffset = optoffset;
384 static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
385 _cleanup_free_ DHCPPacket *packet = NULL;
390 r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
394 packet->dhcp.yiaddr = address;
396 lease_time = htobe32(req->lifetime);
397 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
398 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
402 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
409 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
410 _cleanup_free_ DHCPPacket *packet = NULL;
415 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
419 packet->dhcp.yiaddr = address;
421 lease_time = htobe32(req->lifetime);
422 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
423 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
427 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
434 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
435 _cleanup_free_ DHCPPacket *packet = NULL;
439 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
443 r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
450 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
452 DHCPRequest *req = user_data;
457 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
459 req->lifetime = be32toh(*(be32_t*)option);
462 case DHCP_OPTION_REQUESTED_IP_ADDRESS:
464 req->requested_ip = *(be32_t*)option;
467 case DHCP_OPTION_SERVER_IDENTIFIER:
469 req->server_id = *(be32_t*)option;
472 case DHCP_OPTION_CLIENT_IDENTIFIER:
476 data = memdup(option, len);
480 free(req->client_id.data);
481 req->client_id.data = data;
482 req->client_id.length = len;
486 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
488 req->max_optlen = be16toh(*(be16_t*)option) -
489 - sizeof(DHCPPacket);
497 static void dhcp_request_free(DHCPRequest *req) {
501 free(req->client_id.data);
505 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
506 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
508 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
512 req->message = message;
514 /* set client id based on mac address if client did not send an explicit one */
515 if (!req->client_id.data) {
518 data = new0(uint8_t, ETH_ALEN + 1);
522 req->client_id.length = ETH_ALEN + 1;
523 req->client_id.data = data;
524 req->client_id.data[0] = 0x01;
525 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
528 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
529 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
532 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
537 static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
540 if (!server->pool_size)
543 if (be32toh(requested_ip) < be32toh(server->pool_start) ||
544 be32toh(requested_ip) >= be32toh(server->pool_start) +
548 return be32toh(requested_ip) - be32toh(server->pool_start);
551 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
553 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
554 DHCPLease *existing_lease;
560 if (message->op != BOOTREQUEST ||
561 message->htype != ARPHRD_ETHER ||
562 message->hlen != ETHER_ADDR_LEN)
565 req = new0(DHCPRequest, 1);
569 type = dhcp_option_parse(message, length, parse_request, req);
573 r = ensure_sane_request(req, message);
575 /* this only fails on critical errors */
578 existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
583 be32_t address = INADDR_ANY;
586 log_dhcp_server(server, "DISCOVER (0x%x)",
587 be32toh(req->message->xid));
589 if (!server->pool_size)
590 /* no pool allocated */
593 /* for now pick a random free address from the pool */
595 address = existing_lease->address;
597 for (i = 0; i < server->pool_size; i++) {
598 if (!server->bound_leases[server->next_offer]) {
599 address = htobe32(be32toh(server->pool_start) + server->next_offer);
602 server->next_offer = (server->next_offer + 1) % server->pool_size;
606 if (address == INADDR_ANY)
607 /* no free addresses left */
610 r = server_send_offer(server, req, address);
612 /* this only fails on critical errors */
613 log_dhcp_server(server, "could not send offer: %s",
617 log_dhcp_server(server, "OFFER (0x%x)",
618 be32toh(req->message->xid));
625 log_dhcp_server(server, "DECLINE (0x%x)",
626 be32toh(req->message->xid));
628 /* TODO: make sure we don't offer this address again */
636 bool init_reboot = false;
639 /* see RFC 2131, section 4.3.2 */
641 if (req->server_id) {
642 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
643 be32toh(req->message->xid));
646 if (req->server_id != server->address)
647 /* client did not pick us */
650 if (req->message->ciaddr)
651 /* this MUST be zero */
654 if (!req->requested_ip)
655 /* this must be filled in with the yiaddr
656 from the chosen OFFER */
659 address = req->requested_ip;
660 } else if (req->requested_ip) {
661 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
662 be32toh(req->message->xid));
665 if (req->message->ciaddr)
666 /* this MUST be zero */
669 /* TODO: check more carefully if IP is correct */
670 address = req->requested_ip;
673 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
674 be32toh(req->message->xid));
676 /* REBINDING / RENEWING */
677 if (!req->message->ciaddr)
678 /* this MUST be filled in with clients IP address */
681 address = req->message->ciaddr;
684 pool_offset = get_pool_offset(server, address);
686 /* verify that the requested address is from the pool, and either
687 owned by the current client or free */
688 if (pool_offset >= 0 &&
689 server->bound_leases[pool_offset] == existing_lease) {
693 if (!existing_lease) {
694 lease = new0(DHCPLease, 1);
695 lease->address = req->requested_ip;
696 lease->client_id.data = memdup(req->client_id.data,
697 req->client_id.length);
698 if (!lease->client_id.data) {
702 lease->client_id.length = req->client_id.length;
704 lease = existing_lease;
706 r = sd_event_now(server->event, CLOCK_MONOTONIC, &time_now);
708 time_now = now(CLOCK_MONOTONIC);
709 lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
711 r = server_send_ack(server, req, address);
713 /* this only fails on critical errors */
714 log_dhcp_server(server, "could not send ack: %s",
718 dhcp_lease_free(lease);
722 log_dhcp_server(server, "ACK (0x%x)",
723 be32toh(req->message->xid));
725 server->bound_leases[pool_offset] = lease;
726 hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
730 } else if (init_reboot) {
731 r = server_send_nak(server, req);
733 /* this only fails on critical errors */
734 log_dhcp_server(server, "could not send nak: %s",
738 log_dhcp_server(server, "NAK (0x%x)",
739 be32toh(req->message->xid));
749 log_dhcp_server(server, "RELEASE (0x%x)",
750 be32toh(req->message->xid));
755 if (existing_lease->address != req->message->ciaddr)
758 pool_offset = get_pool_offset(server, req->message->ciaddr);
762 if (server->bound_leases[pool_offset] == existing_lease) {
763 server->bound_leases[pool_offset] = NULL;
764 hashmap_remove(server->leases_by_client_id, existing_lease);
765 dhcp_lease_free(existing_lease);
776 static int server_receive_message(sd_event_source *s, int fd,
777 uint32_t revents, void *userdata) {
778 _cleanup_free_ DHCPMessage *message = NULL;
779 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
780 sd_dhcp_server *server = userdata;
781 struct iovec iov = {};
782 struct msghdr msg = {
785 .msg_control = cmsgbuf,
786 .msg_controllen = sizeof(cmsgbuf),
788 struct cmsghdr *cmsg;
789 int buflen = 0, len, r;
793 r = ioctl(fd, FIONREAD, &buflen);
799 message = malloc0(buflen);
803 iov.iov_base = message;
804 iov.iov_len = buflen;
806 len = recvmsg(fd, &msg, 0);
809 else if ((size_t)len < sizeof(DHCPMessage))
812 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
813 if (cmsg->cmsg_level == IPPROTO_IP &&
814 cmsg->cmsg_type == IP_PKTINFO &&
815 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
816 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
818 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
819 if (server->index != info->ipi_ifindex)
826 return dhcp_server_handle_message(server, message, (size_t)len);
829 int sd_dhcp_server_start(sd_dhcp_server *server) {
832 assert_return(server, -EINVAL);
833 assert_return(server->event, -EINVAL);
834 assert_return(!server->receive_message, -EBUSY);
835 assert_return(server->fd_raw == -1, -EBUSY);
836 assert_return(server->fd == -1, -EBUSY);
837 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
839 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
842 sd_dhcp_server_stop(server);
847 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
849 sd_dhcp_server_stop(server);
854 r = sd_event_add_io(server->event, &server->receive_message,
856 server_receive_message, server);
858 sd_dhcp_server_stop(server);
862 r = sd_event_source_set_priority(server->receive_message,
863 server->event_priority);
865 sd_dhcp_server_stop(server);
869 log_dhcp_server(server, "STARTED");