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