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