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