]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp-server.c
Merge pull request #3 from threatgrid/more_cgtop_enhancements
[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
25 #include "siphash24.h"
26
27 #include "sd-dhcp-server.h"
28 #include "dhcp-server-internal.h"
29 #include "dhcp-internal.h"
30
31 #define DHCP_DEFAULT_LEASE_TIME 3600 /* one hour */
32
33 int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server,
34 struct in_addr *address,
35 size_t size) {
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);
43
44 server->bound_leases = new0(DHCPLease*, size);
45 if (!server->bound_leases)
46 return -ENOMEM;
47
48 server->pool_start = address->s_addr;
49 server->pool_size = size;
50
51 return 0;
52 }
53
54 int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address,
55 unsigned char prefixlen) {
56 assert_return(server, -EINVAL);
57 assert_return(address, -EINVAL);
58 assert_return(address->s_addr, -EINVAL);
59 assert_return(prefixlen <= 32, -ERANGE);
60 assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
61 assert_return(server->netmask == htobe32(INADDR_ANY), -EBUSY);
62
63 server->address = address->s_addr;
64 server->netmask = htobe32(0xfffffffflu << (32 - prefixlen));
65
66 return 0;
67 }
68
69 bool sd_dhcp_server_is_running(sd_dhcp_server *server) {
70 assert_return(server, -EINVAL);
71
72 return !!server->receive_message;
73 }
74
75 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
76 if (server)
77 assert_se(REFCNT_INC(server->n_ref) >= 2);
78
79 return server;
80 }
81
82 unsigned long client_id_hash_func(const void *p,
83 const uint8_t hash_key[HASH_KEY_SIZE]) {
84 uint64_t u;
85 const DHCPClientId *id = p;
86
87 assert(id);
88 assert(id->length);
89 assert(id->data);
90
91 siphash24((uint8_t*) &u, id->data, id->length, hash_key);
92
93 return (unsigned long) u;
94 }
95
96 int client_id_compare_func(const void *_a, const void *_b) {
97 const DHCPClientId *a, *b;
98
99 a = _a;
100 b = _b;
101
102 assert(!a->length || a->data);
103 assert(!b->length || b->data);
104
105 if (a->length != b->length)
106 return a->length < b->length ? -1 : 1;
107
108 return memcmp(a->data, b->data, a->length);
109 }
110
111 static const struct hash_ops client_id_hash_ops = {
112 .hash = client_id_hash_func,
113 .compare = client_id_compare_func
114 };
115
116 static void dhcp_lease_free(DHCPLease *lease) {
117 if (!lease)
118 return;
119
120 free(lease->client_id.data);
121 free(lease);
122 }
123
124 sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
125 DHCPLease *lease;
126
127 if (!server)
128 return NULL;
129
130 if (REFCNT_DEC(server->n_ref) > 0)
131 return NULL;
132
133 log_dhcp_server(server, "UNREF");
134
135 sd_dhcp_server_stop(server);
136
137 sd_event_unref(server->event);
138
139 while ((lease = hashmap_steal_first(server->leases_by_client_id)))
140 dhcp_lease_free(lease);
141 hashmap_free(server->leases_by_client_id);
142
143 free(server->bound_leases);
144 free(server);
145
146 return NULL;
147 }
148
149 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
150 _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
151
152 assert_return(ret, -EINVAL);
153 assert_return(ifindex > 0, -EINVAL);
154
155 server = new0(sd_dhcp_server, 1);
156 if (!server)
157 return -ENOMEM;
158
159 server->n_ref = REFCNT_INIT;
160 server->fd_raw = -1;
161 server->fd = -1;
162 server->address = htobe32(INADDR_ANY);
163 server->netmask = htobe32(INADDR_ANY);
164 server->index = ifindex;
165 server->leases_by_client_id = hashmap_new(&client_id_hash_ops);
166
167 *ret = server;
168 server = NULL;
169
170 return 0;
171 }
172
173 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event,
174 int priority) {
175 int r;
176
177 assert_return(server, -EINVAL);
178 assert_return(!server->event, -EBUSY);
179
180 if (event)
181 server->event = sd_event_ref(event);
182 else {
183 r = sd_event_default(&server->event);
184 if (r < 0)
185 return r;
186 }
187
188 server->event_priority = priority;
189
190 return 0;
191 }
192
193 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
194 assert_return(server, -EINVAL);
195
196 server->event = sd_event_unref(server->event);
197
198 return 0;
199 }
200
201 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
202 assert_return(server, NULL);
203
204 return server->event;
205 }
206
207 int sd_dhcp_server_stop(sd_dhcp_server *server) {
208 assert_return(server, -EINVAL);
209
210 server->receive_message =
211 sd_event_source_unref(server->receive_message);
212
213 server->fd_raw = safe_close(server->fd_raw);
214 server->fd = safe_close(server->fd);
215
216 log_dhcp_server(server, "STOPPED");
217
218 return 0;
219 }
220
221 static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
222 DHCPPacket *packet, size_t len) {
223 union sockaddr_union link = {
224 .ll.sll_family = AF_PACKET,
225 .ll.sll_protocol = htons(ETH_P_IP),
226 .ll.sll_ifindex = server->index,
227 .ll.sll_halen = ETH_ALEN,
228 };
229 int r;
230
231 assert(server);
232 assert(server->index > 0);
233 assert(server->address);
234 assert(packet);
235 assert(len > sizeof(DHCPPacket));
236
237 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
238
239 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
240 packet->dhcp.yiaddr,
241 DHCP_PORT_CLIENT, len);
242
243 r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
244 if (r < 0)
245 return r;
246
247 return 0;
248 }
249
250 static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
251 DHCPMessage *message, size_t len) {
252 union sockaddr_union dest = {
253 .in.sin_family = AF_INET,
254 .in.sin_port = htobe16(DHCP_PORT_CLIENT),
255 .in.sin_addr.s_addr = destination,
256 };
257 struct iovec iov = {
258 .iov_base = message,
259 .iov_len = len,
260 };
261 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
262 struct msghdr msg = {
263 .msg_name = &dest,
264 .msg_namelen = sizeof(dest.in),
265 .msg_iov = &iov,
266 .msg_iovlen = 1,
267 .msg_control = cmsgbuf,
268 .msg_controllen = sizeof(cmsgbuf),
269 };
270 struct cmsghdr *cmsg;
271 struct in_pktinfo *pktinfo;
272 int r;
273
274 assert(server);
275 assert(server->fd > 0);
276 assert(message);
277 assert(len > sizeof(DHCPMessage));
278
279 cmsg = CMSG_FIRSTHDR(&msg);
280 assert(cmsg);
281
282 cmsg->cmsg_level = IPPROTO_IP;
283 cmsg->cmsg_type = IP_PKTINFO;
284 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
285
286 /* we attach source interface and address info to the message
287 rather than binding the socket. This will be mostly useful
288 when we gain support for arbitrary number of server addresses
289 */
290 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
291 assert(pktinfo);
292
293 pktinfo->ipi_ifindex = server->index;
294 pktinfo->ipi_spec_dst.s_addr = server->address;
295
296 r = sendmsg(server->fd, &msg, 0);
297 if (r < 0)
298 return -errno;
299
300 return 0;
301 }
302
303 static bool requested_broadcast(DHCPRequest *req) {
304 assert(req);
305
306 return req->message->flags & htobe16(0x8000);
307 }
308
309 int dhcp_server_send_packet(sd_dhcp_server *server,
310 DHCPRequest *req, DHCPPacket *packet,
311 int type, size_t optoffset) {
312 be32_t destination = INADDR_ANY;
313 int r;
314
315 assert(server);
316 assert(req);
317 assert(req->max_optlen);
318 assert(optoffset <= req->max_optlen);
319 assert(packet);
320
321 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
322 DHCP_OPTION_SERVER_IDENTIFIER,
323 4, &server->address);
324 if (r < 0)
325 return r;
326
327 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
328 DHCP_OPTION_END, 0, NULL);
329 if (r < 0)
330 return r;
331
332 /* RFC 2131 Section 4.1
333
334 If the ’giaddr’ field in a DHCP message from a client is non-zero,
335 the server sends any return messages to the ’DHCP server’ port on the
336 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
337 field is zero and the ’ciaddr’ field is nonzero, then the server
338 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
339 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
340 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
341 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
342 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
343 messages to the client’s hardware address and ’yiaddr’ address. In
344 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
345 messages to 0xffffffff.
346
347 Section 4.3.2
348
349 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
350 different subnet. The server MUST set the broadcast bit in the
351 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
352 client, because the client may not have a correct network address
353 or subnet mask, and the client may not be answering ARP requests.
354 */
355 if (req->message->giaddr) {
356 destination = req->message->giaddr;
357 if (type == DHCP_NAK)
358 packet->dhcp.flags = htobe16(0x8000);
359 } else if (req->message->ciaddr && type != DHCP_NAK)
360 destination = req->message->ciaddr;
361
362 if (destination != INADDR_ANY)
363 return dhcp_server_send_udp(server, destination, &packet->dhcp,
364 sizeof(DHCPMessage) + optoffset);
365 else if (requested_broadcast(req) || type == DHCP_NAK)
366 return dhcp_server_send_udp(server, INADDR_BROADCAST,
367 &packet->dhcp,
368 sizeof(DHCPMessage) + optoffset);
369 else
370 /* we cannot send UDP packet to specific MAC address when the
371 address is not yet configured, so must fall back to raw
372 packets */
373 return dhcp_server_send_unicast_raw(server, packet,
374 sizeof(DHCPPacket) + optoffset);
375 }
376
377 static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
378 uint8_t type, size_t *_optoffset,
379 DHCPRequest *req) {
380 _cleanup_free_ DHCPPacket *packet = NULL;
381 size_t optoffset = 0;
382 int r;
383
384 assert(server);
385 assert(ret);
386 assert(_optoffset);
387 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
388
389 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
390 if (!packet)
391 return -ENOMEM;
392
393 r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
394 be32toh(req->message->xid), type, ARPHRD_ETHER,
395 req->max_optlen, &optoffset);
396 if (r < 0)
397 return r;
398
399 packet->dhcp.flags = req->message->flags;
400 packet->dhcp.giaddr = req->message->giaddr;
401 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
402
403 *_optoffset = optoffset;
404 *ret = packet;
405 packet = NULL;
406
407 return 0;
408 }
409
410 static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
411 be32_t address) {
412 _cleanup_free_ DHCPPacket *packet = NULL;
413 size_t offset;
414 be32_t lease_time;
415 int r;
416
417 r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
418 if (r < 0)
419 return r;
420
421 packet->dhcp.yiaddr = address;
422
423 lease_time = htobe32(req->lifetime);
424 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
425 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
426 &lease_time);
427 if (r < 0)
428 return r;
429
430 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
431 DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
432 if (r < 0)
433 return r;
434
435 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
436 DHCP_OPTION_ROUTER, 4, &server->address);
437 if (r < 0)
438 return r;
439
440 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
441 if (r < 0)
442 return r;
443
444 return 0;
445 }
446
447 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
448 be32_t address) {
449 _cleanup_free_ DHCPPacket *packet = NULL;
450 size_t offset;
451 be32_t lease_time;
452 int r;
453
454 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
455 if (r < 0)
456 return r;
457
458 packet->dhcp.yiaddr = address;
459
460 lease_time = htobe32(req->lifetime);
461 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
462 DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
463 &lease_time);
464 if (r < 0)
465 return r;
466
467 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
468 DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
469 if (r < 0)
470 return r;
471
472 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
473 DHCP_OPTION_ROUTER, 4, &server->address);
474 if (r < 0)
475 return r;
476
477 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
478 if (r < 0)
479 return r;
480
481 return 0;
482 }
483
484 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
485 _cleanup_free_ DHCPPacket *packet = NULL;
486 size_t offset;
487 int r;
488
489 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
490 if (r < 0)
491 return r;
492
493 r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
494 if (r < 0)
495 return r;
496
497 return 0;
498 }
499
500 static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
501 be32_t gateway, uint8_t chaddr[]) {
502 _cleanup_free_ DHCPPacket *packet = NULL;
503 size_t optoffset = 0;
504 int r;
505
506 assert(server);
507 assert(address != INADDR_ANY);
508 assert(chaddr);
509
510 packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE);
511 if (!packet)
512 return -ENOMEM;
513
514 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
515 DHCP_FORCERENEW, ARPHRD_ETHER,
516 DHCP_MIN_OPTIONS_SIZE, &optoffset);
517 if (r < 0)
518 return r;
519
520 r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
521 &optoffset, 0, DHCP_OPTION_END, 0, NULL);
522 if (r < 0)
523 return r;
524
525 memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
526
527 r = dhcp_server_send_udp(server, address, &packet->dhcp,
528 sizeof(DHCPMessage) + optoffset);
529 if (r < 0)
530 return r;
531
532 return 0;
533 }
534
535 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
536 void *user_data) {
537 DHCPRequest *req = user_data;
538
539 assert(req);
540
541 switch(code) {
542 case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
543 if (len == 4)
544 req->lifetime = be32toh(*(be32_t*)option);
545
546 break;
547 case DHCP_OPTION_REQUESTED_IP_ADDRESS:
548 if (len == 4)
549 req->requested_ip = *(be32_t*)option;
550
551 break;
552 case DHCP_OPTION_SERVER_IDENTIFIER:
553 if (len == 4)
554 req->server_id = *(be32_t*)option;
555
556 break;
557 case DHCP_OPTION_CLIENT_IDENTIFIER:
558 if (len >= 2) {
559 uint8_t *data;
560
561 data = memdup(option, len);
562 if (!data)
563 return -ENOMEM;
564
565 free(req->client_id.data);
566 req->client_id.data = data;
567 req->client_id.length = len;
568 }
569
570 break;
571 case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
572 if (len == 2)
573 req->max_optlen = be16toh(*(be16_t*)option) -
574 - sizeof(DHCPPacket);
575
576 break;
577 }
578
579 return 0;
580 }
581
582 static void dhcp_request_free(DHCPRequest *req) {
583 if (!req)
584 return;
585
586 free(req->client_id.data);
587 free(req);
588 }
589
590 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
591 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
592
593 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
594 assert(req);
595 assert(message);
596
597 req->message = message;
598
599 /* set client id based on MAC address if client did not send an explicit
600 one */
601 if (!req->client_id.data) {
602 uint8_t *data;
603
604 data = new0(uint8_t, ETH_ALEN + 1);
605 if (!data)
606 return -ENOMEM;
607
608 req->client_id.length = ETH_ALEN + 1;
609 req->client_id.data = data;
610 req->client_id.data[0] = 0x01;
611 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
612 }
613
614 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
615 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
616
617 if (!req->lifetime)
618 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
619
620 return 0;
621 }
622
623 static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
624 assert(server);
625
626 if (!server->pool_size)
627 return -EINVAL;
628
629 if (be32toh(requested_ip) < be32toh(server->pool_start) ||
630 be32toh(requested_ip) >= be32toh(server->pool_start) +
631 + server->pool_size)
632 return -EINVAL;
633
634 return be32toh(requested_ip) - be32toh(server->pool_start);
635 }
636
637 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
638 size_t length) {
639 _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
640 DHCPLease *existing_lease;
641 int type, r;
642
643 assert(server);
644 assert(message);
645
646 if (message->op != BOOTREQUEST ||
647 message->htype != ARPHRD_ETHER ||
648 message->hlen != ETHER_ADDR_LEN)
649 return 0;
650
651 req = new0(DHCPRequest, 1);
652 if (!req)
653 return -ENOMEM;
654
655 type = dhcp_option_parse(message, length, parse_request, req);
656 if (type < 0)
657 return 0;
658
659 r = ensure_sane_request(req, message);
660 if (r < 0)
661 /* this only fails on critical errors */
662 return r;
663
664 existing_lease = hashmap_get(server->leases_by_client_id,
665 &req->client_id);
666
667 switch(type) {
668 case DHCP_DISCOVER:
669 {
670 be32_t address = INADDR_ANY;
671 unsigned i;
672
673 log_dhcp_server(server, "DISCOVER (0x%x)",
674 be32toh(req->message->xid));
675
676 if (!server->pool_size)
677 /* no pool allocated */
678 return 0;
679
680 /* for now pick a random free address from the pool */
681 if (existing_lease)
682 address = existing_lease->address;
683 else {
684 for (i = 0; i < server->pool_size; i++) {
685 if (!server->bound_leases[server->next_offer]) {
686 address = htobe32(be32toh(server->pool_start) + server->next_offer);
687 break;
688 } else
689 server->next_offer = (server->next_offer + 1) % server->pool_size;
690 }
691 }
692
693 if (address == INADDR_ANY)
694 /* no free addresses left */
695 return 0;
696
697 r = server_send_offer(server, req, address);
698 if (r < 0) {
699 /* this only fails on critical errors */
700 log_dhcp_server(server, "could not send offer: %s",
701 strerror(-r));
702 return r;
703 } else {
704 log_dhcp_server(server, "OFFER (0x%x)",
705 be32toh(req->message->xid));
706 return DHCP_OFFER;
707 }
708
709 break;
710 }
711 case DHCP_DECLINE:
712 log_dhcp_server(server, "DECLINE (0x%x)",
713 be32toh(req->message->xid));
714
715 /* TODO: make sure we don't offer this address again */
716
717 return 1;
718
719 break;
720 case DHCP_REQUEST:
721 {
722 be32_t address;
723 bool init_reboot = false;
724 int pool_offset;
725
726 /* see RFC 2131, section 4.3.2 */
727
728 if (req->server_id) {
729 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
730 be32toh(req->message->xid));
731
732 /* SELECTING */
733 if (req->server_id != server->address)
734 /* client did not pick us */
735 return 0;
736
737 if (req->message->ciaddr)
738 /* this MUST be zero */
739 return 0;
740
741 if (!req->requested_ip)
742 /* this must be filled in with the yiaddr
743 from the chosen OFFER */
744 return 0;
745
746 address = req->requested_ip;
747 } else if (req->requested_ip) {
748 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
749 be32toh(req->message->xid));
750
751 /* INIT-REBOOT */
752 if (req->message->ciaddr)
753 /* this MUST be zero */
754 return 0;
755
756 /* TODO: check more carefully if IP is correct */
757 address = req->requested_ip;
758 init_reboot = true;
759 } else {
760 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
761 be32toh(req->message->xid));
762
763 /* REBINDING / RENEWING */
764 if (!req->message->ciaddr)
765 /* this MUST be filled in with clients IP address */
766 return 0;
767
768 address = req->message->ciaddr;
769 }
770
771 pool_offset = get_pool_offset(server, address);
772
773 /* verify that the requested address is from the pool, and either
774 owned by the current client or free */
775 if (pool_offset >= 0 &&
776 server->bound_leases[pool_offset] == existing_lease) {
777 DHCPLease *lease;
778 usec_t time_now = 0;
779
780 if (!existing_lease) {
781 lease = new0(DHCPLease, 1);
782 lease->address = req->requested_ip;
783 lease->client_id.data = memdup(req->client_id.data,
784 req->client_id.length);
785 if (!lease->client_id.data) {
786 free(lease);
787 return -ENOMEM;
788 }
789 lease->client_id.length = req->client_id.length;
790 memcpy(&lease->chaddr, &req->message->chaddr,
791 ETH_ALEN);
792 lease->gateway = req->message->giaddr;
793 } else
794 lease = existing_lease;
795
796 r = sd_event_now(server->event,
797 clock_boottime_or_monotonic(),
798 &time_now);
799 if (r < 0)
800 time_now = now(clock_boottime_or_monotonic());
801 lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
802
803 r = server_send_ack(server, req, address);
804 if (r < 0) {
805 /* this only fails on critical errors */
806 log_dhcp_server(server, "could not send ack: %s",
807 strerror(-r));
808
809 if (!existing_lease)
810 dhcp_lease_free(lease);
811
812 return r;
813 } else {
814 log_dhcp_server(server, "ACK (0x%x)",
815 be32toh(req->message->xid));
816
817 server->bound_leases[pool_offset] = lease;
818 hashmap_put(server->leases_by_client_id,
819 &lease->client_id, lease);
820
821 return DHCP_ACK;
822 }
823 } else if (init_reboot) {
824 r = server_send_nak(server, req);
825 if (r < 0) {
826 /* this only fails on critical errors */
827 log_dhcp_server(server, "could not send nak: %s",
828 strerror(-r));
829 return r;
830 } else {
831 log_dhcp_server(server, "NAK (0x%x)",
832 be32toh(req->message->xid));
833 return DHCP_NAK;
834 }
835 }
836
837 break;
838 }
839 case DHCP_RELEASE: {
840 int pool_offset;
841
842 log_dhcp_server(server, "RELEASE (0x%x)",
843 be32toh(req->message->xid));
844
845 if (!existing_lease)
846 return 0;
847
848 if (existing_lease->address != req->message->ciaddr)
849 return 0;
850
851 pool_offset = get_pool_offset(server, req->message->ciaddr);
852 if (pool_offset < 0)
853 return 0;
854
855 if (server->bound_leases[pool_offset] == existing_lease) {
856 server->bound_leases[pool_offset] = NULL;
857 hashmap_remove(server->leases_by_client_id, existing_lease);
858 dhcp_lease_free(existing_lease);
859
860 return 1;
861 } else
862 return 0;
863 }
864 }
865
866 return 0;
867 }
868
869 static int server_receive_message(sd_event_source *s, int fd,
870 uint32_t revents, void *userdata) {
871 _cleanup_free_ DHCPMessage *message = NULL;
872 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
873 sd_dhcp_server *server = userdata;
874 struct iovec iov = {};
875 struct msghdr msg = {
876 .msg_iov = &iov,
877 .msg_iovlen = 1,
878 .msg_control = cmsgbuf,
879 .msg_controllen = sizeof(cmsgbuf),
880 };
881 struct cmsghdr *cmsg;
882 int buflen = 0, len, r;
883
884 assert(server);
885
886 r = ioctl(fd, FIONREAD, &buflen);
887 if (r < 0)
888 return r;
889 if (buflen < 0)
890 return -EIO;
891
892 message = malloc0(buflen);
893 if (!message)
894 return -ENOMEM;
895
896 iov.iov_base = message;
897 iov.iov_len = buflen;
898
899 len = recvmsg(fd, &msg, 0);
900 if (len < buflen)
901 return 0;
902 else if ((size_t)len < sizeof(DHCPMessage))
903 return 0;
904
905 CMSG_FOREACH(cmsg, &msg) {
906 if (cmsg->cmsg_level == IPPROTO_IP &&
907 cmsg->cmsg_type == IP_PKTINFO &&
908 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
909 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
910
911 /* TODO figure out if this can be done as a filter on
912 * the socket, like for IPv6 */
913 if (server->index != info->ipi_ifindex)
914 return 0;
915
916 break;
917 }
918 }
919
920 return dhcp_server_handle_message(server, message, (size_t)len);
921 }
922
923 int sd_dhcp_server_start(sd_dhcp_server *server) {
924 int r;
925
926 assert_return(server, -EINVAL);
927 assert_return(server->event, -EINVAL);
928 assert_return(!server->receive_message, -EBUSY);
929 assert_return(server->fd_raw == -1, -EBUSY);
930 assert_return(server->fd == -1, -EBUSY);
931 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
932
933 r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
934 if (r < 0) {
935 r = -errno;
936 sd_dhcp_server_stop(server);
937 return r;
938 }
939 server->fd_raw = r;
940
941 r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
942 if (r < 0) {
943 sd_dhcp_server_stop(server);
944 return r;
945 }
946 server->fd = r;
947
948 r = sd_event_add_io(server->event, &server->receive_message,
949 server->fd, EPOLLIN,
950 server_receive_message, server);
951 if (r < 0) {
952 sd_dhcp_server_stop(server);
953 return r;
954 }
955
956 r = sd_event_source_set_priority(server->receive_message,
957 server->event_priority);
958 if (r < 0) {
959 sd_dhcp_server_stop(server);
960 return r;
961 }
962
963 log_dhcp_server(server, "STARTED");
964
965 return 0;
966 }
967
968 int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
969 unsigned i;
970 int r = 0;
971
972 assert_return(server, -EINVAL);
973 assert(server->bound_leases);
974
975 for (i = 0; i < server->pool_size; i++) {
976 DHCPLease *lease = server->bound_leases[i];
977
978 if (!lease)
979 continue;
980
981 r = server_send_forcerenew(server, lease->address,
982 lease->gateway,
983 lease->chaddr);
984 if (r < 0)
985 return r;
986 else
987 log_dhcp_server(server, "FORCERENEW");
988 }
989
990 return r;
991 }