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;
341 lease_time = htobe32(DHCP_DEFAULT_LEASE_TIME);
342 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
343 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
347 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
354 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
355 _cleanup_free_ DHCPPacket *packet = NULL;
360 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
364 packet->dhcp.yiaddr = address;
366 /* for ten seconds */
367 lease_time = htobe32(DHCP_DEFAULT_LEASE_TIME);
368 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
369 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
373 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
380 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
381 _cleanup_free_ DHCPPacket *packet = NULL;
385 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
389 r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
396 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
398 DHCPRequest *req = user_data;
403 case DHCP_OPTION_REQUESTED_IP_ADDRESS:
405 req->requested_ip = *(be32_t*)option;
408 case DHCP_OPTION_SERVER_IDENTIFIER:
410 req->server_id = *(be32_t*)option;
413 case DHCP_OPTION_CLIENT_IDENTIFIER:
417 data = memdup(option, len);
421 free(req->client_id.data);
422 req->client_id.data = data;
423 req->client_id.length = len;
427 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
429 req->max_optlen = be16toh(*(be16_t*)option) -
430 - sizeof(DHCPPacket);
438 static void dhcp_request_free(DHCPRequest *req) {
442 free(req->client_id.data);
446 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
447 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
449 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
453 req->message = message;
455 /* set client id based on mac address if client did not send an explicit one */
456 if (!req->client_id.data) {
459 data = new0(uint8_t, ETH_ALEN + 1);
463 req->client_id.length = ETH_ALEN + 1;
464 req->client_id.data = data;
465 req->client_id.data[0] = 0x01;
466 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
469 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
470 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
475 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
477 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
483 if (message->op != BOOTREQUEST ||
484 message->htype != ARPHRD_ETHER ||
485 message->hlen != ETHER_ADDR_LEN)
488 req = new0(DHCPRequest, 1);
492 type = dhcp_option_parse(message, length, parse_request, req);
496 r = ensure_sane_request(req, message);
498 /* this only fails on critical errors */
506 log_dhcp_server(server, "DISCOVER (0x%x)",
507 be32toh(req->message->xid));
509 if (!server->pool_size)
510 /* no pool allocated */
513 /* for now pick a random address from the pool */
514 address = htobe32(be32toh(server->pool_start) +
515 (random_u32() % server->pool_size));
517 r = server_send_offer(server, req, address);
519 /* this only fails on critical errors */
520 log_dhcp_server(server, "could not send offer: %s",
524 log_dhcp_server(server, "OFFER (0x%x)",
525 be32toh(req->message->xid));
534 bool init_reboot = false;
536 /* see RFC 2131, section 4.3.2 */
538 if (req->server_id) {
539 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
540 be32toh(req->message->xid));
543 if (req->server_id != server->address)
544 /* client did not pick us */
547 if (req->message->ciaddr)
548 /* this MUST be zero */
551 if (!req->requested_ip)
552 /* this must be filled in with the yiaddr
553 from the chosen OFFER */
556 address = req->requested_ip;
557 } else if (req->requested_ip) {
558 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
559 be32toh(req->message->xid));
562 if (req->message->ciaddr)
563 /* this MUST be zero */
566 /* TODO: check more carefully if IP is correct */
567 address = req->requested_ip;
570 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
571 be32toh(req->message->xid));
573 /* REBINDING / RENEWING */
574 if (!req->message->ciaddr)
575 /* this MUST be filled in with clients IP address */
578 address = req->message->ciaddr;
581 /* for now we just verify that the address is from the pool, not
582 whether or not it is taken */
583 if (htobe32(req->requested_ip) >= htobe32(server->pool_start) &&
584 htobe32(req->requested_ip) < htobe32(server->pool_start) +
585 + server->pool_size) {
586 r = server_send_ack(server, req, address);
588 /* this only fails on critical errors */
589 log_dhcp_server(server, "could not send ack: %s",
593 log_dhcp_server(server, "ACK (0x%x)",
594 be32toh(req->message->xid));
597 } else if (init_reboot) {
598 r = server_send_nak(server, req);
600 /* this only fails on critical errors */
601 log_dhcp_server(server, "could not send nak: %s",
605 log_dhcp_server(server, "NAK (0x%x)",
606 be32toh(req->message->xid));
618 static int server_receive_message(sd_event_source *s, int fd,
619 uint32_t revents, void *userdata) {
620 _cleanup_free_ DHCPMessage *message = NULL;
621 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
622 sd_dhcp_server *server = userdata;
623 struct iovec iov = {};
624 struct msghdr msg = {
627 .msg_control = cmsgbuf,
628 .msg_controllen = sizeof(cmsgbuf),
630 struct cmsghdr *cmsg;
631 int buflen = 0, len, r;
635 r = ioctl(fd, FIONREAD, &buflen);
641 message = malloc0(buflen);
645 iov.iov_base = message;
646 iov.iov_len = buflen;
648 len = recvmsg(fd, &msg, 0);
651 else if ((size_t)len < sizeof(DHCPMessage))
654 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
655 if (cmsg->cmsg_level == IPPROTO_IP &&
656 cmsg->cmsg_type == IP_PKTINFO &&
657 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
658 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
660 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
661 if (server->index != info->ipi_ifindex)
668 return dhcp_server_handle_message(server, message, (size_t)len);
671 int sd_dhcp_server_start(sd_dhcp_server *server) {
674 assert_return(server, -EINVAL);
675 assert_return(server->event, -EINVAL);
676 assert_return(!server->receive_message, -EBUSY);
677 assert_return(server->fd_raw == -1, -EBUSY);
678 assert_return(server->fd == -1, -EBUSY);
679 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
681 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
684 sd_dhcp_server_stop(server);
689 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
691 sd_dhcp_server_stop(server);
696 r = sd_event_add_io(server->event, &server->receive_message,
698 server_receive_message, server);
700 sd_dhcp_server_stop(server);
704 r = sd_event_source_set_priority(server->receive_message,
705 server->event_priority);
707 sd_dhcp_server_stop(server);
711 log_dhcp_server(server, "STARTED");