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) {
117 if (REFCNT_DEC(server->n_ref) > 0)
120 log_dhcp_server(server, "UNREF");
122 sd_dhcp_server_stop(server);
124 sd_event_unref(server->event);
126 while ((lease = hashmap_steal_first(server->leases_by_client_id)))
127 dhcp_lease_free(lease);
128 hashmap_free(server->leases_by_client_id);
130 free(server->bound_leases);
136 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
137 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
139 assert_return(ret, -EINVAL);
140 assert_return(ifindex > 0, -EINVAL);
142 server = new0(sd_dhcp_server, 1);
146 server->n_ref = REFCNT_INIT;
149 server->address = htobe32(INADDR_ANY);
150 server->index = ifindex;
151 server->leases_by_client_id = hashmap_new(client_id_hash_func, client_id_compare_func);
159 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
162 assert_return(server, -EINVAL);
163 assert_return(!server->event, -EBUSY);
166 server->event = sd_event_ref(event);
168 r = sd_event_default(&server->event);
173 server->event_priority = priority;
178 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
179 assert_return(server, -EINVAL);
181 server->event = sd_event_unref(server->event);
186 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
187 assert_return(server, NULL);
189 return server->event;
192 int sd_dhcp_server_stop(sd_dhcp_server *server) {
193 assert_return(server, -EINVAL);
195 server->receive_message =
196 sd_event_source_unref(server->receive_message);
198 server->fd_raw = safe_close(server->fd_raw);
199 server->fd = safe_close(server->fd);
201 log_dhcp_server(server, "STOPPED");
206 static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
208 union sockaddr_union link = {
209 .ll.sll_family = AF_PACKET,
210 .ll.sll_protocol = htons(ETH_P_IP),
211 .ll.sll_ifindex = server->index,
212 .ll.sll_halen = ETH_ALEN,
217 assert(server->index > 0);
218 assert(server->address);
220 assert(len > sizeof(DHCPPacket));
222 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
224 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
225 packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
227 r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
234 static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
235 DHCPMessage *message, size_t len) {
236 union sockaddr_union dest = {
237 .in.sin_family = AF_INET,
238 .in.sin_port = htobe16(DHCP_PORT_CLIENT),
239 .in.sin_addr.s_addr = destination,
245 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
246 struct msghdr msg = {
248 .msg_namelen = sizeof(dest.in),
251 .msg_control = cmsgbuf,
252 .msg_controllen = sizeof(cmsgbuf),
254 struct cmsghdr *cmsg;
255 struct in_pktinfo *pktinfo;
259 assert(server->fd > 0);
261 assert(len > sizeof(DHCPMessage));
263 cmsg = CMSG_FIRSTHDR(&msg);
266 cmsg->cmsg_level = IPPROTO_IP;
267 cmsg->cmsg_type = IP_PKTINFO;
268 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
270 /* we attach source interface and address info to the message
271 rather than binding the socket. This will be mostly useful
272 when we gain support for arbitrary number of server addresses
274 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
277 pktinfo->ipi_ifindex = server->index;
278 pktinfo->ipi_spec_dst.s_addr = server->address;
280 r = sendmsg(server->fd, &msg, 0);
287 static bool requested_broadcast(DHCPRequest *req) {
290 return req->message->flags & htobe16(0x8000);
293 int dhcp_server_send_packet(sd_dhcp_server *server,
294 DHCPRequest *req, DHCPPacket *packet,
295 int type, size_t optoffset) {
296 be32_t destination = INADDR_ANY;
301 assert(req->max_optlen);
302 assert(optoffset <= req->max_optlen);
305 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
306 DHCP_OPTION_SERVER_IDENTIFIER,
307 4, &server->address);
311 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
312 DHCP_OPTION_END, 0, NULL);
316 /* RFC 2131 Section 4.1
318 If the ’giaddr’ field in a DHCP message from a client is non-zero,
319 the server sends any return messages to the ’DHCP server’ port on the
320 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
321 field is zero and the ’ciaddr’ field is nonzero, then the server
322 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
323 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
324 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
325 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
326 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
327 messages to the client’s hardware address and ’yiaddr’ address. In
328 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
329 messages to 0xffffffff.
333 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
334 different subnet. The server MUST set the broadcast bit in the
335 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
336 client, because the client may not have a correct network address
337 or subnet mask, and the client may not be answering ARP requests.
339 if (req->message->giaddr) {
340 destination = req->message->giaddr;
341 if (type == DHCP_NAK)
342 packet->dhcp.flags = htobe16(0x8000);
343 } else if (req->message->ciaddr && type != DHCP_NAK)
344 destination = req->message->ciaddr;
346 if (destination || requested_broadcast(req) || type == DHCP_NAK)
347 return dhcp_server_send_udp(server, destination, &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");