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));
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 parse_request(uint8_t code, uint8_t len, const uint8_t *option,
382 DHCPRequest *req = user_data;
387 case DHCP_OPTION_REQUESTED_IP_ADDRESS:
389 req->requested_ip = *(be32_t*)option;
392 case DHCP_OPTION_SERVER_IDENTIFIER:
394 req->server_id = *(be32_t*)option;
397 case DHCP_OPTION_CLIENT_IDENTIFIER:
401 data = memdup(option, len);
405 free(req->client_id.data);
406 req->client_id.data = data;
407 req->client_id.length = len;
411 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
413 req->max_optlen = be16toh(*(be16_t*)option) -
414 - sizeof(DHCPPacket);
422 static void dhcp_request_free(DHCPRequest *req) {
426 free(req->client_id.data);
430 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
431 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
433 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
437 req->message = message;
439 /* set client id based on mac address if client did not send an explicit one */
440 if (!req->client_id.data) {
443 data = new0(uint8_t, ETH_ALEN + 1);
447 req->client_id.length = ETH_ALEN + 1;
448 req->client_id.data = data;
449 req->client_id.data[0] = 0x01;
450 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
453 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
454 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
459 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
461 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
467 if (message->op != BOOTREQUEST ||
468 message->htype != ARPHRD_ETHER ||
469 message->hlen != ETHER_ADDR_LEN)
472 req = new0(DHCPRequest, 1);
476 type = dhcp_option_parse(message, length, parse_request, req);
480 r = ensure_sane_request(req, message);
482 /* this only fails on critical errors */
490 log_dhcp_server(server, "DISCOVER (0x%x)",
491 be32toh(req->message->xid));
493 if (!server->pool_size)
494 /* no pool allocated */
497 /* for now pick a random address from the pool */
498 address = htobe32(be32toh(server->pool_start) +
499 (random_u32() % server->pool_size));
501 r = server_send_offer(server, req, address);
503 /* this only fails on critical errors */
504 log_dhcp_server(server, "could not send offer: %s",
508 log_dhcp_server(server, "OFFER (0x%x)",
509 be32toh(req->message->xid));
519 /* see RFC 2131, section 4.3.2 */
521 if (req->server_id) {
522 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
523 be32toh(req->message->xid));
526 if (req->server_id != server->address)
527 /* client did not pick us */
530 if (req->message->ciaddr)
531 /* this MUST be zero */
534 if (!req->requested_ip)
535 /* this must be filled in with the yiaddr
536 from the chosen OFFER */
539 address = req->requested_ip;
540 } else if (req->requested_ip) {
541 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
542 be32toh(req->message->xid));
545 if (req->message->ciaddr)
546 /* this MUST be zero */
549 /* TODO: check if requested IP is correct, NAK if not */
550 address = req->requested_ip;
552 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
553 be32toh(req->message->xid));
555 /* REBINDING / RENEWING */
556 if (!req->message->ciaddr)
557 /* this MUST be filled in with clients IP address */
560 address = req->message->ciaddr;
563 /* for now we just verify that the address is from the pool, not
564 whether or not it is taken */
565 if (htobe32(req->requested_ip) >= htobe32(server->pool_start) &&
566 htobe32(req->requested_ip) < htobe32(server->pool_start) +
567 + server->pool_size) {
568 r = server_send_ack(server, req, address);
570 /* this only fails on critical errors */
571 log_dhcp_server(server, "could not send ack: %s",
575 log_dhcp_server(server, "ACK (0x%x)",
576 be32toh(req->message->xid));
589 static int server_receive_message(sd_event_source *s, int fd,
590 uint32_t revents, void *userdata) {
591 _cleanup_free_ DHCPMessage *message = NULL;
592 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
593 sd_dhcp_server *server = userdata;
594 struct iovec iov = {};
595 struct msghdr msg = {
598 .msg_control = cmsgbuf,
599 .msg_controllen = sizeof(cmsgbuf),
601 struct cmsghdr *cmsg;
602 int buflen = 0, len, r;
606 r = ioctl(fd, FIONREAD, &buflen);
612 message = malloc0(buflen);
616 iov.iov_base = message;
617 iov.iov_len = buflen;
619 len = recvmsg(fd, &msg, 0);
622 else if ((size_t)len < sizeof(DHCPMessage))
625 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
626 if (cmsg->cmsg_level == IPPROTO_IP &&
627 cmsg->cmsg_type == IP_PKTINFO &&
628 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
629 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
631 /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
632 if (server->index != info->ipi_ifindex)
639 return dhcp_server_handle_message(server, message, (size_t)len);
642 int sd_dhcp_server_start(sd_dhcp_server *server) {
645 assert_return(server, -EINVAL);
646 assert_return(server->event, -EINVAL);
647 assert_return(!server->receive_message, -EBUSY);
648 assert_return(server->fd_raw == -1, -EBUSY);
649 assert_return(server->fd == -1, -EBUSY);
650 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
652 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
655 sd_dhcp_server_stop(server);
660 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
662 sd_dhcp_server_stop(server);
667 r = sd_event_add_io(server->event, &server->receive_message,
669 server_receive_message, server);
671 sd_dhcp_server_stop(server);
675 r = sd_event_source_set_priority(server->receive_message,
676 server->event_priority);
678 sd_dhcp_server_stop(server);
682 log_dhcp_server(server, "STARTED");