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