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 6 : 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 6 : assert_return(server, -EINVAL);
42 6 : assert_return(address, -EINVAL);
43 6 : assert_return(address->s_addr != INADDR_ANY, -EINVAL);
44 4 : assert_return(prefixlen <= 32, -ERANGE);
45 :
46 3 : assert_se(in4_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
47 3 : netmask = netmask_addr.s_addr;
48 :
49 3 : server_off = be32toh(address->s_addr & ~netmask);
50 3 : broadcast_off = be32toh(~netmask);
51 :
52 : /* the server address cannot be the subnet address */
53 3 : assert_return(server_off != 0, -ERANGE);
54 :
55 : /* nor the broadcast address */
56 3 : 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 3 : if (offset == 0)
61 3 : offset = 1;
62 :
63 3 : 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 3 : assert_return(size_max >= 1, -ERANGE);
69 :
70 3 : if (size != 0)
71 1 : assert_return(size <= size_max, -ERANGE);
72 : else
73 2 : size = size_max;
74 :
75 3 : if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) {
76 :
77 2 : free(server->bound_leases);
78 2 : server->bound_leases = new0(DHCPLease*, size);
79 2 : if (!server->bound_leases)
80 0 : return -ENOMEM;
81 :
82 2 : server->pool_offset = offset;
83 2 : server->pool_size = size;
84 :
85 2 : server->address = address->s_addr;
86 2 : server->netmask = netmask;
87 2 : server->subnet = address->s_addr & netmask;
88 :
89 2 : if (server_off >= offset && server_off - offset < size)
90 2 : server->bound_leases[server_off - offset] = &server->invalid_lease;
91 :
92 : /* Drop any leases associated with the old address range */
93 2 : hashmap_clear(server->leases_by_client_id);
94 : }
95 :
96 3 : 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 3 : static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
131 3 : assert(server);
132 :
133 3 : log_dhcp_server(server, "UNREF");
134 :
135 3 : sd_dhcp_server_stop(server);
136 :
137 3 : sd_event_unref(server->event);
138 :
139 3 : free(server->timezone);
140 3 : free(server->dns);
141 3 : free(server->ntp);
142 :
143 3 : hashmap_free(server->leases_by_client_id);
144 :
145 3 : free(server->bound_leases);
146 3 : return mfree(server);
147 : }
148 :
149 11 : DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_server, sd_dhcp_server, dhcp_server_free);
150 :
151 3 : int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
152 3 : _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
153 :
154 3 : assert_return(ret, -EINVAL);
155 3 : assert_return(ifindex > 0, -EINVAL);
156 :
157 3 : server = new0(sd_dhcp_server, 1);
158 3 : if (!server)
159 0 : return -ENOMEM;
160 :
161 3 : server->n_ref = 1;
162 3 : server->fd_raw = -1;
163 3 : server->fd = -1;
164 3 : server->address = htobe32(INADDR_ANY);
165 3 : server->netmask = htobe32(INADDR_ANY);
166 3 : server->ifindex = ifindex;
167 :
168 3 : server->leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
169 3 : if (!server->leases_by_client_id)
170 0 : return -ENOMEM;
171 :
172 3 : server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC);
173 3 : server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC);
174 :
175 3 : *ret = TAKE_PTR(server);
176 :
177 3 : return 0;
178 : }
179 :
180 4 : int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) {
181 : int r;
182 :
183 4 : assert_return(server, -EINVAL);
184 4 : assert_return(!server->event, -EBUSY);
185 :
186 2 : if (event)
187 1 : server->event = sd_event_ref(event);
188 : else {
189 1 : r = sd_event_default(&server->event);
190 1 : if (r < 0)
191 0 : return r;
192 : }
193 :
194 2 : server->event_priority = priority;
195 :
196 2 : return 0;
197 : }
198 :
199 1 : int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
200 1 : assert_return(server, -EINVAL);
201 :
202 1 : server->event = sd_event_unref(server->event);
203 :
204 1 : return 0;
205 : }
206 :
207 2 : sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
208 2 : assert_return(server, NULL);
209 :
210 2 : return server->event;
211 : }
212 :
213 4 : int sd_dhcp_server_stop(sd_dhcp_server *server) {
214 4 : assert_return(server, -EINVAL);
215 :
216 4 : server->receive_message =
217 4 : sd_event_source_unref(server->receive_message);
218 :
219 4 : server->fd_raw = safe_close(server->fd_raw);
220 4 : server->fd = safe_close(server->fd);
221 :
222 4 : log_dhcp_server(server, "STOPPED");
223 :
224 4 : 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 2 : int sd_dhcp_server_start(sd_dhcp_server *server) {
970 : int r;
971 :
972 2 : assert_return(server, -EINVAL);
973 2 : assert_return(server->event, -EINVAL);
974 2 : assert_return(!server->receive_message, -EBUSY);
975 2 : assert_return(server->fd_raw < 0, -EBUSY);
976 2 : assert_return(server->fd < 0, -EBUSY);
977 2 : assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
978 :
979 1 : r = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
980 1 : if (r < 0) {
981 1 : r = -errno;
982 1 : sd_dhcp_server_stop(server);
983 1 : 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 : }
|