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