]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp-server.c
sd-dhcp-server: add support for clients requesting lease lifetime
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-server.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright (C) 2013 Intel Corporation. All rights reserved.
7 Copyright (C) 2014 Tom Gundersen
8
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.
13
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.
18
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/>.
21 ***/
22
23 #include <sys/ioctl.h>
24 #include <netinet/if_ether.h>
25
26 #include "sd-dhcp-server.h"
27 #include "dhcp-server-internal.h"
28 #include "dhcp-internal.h"
29
30 #define DHCP_DEFAULT_LEASE_TIME 60
31
32 int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *address,
33 size_t size) {
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);
40
41 server->pool_start = address->s_addr;
42 server->pool_size = size;
43
44 return 0;
45 }
46
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);
52
53 server->address = address->s_addr;
54
55 return 0;
56 }
57
58 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
59 if (server)
60 assert_se(REFCNT_INC(server->n_ref) >= 2);
61
62 return server;
63 }
64
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");
68
69 sd_dhcp_server_stop(server);
70
71 sd_event_unref(server->event);
72 free(server);
73 }
74
75 return NULL;
76 }
77
78 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
79 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
80
81 assert_return(ret, -EINVAL);
82 assert_return(ifindex > 0, -EINVAL);
83
84 server = new0(sd_dhcp_server, 1);
85 if (!server)
86 return -ENOMEM;
87
88 server->n_ref = REFCNT_INIT;
89 server->fd_raw = -1;
90 server->fd = -1;
91 server->address = htobe32(INADDR_ANY);
92 server->index = ifindex;
93
94 *ret = server;
95 server = NULL;
96
97 return 0;
98 }
99
100 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
101 int r;
102
103 assert_return(server, -EINVAL);
104 assert_return(!server->event, -EBUSY);
105
106 if (event)
107 server->event = sd_event_ref(event);
108 else {
109 r = sd_event_default(&server->event);
110 if (r < 0)
111 return r;
112 }
113
114 server->event_priority = priority;
115
116 return 0;
117 }
118
119 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
120 assert_return(server, -EINVAL);
121
122 server->event = sd_event_unref(server->event);
123
124 return 0;
125 }
126
127 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
128 assert_return(server, NULL);
129
130 return server->event;
131 }
132
133 int sd_dhcp_server_stop(sd_dhcp_server *server) {
134 assert_return(server, -EINVAL);
135
136 server->receive_message =
137 sd_event_source_unref(server->receive_message);
138
139 server->fd_raw = safe_close(server->fd_raw);
140 server->fd = safe_close(server->fd);
141
142 log_dhcp_server(server, "STOPPED");
143
144 return 0;
145 }
146
147 static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
148 size_t len) {
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,
154 };
155 int r;
156
157 assert(server);
158 assert(server->index > 0);
159 assert(server->address);
160 assert(packet);
161 assert(len > sizeof(DHCPPacket));
162
163 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
164
165 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
166 packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
167
168 r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
169 if (r < 0)
170 return r;
171
172 return 0;
173 }
174
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,
181 };
182 struct iovec iov = {
183 .iov_base = message,
184 .iov_len = len,
185 };
186 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
187 struct msghdr msg = {
188 .msg_name = &dest,
189 .msg_namelen = sizeof(dest.in),
190 .msg_iov = &iov,
191 .msg_iovlen = 1,
192 .msg_control = cmsgbuf,
193 .msg_controllen = sizeof(cmsgbuf),
194 };
195 struct cmsghdr *cmsg;
196 struct in_pktinfo *pktinfo;
197 int r;
198
199 assert(server);
200 assert(server->fd > 0);
201 assert(message);
202 assert(len > sizeof(DHCPMessage));
203
204 cmsg = CMSG_FIRSTHDR(&msg);
205 assert(cmsg);
206
207 cmsg->cmsg_level = IPPROTO_IP;
208 cmsg->cmsg_type = IP_PKTINFO;
209 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
210
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
214 */
215 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
216 assert(pktinfo);
217
218 pktinfo->ipi_ifindex = server->index;
219 pktinfo->ipi_spec_dst.s_addr = server->address;
220
221 r = sendmsg(server->fd, &msg, 0);
222 if (r < 0)
223 return -errno;
224
225 return 0;
226 }
227
228 static bool requested_broadcast(DHCPRequest *req) {
229 assert(req);
230
231 return req->message->flags & htobe16(0x8000);
232 }
233
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;
238 int r;
239
240 assert(server);
241 assert(req);
242 assert(req->max_optlen);
243 assert(optoffset <= req->max_optlen);
244 assert(packet);
245
246 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
247 DHCP_OPTION_SERVER_IDENTIFIER,
248 4, &server->address);
249 if (r < 0)
250 return r;
251
252 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
253 DHCP_OPTION_END, 0, NULL);
254 if (r < 0)
255 return r;
256
257 /* RFC 2131 Section 4.1
258
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.
271
272 Section 4.3.2
273
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.
279 */
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;
286
287 if (destination || requested_broadcast(req) || type == DHCP_NAK)
288 return dhcp_server_send_udp(server, destination, &packet->dhcp,
289 sizeof(DHCPMessage) + optoffset);
290 else
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);
295 }
296
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;
300 size_t optoffset;
301 int r;
302
303 assert(server);
304 assert(ret);
305 assert(_optoffset);
306 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
307
308 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
309 if (!packet)
310 return -ENOMEM;
311
312 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
313 type, req->max_optlen, &optoffset);
314 if (r < 0)
315 return r;
316
317 packet->dhcp.flags = req->message->flags;
318 packet->dhcp.giaddr = req->message->giaddr;
319 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
320
321 *_optoffset = optoffset;
322 *ret = packet;
323 packet = NULL;
324
325 return 0;
326 }
327
328 static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
329 _cleanup_free_ DHCPPacket *packet = NULL;
330 size_t offset;
331 be32_t lease_time;
332 int r;
333
334 r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
335 if (r < 0)
336 return r;
337
338 packet->dhcp.yiaddr = address;
339
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);
343 if (r < 0)
344 return r;
345
346 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
347 if (r < 0)
348 return r;
349
350 return 0;
351 }
352
353 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
354 _cleanup_free_ DHCPPacket *packet = NULL;
355 size_t offset;
356 be32_t lease_time;
357 int r;
358
359 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
360 if (r < 0)
361 return r;
362
363 packet->dhcp.yiaddr = address;
364
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);
368 if (r < 0)
369 return r;
370
371 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
372 if (r < 0)
373 return r;
374
375 return 0;
376 }
377
378 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
379 _cleanup_free_ DHCPPacket *packet = NULL;
380 size_t offset;
381 int r;
382
383 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
384 if (r < 0)
385 return r;
386
387 r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
388 if (r < 0)
389 return r;
390
391 return 0;
392 }
393
394 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
395 void *user_data) {
396 DHCPRequest *req = user_data;
397
398 assert(req);
399
400 switch(code) {
401 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
402 if (len == 4)
403 req->lifetime = be32toh(*(be32_t*)option);
404
405 break;
406 case DHCP_OPTION_REQUESTED_IP_ADDRESS:
407 if (len == 4)
408 req->requested_ip = *(be32_t*)option;
409
410 break;
411 case DHCP_OPTION_SERVER_IDENTIFIER:
412 if (len == 4)
413 req->server_id = *(be32_t*)option;
414
415 break;
416 case DHCP_OPTION_CLIENT_IDENTIFIER:
417 if (len >= 2) {
418 uint8_t *data;
419
420 data = memdup(option, len);
421 if (!data)
422 return -ENOMEM;
423
424 free(req->client_id.data);
425 req->client_id.data = data;
426 req->client_id.length = len;
427 }
428
429 break;
430 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
431 if (len == 2)
432 req->max_optlen = be16toh(*(be16_t*)option) -
433 - sizeof(DHCPPacket);
434
435 break;
436 }
437
438 return 0;
439 }
440
441 static void dhcp_request_free(DHCPRequest *req) {
442 if (!req)
443 return;
444
445 free(req->client_id.data);
446 free(req);
447 }
448
449 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
450 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
451
452 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
453 assert(req);
454 assert(message);
455
456 req->message = message;
457
458 /* set client id based on mac address if client did not send an explicit one */
459 if (!req->client_id.data) {
460 uint8_t *data;
461
462 data = new0(uint8_t, ETH_ALEN + 1);
463 if (!data)
464 return -ENOMEM;
465
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);
470 }
471
472 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
473 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
474
475 if (!req->lifetime)
476 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
477
478 return 0;
479 }
480
481 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
482 size_t length) {
483 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
484 int type, r;
485
486 assert(server);
487 assert(message);
488
489 if (message->op != BOOTREQUEST ||
490 message->htype != ARPHRD_ETHER ||
491 message->hlen != ETHER_ADDR_LEN)
492 return 0;
493
494 req = new0(DHCPRequest, 1);
495 if (!req)
496 return -ENOMEM;
497
498 type = dhcp_option_parse(message, length, parse_request, req);
499 if (type < 0)
500 return 0;
501
502 r = ensure_sane_request(req, message);
503 if (r < 0)
504 /* this only fails on critical errors */
505 return r;
506
507 switch(type) {
508 case DHCP_DISCOVER:
509 {
510 be32_t address;
511
512 log_dhcp_server(server, "DISCOVER (0x%x)",
513 be32toh(req->message->xid));
514
515 if (!server->pool_size)
516 /* no pool allocated */
517 return 0;
518
519 /* for now pick a random address from the pool */
520 address = htobe32(be32toh(server->pool_start) +
521 (random_u32() % server->pool_size));
522
523 r = server_send_offer(server, req, address);
524 if (r < 0) {
525 /* this only fails on critical errors */
526 log_dhcp_server(server, "could not send offer: %s",
527 strerror(-r));
528 return r;
529 } else {
530 log_dhcp_server(server, "OFFER (0x%x)",
531 be32toh(req->message->xid));
532 return DHCP_OFFER;
533 }
534
535 break;
536 }
537 case DHCP_REQUEST:
538 {
539 be32_t address;
540 bool init_reboot = false;
541
542 /* see RFC 2131, section 4.3.2 */
543
544 if (req->server_id) {
545 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
546 be32toh(req->message->xid));
547
548 /* SELECTING */
549 if (req->server_id != server->address)
550 /* client did not pick us */
551 return 0;
552
553 if (req->message->ciaddr)
554 /* this MUST be zero */
555 return 0;
556
557 if (!req->requested_ip)
558 /* this must be filled in with the yiaddr
559 from the chosen OFFER */
560 return 0;
561
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));
566
567 /* INIT-REBOOT */
568 if (req->message->ciaddr)
569 /* this MUST be zero */
570 return 0;
571
572 /* TODO: check more carefully if IP is correct */
573 address = req->requested_ip;
574 init_reboot = true;
575 } else {
576 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
577 be32toh(req->message->xid));
578
579 /* REBINDING / RENEWING */
580 if (!req->message->ciaddr)
581 /* this MUST be filled in with clients IP address */
582 return 0;
583
584 address = req->message->ciaddr;
585 }
586
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);
593 if (r < 0) {
594 /* this only fails on critical errors */
595 log_dhcp_server(server, "could not send ack: %s",
596 strerror(-r));
597 return r;
598 } else {
599 log_dhcp_server(server, "ACK (0x%x)",
600 be32toh(req->message->xid));
601 return DHCP_ACK;
602 }
603 } else if (init_reboot) {
604 r = server_send_nak(server, req);
605 if (r < 0) {
606 /* this only fails on critical errors */
607 log_dhcp_server(server, "could not send nak: %s",
608 strerror(-r));
609 return r;
610 } else {
611 log_dhcp_server(server, "NAK (0x%x)",
612 be32toh(req->message->xid));
613 return DHCP_NAK;
614 }
615 }
616
617 break;
618 }
619 }
620
621 return 0;
622 }
623
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 = {
631 .msg_iov = &iov,
632 .msg_iovlen = 1,
633 .msg_control = cmsgbuf,
634 .msg_controllen = sizeof(cmsgbuf),
635 };
636 struct cmsghdr *cmsg;
637 int buflen = 0, len, r;
638
639 assert(server);
640
641 r = ioctl(fd, FIONREAD, &buflen);
642 if (r < 0)
643 return r;
644 if (buflen < 0)
645 return -EIO;
646
647 message = malloc0(buflen);
648 if (!message)
649 return -ENOMEM;
650
651 iov.iov_base = message;
652 iov.iov_len = buflen;
653
654 len = recvmsg(fd, &msg, 0);
655 if (len < buflen)
656 return 0;
657 else if ((size_t)len < sizeof(DHCPMessage))
658 return 0;
659
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);
665
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)
668 return 0;
669
670 break;
671 }
672 }
673
674 return dhcp_server_handle_message(server, message, (size_t)len);
675 }
676
677 int sd_dhcp_server_start(sd_dhcp_server *server) {
678 int r;
679
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);
686
687 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
688 if (r < 0) {
689 r = -errno;
690 sd_dhcp_server_stop(server);
691 return r;
692 }
693 server->fd_raw = r;
694
695 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
696 if (r < 0) {
697 sd_dhcp_server_stop(server);
698 return r;
699 }
700 server->fd = r;
701
702 r = sd_event_add_io(server->event, &server->receive_message,
703 server->fd, EPOLLIN,
704 server_receive_message, server);
705 if (r < 0) {
706 sd_dhcp_server_stop(server);
707 return r;
708 }
709
710 r = sd_event_source_set_priority(server->receive_message,
711 server->event_priority);
712 if (r < 0) {
713 sd_dhcp_server_stop(server);
714 return r;
715 }
716
717 log_dhcp_server(server, "STARTED");
718
719 return 0;
720 }