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