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 "sd-dhcp-server.h"
27 #include "dhcp-server-internal.h"
28 #include "dhcp-internal.h"
30 #define DHCP_DEFAULT_LEASE_TIME 60
32 int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *address,
34 assert_return(server, -EINVAL);
35 assert_return(address, -EINVAL);
36 assert_return(address->s_addr, -EINVAL);
37 assert_return(size, -EINVAL);
38 assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
39 assert_return(!server->pool_size, -EBUSY);
41 server->pool_start = address->s_addr;
42 server->pool_size = size;
47 int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address) {
48 assert_return(server, -EINVAL);
49 assert_return(address, -EINVAL);
50 assert_return(address->s_addr, -EINVAL);
51 assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
53 server->address = address->s_addr;
58 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
60 assert_se(REFCNT_INC(server->n_ref) >= 2);
65 sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
66 if (server && REFCNT_DEC(server->n_ref) <= 0) {
67 log_dhcp_server(server, "UNREF");
69 sd_dhcp_server_stop(server);
71 sd_event_unref(server->event);
78 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
79 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
81 assert_return(ret, -EINVAL);
82 assert_return(ifindex > 0, -EINVAL);
84 server = new0(sd_dhcp_server, 1);
88 server->n_ref = REFCNT_INIT;
91 server->address = htobe32(INADDR_ANY);
92 server->index = ifindex;
100 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
103 assert_return(server, -EINVAL);
104 assert_return(!server->event, -EBUSY);
107 server->event = sd_event_ref(event);
109 r = sd_event_default(&server->event);
114 server->event_priority = priority;
119 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
120 assert_return(server, -EINVAL);
122 server->event = sd_event_unref(server->event);
127 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
128 assert_return(server, NULL);
130 return server->event;
133 int sd_dhcp_server_stop(sd_dhcp_server *server) {
134 assert_return(server, -EINVAL);
136 server->receive_message =
137 sd_event_source_unref(server->receive_message);
139 server->fd_raw = safe_close(server->fd_raw);
140 server->fd = safe_close(server->fd);
142 log_dhcp_server(server, "STOPPED");
147 static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
149 union sockaddr_union link = {
150 .ll.sll_family = AF_PACKET,
151 .ll.sll_protocol = htons(ETH_P_IP),
152 .ll.sll_ifindex = server->index,
153 .ll.sll_halen = ETH_ALEN,
158 assert(server->index > 0);
159 assert(server->address);
161 assert(len > sizeof(DHCPPacket));
163 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
165 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
166 packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
168 r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
175 static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
176 DHCPMessage *message, size_t len) {
177 union sockaddr_union dest = {
178 .in.sin_family = AF_INET,
179 .in.sin_port = htobe16(DHCP_PORT_CLIENT),
180 .in.sin_addr.s_addr = destination,
186 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
187 struct msghdr msg = {
189 .msg_namelen = sizeof(dest.in),
192 .msg_control = cmsgbuf,
193 .msg_controllen = sizeof(cmsgbuf),
195 struct cmsghdr *cmsg;
196 struct in_pktinfo *pktinfo;
200 assert(server->fd > 0);
202 assert(len > sizeof(DHCPMessage));
204 cmsg = CMSG_FIRSTHDR(&msg);
207 cmsg->cmsg_level = IPPROTO_IP;
208 cmsg->cmsg_type = IP_PKTINFO;
209 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
211 /* we attach source interface and address info to the message
212 rather than binding the socket. This will be mostly useful
213 when we gain support for arbitrary number of server addresses
215 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
218 pktinfo->ipi_ifindex = server->index;
219 pktinfo->ipi_spec_dst.s_addr = server->address;
221 r = sendmsg(server->fd, &msg, 0);
228 static bool requested_broadcast(DHCPRequest *req) {
231 return req->message->flags & htobe16(0x8000);
234 int dhcp_server_send_packet(sd_dhcp_server *server,
235 DHCPRequest *req, DHCPPacket *packet,
236 int type, size_t optoffset) {
237 be32_t destination = INADDR_ANY;
242 assert(req->max_optlen);
243 assert(optoffset <= req->max_optlen);
246 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
247 DHCP_OPTION_SERVER_IDENTIFIER,
248 4, &server->address);
252 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
253 DHCP_OPTION_END, 0, NULL);
257 /* RFC 2131 Section 4.1
259 If the ’giaddr’ field in a DHCP message from a client is non-zero,
260 the server sends any return messages to the ’DHCP server’ port on the
261 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
262 field is zero and the ’ciaddr’ field is nonzero, then the server
263 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
264 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
265 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
266 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
267 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
268 messages to the client’s hardware address and ’yiaddr’ address. In
269 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
270 messages to 0xffffffff.
274 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
275 different subnet. The server MUST set the broadcast bit in the
276 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
277 client, because the client may not have a correct network address
278 or subnet mask, and the client may not be answering ARP requests.
280 if (req->message->giaddr) {
281 destination = req->message->giaddr;
282 if (type == DHCP_NAK)
283 packet->dhcp.flags = htobe16(0x8000);
284 } else if (req->message->ciaddr && type != DHCP_NAK)
285 destination = req->message->ciaddr;
287 if (destination || requested_broadcast(req) || type == DHCP_NAK)
288 return dhcp_server_send_udp(server, destination, &packet->dhcp,
289 sizeof(DHCPMessage) + optoffset);
291 /* we cannot send UDP packet to specific MAC address when the address is
292 not yet configured, so must fall back to raw packets */
293 return dhcp_server_send_unicast_raw(server, packet,
294 sizeof(DHCPPacket) + optoffset);
297 static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
298 uint8_t type, size_t *_optoffset, DHCPRequest *req) {
299 _cleanup_free_ DHCPPacket *packet = NULL;
306 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
308 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
312 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
313 type, req->max_optlen, &optoffset);
317 packet->dhcp.flags = req->message->flags;
318 packet->dhcp.giaddr = req->message->giaddr;
319 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
321 *_optoffset = optoffset;
328 static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
329 _cleanup_free_ DHCPPacket *packet = NULL;
334 r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
338 packet->dhcp.yiaddr = address;
340 lease_time = htobe32(req->lifetime);
341 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
342 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
346 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
353 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
354 _cleanup_free_ DHCPPacket *packet = NULL;
359 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
363 packet->dhcp.yiaddr = address;
365 lease_time = htobe32(req->lifetime);
366 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
367 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
371 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
378 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
379 _cleanup_free_ DHCPPacket *packet = NULL;
383 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
387 r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
394 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
396 DHCPRequest *req = user_data;
401 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
403 req->lifetime = be32toh(*(be32_t*)option);
406 case DHCP_OPTION_REQUESTED_IP_ADDRESS:
408 req->requested_ip = *(be32_t*)option;
411 case DHCP_OPTION_SERVER_IDENTIFIER:
413 req->server_id = *(be32_t*)option;
416 case DHCP_OPTION_CLIENT_IDENTIFIER:
420 data = memdup(option, len);
424 free(req->client_id.data);
425 req->client_id.data = data;
426 req->client_id.length = len;
430 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
432 req->max_optlen = be16toh(*(be16_t*)option) -
433 - sizeof(DHCPPacket);
441 static void dhcp_request_free(DHCPRequest *req) {
445 free(req->client_id.data);
449 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
450 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
452 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
456 req->message = message;
458 /* set client id based on mac address if client did not send an explicit one */
459 if (!req->client_id.data) {
462 data = new0(uint8_t, ETH_ALEN + 1);
466 req->client_id.length = ETH_ALEN + 1;
467 req->client_id.data = data;
468 req->client_id.data[0] = 0x01;
469 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
472 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
473 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
476 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
481 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
483 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
489 if (message->op != BOOTREQUEST ||
490 message->htype != ARPHRD_ETHER ||
491 message->hlen != ETHER_ADDR_LEN)
494 req = new0(DHCPRequest, 1);
498 type = dhcp_option_parse(message, length, parse_request, req);
502 r = ensure_sane_request(req, message);
504 /* this only fails on critical errors */
512 log_dhcp_server(server, "DISCOVER (0x%x)",
513 be32toh(req->message->xid));
515 if (!server->pool_size)
516 /* no pool allocated */
519 /* for now pick a random address from the pool */
520 address = htobe32(be32toh(server->pool_start) +
521 (random_u32() % server->pool_size));
523 r = server_send_offer(server, req, address);
525 /* this only fails on critical errors */
526 log_dhcp_server(server, "could not send offer: %s",
530 log_dhcp_server(server, "OFFER (0x%x)",
531 be32toh(req->message->xid));
540 bool init_reboot = false;
542 /* see RFC 2131, section 4.3.2 */
544 if (req->server_id) {
545 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
546 be32toh(req->message->xid));
549 if (req->server_id != server->address)
550 /* client did not pick us */
553 if (req->message->ciaddr)
554 /* this MUST be zero */
557 if (!req->requested_ip)
558 /* this must be filled in with the yiaddr
559 from the chosen OFFER */
562 address = req->requested_ip;
563 } else if (req->requested_ip) {
564 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
565 be32toh(req->message->xid));
568 if (req->message->ciaddr)
569 /* this MUST be zero */
572 /* TODO: check more carefully if IP is correct */
573 address = req->requested_ip;
576 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
577 be32toh(req->message->xid));
579 /* REBINDING / RENEWING */
580 if (!req->message->ciaddr)
581 /* this MUST be filled in with clients IP address */
584 address = req->message->ciaddr;
587 /* for now we just verify that the address is from the pool, not
588 whether or not it is taken */
589 if (htobe32(req->requested_ip) >= htobe32(server->pool_start) &&
590 htobe32(req->requested_ip) < htobe32(server->pool_start) +
591 + server->pool_size) {
592 r = server_send_ack(server, req, address);
594 /* this only fails on critical errors */
595 log_dhcp_server(server, "could not send ack: %s",
599 log_dhcp_server(server, "ACK (0x%x)",
600 be32toh(req->message->xid));
603 } else if (init_reboot) {
604 r = server_send_nak(server, req);
606 /* this only fails on critical errors */
607 log_dhcp_server(server, "could not send nak: %s",
611 log_dhcp_server(server, "NAK (0x%x)",
612 be32toh(req->message->xid));
624 static int server_receive_message(sd_event_source *s, int fd,
625 uint32_t revents, void *userdata) {
626 _cleanup_free_ DHCPMessage *message = NULL;
627 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
628 sd_dhcp_server *server = userdata;
629 struct iovec iov = {};
630 struct msghdr msg = {
633 .msg_control = cmsgbuf,
634 .msg_controllen = sizeof(cmsgbuf),
636 struct cmsghdr *cmsg;
637 int buflen = 0, len, r;
641 r = ioctl(fd, FIONREAD, &buflen);
647 message = malloc0(buflen);
651 iov.iov_base = message;
652 iov.iov_len = buflen;
654 len = recvmsg(fd, &msg, 0);
657 else if ((size_t)len < sizeof(DHCPMessage))
660 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
661 if (cmsg->cmsg_level == IPPROTO_IP &&
662 cmsg->cmsg_type == IP_PKTINFO &&
663 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
664 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
666 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
667 if (server->index != info->ipi_ifindex)
674 return dhcp_server_handle_message(server, message, (size_t)len);
677 int sd_dhcp_server_start(sd_dhcp_server *server) {
680 assert_return(server, -EINVAL);
681 assert_return(server->event, -EINVAL);
682 assert_return(!server->receive_message, -EBUSY);
683 assert_return(server->fd_raw == -1, -EBUSY);
684 assert_return(server->fd == -1, -EBUSY);
685 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
687 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
690 sd_dhcp_server_stop(server);
695 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
697 sd_dhcp_server_stop(server);
702 r = sd_event_add_io(server->event, &server->receive_message,
704 server_receive_message, server);
706 sd_dhcp_server_stop(server);
710 r = sd_event_source_set_priority(server->receive_message,
711 server->event_priority);
713 sd_dhcp_server_stop(server);
717 log_dhcp_server(server, "STARTED");