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