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