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