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