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