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 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, dhcp_lease_free);
109 #define _cleanup_dhcp_lease_free_ _cleanup_(dhcp_lease_freep)
111 sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
112 if (server && REFCNT_DEC(server->n_ref) <= 0) {
116 log_dhcp_server(server, "UNREF");
118 sd_dhcp_server_stop(server);
120 sd_event_unref(server->event);
122 HASHMAP_FOREACH(lease, server->leases_by_client_id, i) {
123 hashmap_remove(server->leases_by_client_id, lease);
124 dhcp_lease_free(lease);
127 hashmap_free(server->leases_by_client_id);
128 free(server->bound_leases);
135 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
136 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
138 assert_return(ret, -EINVAL);
139 assert_return(ifindex > 0, -EINVAL);
141 server = new0(sd_dhcp_server, 1);
145 server->n_ref = REFCNT_INIT;
148 server->address = htobe32(INADDR_ANY);
149 server->index = ifindex;
150 server->leases_by_client_id = hashmap_new(client_id_hash_func, client_id_compare_func);
158 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
161 assert_return(server, -EINVAL);
162 assert_return(!server->event, -EBUSY);
165 server->event = sd_event_ref(event);
167 r = sd_event_default(&server->event);
172 server->event_priority = priority;
177 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
178 assert_return(server, -EINVAL);
180 server->event = sd_event_unref(server->event);
185 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
186 assert_return(server, NULL);
188 return server->event;
191 int sd_dhcp_server_stop(sd_dhcp_server *server) {
192 assert_return(server, -EINVAL);
194 server->receive_message =
195 sd_event_source_unref(server->receive_message);
197 server->fd_raw = safe_close(server->fd_raw);
198 server->fd = safe_close(server->fd);
200 log_dhcp_server(server, "STOPPED");
205 static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
207 union sockaddr_union link = {
208 .ll.sll_family = AF_PACKET,
209 .ll.sll_protocol = htons(ETH_P_IP),
210 .ll.sll_ifindex = server->index,
211 .ll.sll_halen = ETH_ALEN,
216 assert(server->index > 0);
217 assert(server->address);
219 assert(len > sizeof(DHCPPacket));
221 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
223 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
224 packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
226 r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
233 static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
234 DHCPMessage *message, size_t len) {
235 union sockaddr_union dest = {
236 .in.sin_family = AF_INET,
237 .in.sin_port = htobe16(DHCP_PORT_CLIENT),
238 .in.sin_addr.s_addr = destination,
244 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
245 struct msghdr msg = {
247 .msg_namelen = sizeof(dest.in),
250 .msg_control = cmsgbuf,
251 .msg_controllen = sizeof(cmsgbuf),
253 struct cmsghdr *cmsg;
254 struct in_pktinfo *pktinfo;
258 assert(server->fd > 0);
260 assert(len > sizeof(DHCPMessage));
262 cmsg = CMSG_FIRSTHDR(&msg);
265 cmsg->cmsg_level = IPPROTO_IP;
266 cmsg->cmsg_type = IP_PKTINFO;
267 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
269 /* we attach source interface and address info to the message
270 rather than binding the socket. This will be mostly useful
271 when we gain support for arbitrary number of server addresses
273 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
276 pktinfo->ipi_ifindex = server->index;
277 pktinfo->ipi_spec_dst.s_addr = server->address;
279 r = sendmsg(server->fd, &msg, 0);
286 static bool requested_broadcast(DHCPRequest *req) {
289 return req->message->flags & htobe16(0x8000);
292 int dhcp_server_send_packet(sd_dhcp_server *server,
293 DHCPRequest *req, DHCPPacket *packet,
294 int type, size_t optoffset) {
295 be32_t destination = INADDR_ANY;
300 assert(req->max_optlen);
301 assert(optoffset <= req->max_optlen);
304 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
305 DHCP_OPTION_SERVER_IDENTIFIER,
306 4, &server->address);
310 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
311 DHCP_OPTION_END, 0, NULL);
315 /* RFC 2131 Section 4.1
317 If the ’giaddr’ field in a DHCP message from a client is non-zero,
318 the server sends any return messages to the ’DHCP server’ port on the
319 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
320 field is zero and the ’ciaddr’ field is nonzero, then the server
321 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
322 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
323 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
324 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
325 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
326 messages to the client’s hardware address and ’yiaddr’ address. In
327 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
328 messages to 0xffffffff.
332 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
333 different subnet. The server MUST set the broadcast bit in the
334 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
335 client, because the client may not have a correct network address
336 or subnet mask, and the client may not be answering ARP requests.
338 if (req->message->giaddr) {
339 destination = req->message->giaddr;
340 if (type == DHCP_NAK)
341 packet->dhcp.flags = htobe16(0x8000);
342 } else if (req->message->ciaddr && type != DHCP_NAK)
343 destination = req->message->ciaddr;
345 if (destination || requested_broadcast(req) || type == DHCP_NAK)
346 return dhcp_server_send_udp(server, destination, &packet->dhcp,
347 sizeof(DHCPMessage) + optoffset);
349 /* we cannot send UDP packet to specific MAC address when the address is
350 not yet configured, so must fall back to raw packets */
351 return dhcp_server_send_unicast_raw(server, packet,
352 sizeof(DHCPPacket) + optoffset);
355 static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
356 uint8_t type, size_t *_optoffset, DHCPRequest *req) {
357 _cleanup_free_ DHCPPacket *packet = NULL;
364 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
366 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
370 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
371 type, req->max_optlen, &optoffset);
375 packet->dhcp.flags = req->message->flags;
376 packet->dhcp.giaddr = req->message->giaddr;
377 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
379 *_optoffset = optoffset;
386 static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
387 _cleanup_free_ DHCPPacket *packet = NULL;
392 r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
396 packet->dhcp.yiaddr = address;
398 lease_time = htobe32(req->lifetime);
399 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
400 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
404 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
411 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
412 _cleanup_free_ DHCPPacket *packet = NULL;
417 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
421 packet->dhcp.yiaddr = address;
423 lease_time = htobe32(req->lifetime);
424 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
425 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
429 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
436 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
437 _cleanup_free_ DHCPPacket *packet = NULL;
441 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
445 r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
452 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
454 DHCPRequest *req = user_data;
459 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
461 req->lifetime = be32toh(*(be32_t*)option);
464 case DHCP_OPTION_REQUESTED_IP_ADDRESS:
466 req->requested_ip = *(be32_t*)option;
469 case DHCP_OPTION_SERVER_IDENTIFIER:
471 req->server_id = *(be32_t*)option;
474 case DHCP_OPTION_CLIENT_IDENTIFIER:
478 data = memdup(option, len);
482 free(req->client_id.data);
483 req->client_id.data = data;
484 req->client_id.length = len;
488 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
490 req->max_optlen = be16toh(*(be16_t*)option) -
491 - sizeof(DHCPPacket);
499 static void dhcp_request_free(DHCPRequest *req) {
503 free(req->client_id.data);
507 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
508 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
510 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
514 req->message = message;
516 /* set client id based on mac address if client did not send an explicit one */
517 if (!req->client_id.data) {
520 data = new0(uint8_t, ETH_ALEN + 1);
524 req->client_id.length = ETH_ALEN + 1;
525 req->client_id.data = data;
526 req->client_id.data[0] = 0x01;
527 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
530 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
531 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
534 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
539 static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
542 if (!server->pool_size)
545 if (be32toh(requested_ip) < be32toh(server->pool_start) ||
546 be32toh(requested_ip) >= be32toh(server->pool_start) +
550 return be32toh(requested_ip) - be32toh(server->pool_start);
553 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
555 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
556 DHCPLease *existing_lease;
562 if (message->op != BOOTREQUEST ||
563 message->htype != ARPHRD_ETHER ||
564 message->hlen != ETHER_ADDR_LEN)
567 req = new0(DHCPRequest, 1);
571 type = dhcp_option_parse(message, length, parse_request, req);
575 r = ensure_sane_request(req, message);
577 /* this only fails on critical errors */
580 existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
585 be32_t address = INADDR_ANY;
588 log_dhcp_server(server, "DISCOVER (0x%x)",
589 be32toh(req->message->xid));
591 if (!server->pool_size)
592 /* no pool allocated */
595 /* for now pick a random free address from the pool */
597 address = existing_lease->address;
599 for (i = 0; i < server->pool_size; i++) {
600 if (!server->bound_leases[server->next_offer]) {
601 address = htobe32(be32toh(server->pool_start) + server->next_offer);
604 server->next_offer = (server->next_offer + 1) % server->pool_size;
608 if (address == INADDR_ANY)
609 /* no free addresses left */
612 r = server_send_offer(server, req, address);
614 /* this only fails on critical errors */
615 log_dhcp_server(server, "could not send offer: %s",
619 log_dhcp_server(server, "OFFER (0x%x)",
620 be32toh(req->message->xid));
629 bool init_reboot = false;
632 /* see RFC 2131, section 4.3.2 */
634 if (req->server_id) {
635 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
636 be32toh(req->message->xid));
639 if (req->server_id != server->address)
640 /* client did not pick us */
643 if (req->message->ciaddr)
644 /* this MUST be zero */
647 if (!req->requested_ip)
648 /* this must be filled in with the yiaddr
649 from the chosen OFFER */
652 address = req->requested_ip;
653 } else if (req->requested_ip) {
654 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
655 be32toh(req->message->xid));
658 if (req->message->ciaddr)
659 /* this MUST be zero */
662 /* TODO: check more carefully if IP is correct */
663 address = req->requested_ip;
666 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
667 be32toh(req->message->xid));
669 /* REBINDING / RENEWING */
670 if (!req->message->ciaddr)
671 /* this MUST be filled in with clients IP address */
674 address = req->message->ciaddr;
677 pool_offset = get_pool_offset(server, address);
679 /* verify that the requested address is from the pool, and either
680 owned by the current client or free */
681 if (pool_offset >= 0 &&
682 server->bound_leases[pool_offset] == existing_lease) {
686 if (!existing_lease) {
687 lease = new0(DHCPLease, 1);
688 lease->address = req->requested_ip;
689 lease->client_id.data = memdup(req->client_id.data,
690 req->client_id.length);
691 if (!lease->client_id.data)
693 lease->client_id.length = req->client_id.length;
695 lease = existing_lease;
697 r = sd_event_now(server->event, CLOCK_MONOTONIC, &time_now);
699 time_now = now(CLOCK_MONOTONIC);
700 lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
702 r = server_send_ack(server, req, address);
704 /* this only fails on critical errors */
705 log_dhcp_server(server, "could not send ack: %s",
709 dhcp_lease_free(lease);
713 log_dhcp_server(server, "ACK (0x%x)",
714 be32toh(req->message->xid));
716 server->bound_leases[pool_offset] = lease;
717 hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
721 } else if (init_reboot) {
722 r = server_send_nak(server, req);
724 /* this only fails on critical errors */
725 log_dhcp_server(server, "could not send nak: %s",
729 log_dhcp_server(server, "NAK (0x%x)",
730 be32toh(req->message->xid));
742 static int server_receive_message(sd_event_source *s, int fd,
743 uint32_t revents, void *userdata) {
744 _cleanup_free_ DHCPMessage *message = NULL;
745 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
746 sd_dhcp_server *server = userdata;
747 struct iovec iov = {};
748 struct msghdr msg = {
751 .msg_control = cmsgbuf,
752 .msg_controllen = sizeof(cmsgbuf),
754 struct cmsghdr *cmsg;
755 int buflen = 0, len, r;
759 r = ioctl(fd, FIONREAD, &buflen);
765 message = malloc0(buflen);
769 iov.iov_base = message;
770 iov.iov_len = buflen;
772 len = recvmsg(fd, &msg, 0);
775 else if ((size_t)len < sizeof(DHCPMessage))
778 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
779 if (cmsg->cmsg_level == IPPROTO_IP &&
780 cmsg->cmsg_type == IP_PKTINFO &&
781 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
782 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
784 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
785 if (server->index != info->ipi_ifindex)
792 return dhcp_server_handle_message(server, message, (size_t)len);
795 int sd_dhcp_server_start(sd_dhcp_server *server) {
798 assert_return(server, -EINVAL);
799 assert_return(server->event, -EINVAL);
800 assert_return(!server->receive_message, -EBUSY);
801 assert_return(server->fd_raw == -1, -EBUSY);
802 assert_return(server->fd == -1, -EBUSY);
803 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
805 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
808 sd_dhcp_server_stop(server);
813 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
815 sd_dhcp_server_stop(server);
820 r = sd_event_add_io(server->event, &server->receive_message,
822 server_receive_message, server);
824 sd_dhcp_server_stop(server);
828 r = sd_event_source_set_priority(server->receive_message,
829 server->event_priority);
831 sd_dhcp_server_stop(server);
835 log_dhcp_server(server, "STARTED");