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