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