Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is part of systemd.
5 :
6 : Copyright (C) 2013 Intel Corporation. All rights reserved.
7 : Copyright (C) 2014 Tom Gundersen
8 :
9 : systemd is free software; you can redistribute it and/or modify it
10 : under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation; either version 2.1 of the License, or
12 : (at your option) any later version.
13 :
14 : systemd is distributed in the hope that it will be useful, but
15 : WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 : ***/
22 :
23 : #include <sys/ioctl.h>
24 :
25 : #include "siphash24.h"
26 :
27 : #include "sd-dhcp-server.h"
28 : #include "dhcp-server-internal.h"
29 : #include "dhcp-internal.h"
30 :
31 : #define DHCP_DEFAULT_LEASE_TIME 3600 /* one hour */
32 :
33 4 : int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server,
34 : struct in_addr *address,
35 : size_t size) {
36 4 : assert_return(server, -EINVAL);
37 4 : assert_return(address, -EINVAL);
38 4 : assert_return(address->s_addr, -EINVAL);
39 3 : assert_return(size, -EINVAL);
40 2 : assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
41 1 : assert_return(!server->pool_size, -EBUSY);
42 1 : assert_return(!server->bound_leases, -EBUSY);
43 :
44 1 : server->bound_leases = new0(DHCPLease*, size);
45 1 : if (!server->bound_leases)
46 0 : return -ENOMEM;
47 :
48 1 : server->pool_start = address->s_addr;
49 1 : server->pool_size = size;
50 :
51 1 : return 0;
52 : }
53 :
54 4 : int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address,
55 : unsigned char prefixlen) {
56 4 : assert_return(server, -EINVAL);
57 4 : assert_return(address, -EINVAL);
58 4 : assert_return(address->s_addr, -EINVAL);
59 3 : assert_return(prefixlen <= 32, -ERANGE);
60 2 : assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
61 1 : assert_return(server->netmask == htobe32(INADDR_ANY), -EBUSY);
62 :
63 1 : server->address = address->s_addr;
64 1 : server->netmask = htobe32(0xfffffffflu << (32 - prefixlen));
65 :
66 1 : return 0;
67 : }
68 :
69 0 : bool sd_dhcp_server_is_running(sd_dhcp_server *server) {
70 0 : assert_return(server, -EINVAL);
71 :
72 0 : return !!server->receive_message;
73 : }
74 :
75 1 : sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
76 1 : if (server)
77 1 : assert_se(REFCNT_INC(server->n_ref) >= 2);
78 :
79 1 : return server;
80 : }
81 :
82 0 : unsigned long client_id_hash_func(const void *p,
83 : const uint8_t hash_key[HASH_KEY_SIZE]) {
84 : uint64_t u;
85 0 : const DHCPClientId *id = p;
86 :
87 0 : assert(id);
88 0 : assert(id->length);
89 0 : assert(id->data);
90 :
91 0 : siphash24((uint8_t*) &u, id->data, id->length, hash_key);
92 :
93 0 : return (unsigned long) u;
94 : }
95 :
96 0 : int client_id_compare_func(const void *_a, const void *_b) {
97 : const DHCPClientId *a, *b;
98 :
99 0 : a = _a;
100 0 : b = _b;
101 :
102 0 : assert(!a->length || a->data);
103 0 : assert(!b->length || b->data);
104 :
105 0 : if (a->length != b->length)
106 0 : return a->length < b->length ? -1 : 1;
107 :
108 0 : return memcmp(a->data, b->data, a->length);
109 : }
110 :
111 : static const struct hash_ops client_id_hash_ops = {
112 : .hash = client_id_hash_func,
113 : .compare = client_id_compare_func
114 : };
115 :
116 0 : static void dhcp_lease_free(DHCPLease *lease) {
117 0 : if (!lease)
118 0 : return;
119 :
120 0 : free(lease->client_id.data);
121 0 : free(lease);
122 : }
123 :
124 6 : sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
125 : DHCPLease *lease;
126 :
127 6 : if (!server)
128 4 : return NULL;
129 :
130 2 : if (REFCNT_DEC(server->n_ref) > 0)
131 1 : return NULL;
132 :
133 1 : log_dhcp_server(server, "UNREF");
134 :
135 1 : sd_dhcp_server_stop(server);
136 :
137 1 : sd_event_unref(server->event);
138 :
139 2 : while ((lease = hashmap_steal_first(server->leases_by_client_id)))
140 0 : dhcp_lease_free(lease);
141 1 : hashmap_free(server->leases_by_client_id);
142 :
143 1 : free(server->bound_leases);
144 1 : free(server);
145 :
146 1 : return NULL;
147 : }
148 :
149 1 : int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
150 2 : _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
151 :
152 1 : assert_return(ret, -EINVAL);
153 1 : assert_return(ifindex > 0, -EINVAL);
154 :
155 1 : server = new0(sd_dhcp_server, 1);
156 1 : if (!server)
157 0 : return -ENOMEM;
158 :
159 1 : server->n_ref = REFCNT_INIT;
160 1 : server->fd_raw = -1;
161 1 : server->fd = -1;
162 1 : server->address = htobe32(INADDR_ANY);
163 1 : server->netmask = htobe32(INADDR_ANY);
164 1 : server->index = ifindex;
165 1 : server->leases_by_client_id = hashmap_new(&client_id_hash_ops);
166 :
167 1 : *ret = server;
168 1 : server = NULL;
169 :
170 1 : return 0;
171 : }
172 :
173 4 : int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event,
174 : int priority) {
175 : int r;
176 :
177 4 : assert_return(server, -EINVAL);
178 4 : assert_return(!server->event, -EBUSY);
179 :
180 2 : if (event)
181 1 : server->event = sd_event_ref(event);
182 : else {
183 1 : r = sd_event_default(&server->event);
184 1 : if (r < 0)
185 0 : return r;
186 : }
187 :
188 2 : server->event_priority = priority;
189 :
190 2 : return 0;
191 : }
192 :
193 1 : int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
194 1 : assert_return(server, -EINVAL);
195 :
196 1 : server->event = sd_event_unref(server->event);
197 :
198 1 : return 0;
199 : }
200 :
201 2 : sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
202 2 : assert_return(server, NULL);
203 :
204 2 : return server->event;
205 : }
206 :
207 2 : int sd_dhcp_server_stop(sd_dhcp_server *server) {
208 2 : assert_return(server, -EINVAL);
209 :
210 2 : server->receive_message =
211 2 : sd_event_source_unref(server->receive_message);
212 :
213 2 : server->fd_raw = safe_close(server->fd_raw);
214 2 : server->fd = safe_close(server->fd);
215 :
216 2 : log_dhcp_server(server, "STOPPED");
217 :
218 2 : return 0;
219 : }
220 :
221 0 : static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
222 : DHCPPacket *packet, size_t len) {
223 0 : union sockaddr_union link = {
224 : .ll.sll_family = AF_PACKET,
225 0 : .ll.sll_protocol = htons(ETH_P_IP),
226 0 : .ll.sll_ifindex = server->index,
227 : .ll.sll_halen = ETH_ALEN,
228 : };
229 : int r;
230 :
231 0 : assert(server);
232 0 : assert(server->index > 0);
233 0 : assert(server->address);
234 0 : assert(packet);
235 0 : assert(len > sizeof(DHCPPacket));
236 :
237 0 : memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
238 :
239 0 : dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
240 : packet->dhcp.yiaddr,
241 : DHCP_PORT_CLIENT, len);
242 :
243 0 : r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
244 0 : if (r < 0)
245 0 : return r;
246 :
247 0 : return 0;
248 : }
249 :
250 0 : static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
251 : DHCPMessage *message, size_t len) {
252 0 : union sockaddr_union dest = {
253 : .in.sin_family = AF_INET,
254 0 : .in.sin_port = htobe16(DHCP_PORT_CLIENT),
255 : .in.sin_addr.s_addr = destination,
256 : };
257 0 : struct iovec iov = {
258 : .iov_base = message,
259 : .iov_len = len,
260 : };
261 0 : uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
262 0 : struct msghdr msg = {
263 : .msg_name = &dest,
264 : .msg_namelen = sizeof(dest.in),
265 : .msg_iov = &iov,
266 : .msg_iovlen = 1,
267 : .msg_control = cmsgbuf,
268 : .msg_controllen = sizeof(cmsgbuf),
269 : };
270 : struct cmsghdr *cmsg;
271 : struct in_pktinfo *pktinfo;
272 : int r;
273 :
274 0 : assert(server);
275 0 : assert(server->fd > 0);
276 0 : assert(message);
277 0 : assert(len > sizeof(DHCPMessage));
278 :
279 0 : cmsg = CMSG_FIRSTHDR(&msg);
280 0 : assert(cmsg);
281 :
282 0 : cmsg->cmsg_level = IPPROTO_IP;
283 0 : cmsg->cmsg_type = IP_PKTINFO;
284 0 : cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
285 :
286 : /* we attach source interface and address info to the message
287 : rather than binding the socket. This will be mostly useful
288 : when we gain support for arbitrary number of server addresses
289 : */
290 0 : pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
291 0 : assert(pktinfo);
292 :
293 0 : pktinfo->ipi_ifindex = server->index;
294 0 : pktinfo->ipi_spec_dst.s_addr = server->address;
295 :
296 0 : r = sendmsg(server->fd, &msg, 0);
297 0 : if (r < 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 : int r;
314 :
315 0 : assert(server);
316 0 : assert(req);
317 0 : assert(req->max_optlen);
318 0 : assert(optoffset <= req->max_optlen);
319 0 : assert(packet);
320 :
321 0 : r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
322 : DHCP_OPTION_SERVER_IDENTIFIER,
323 0 : 4, &server->address);
324 0 : if (r < 0)
325 0 : return r;
326 :
327 0 : r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
328 : DHCP_OPTION_END, 0, NULL);
329 0 : if (r < 0)
330 0 : return r;
331 :
332 : /* RFC 2131 Section 4.1
333 :
334 : If the ’giaddr’ field in a DHCP message from a client is non-zero,
335 : the server sends any return messages to the ’DHCP server’ port on the
336 : BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
337 : field is zero and the ’ciaddr’ field is nonzero, then the server
338 : unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
339 : If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
340 : set, then the server broadcasts DHCPOFFER and DHCPACK messages to
341 : 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
342 : ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
343 : messages to the client’s hardware address and ’yiaddr’ address. In
344 : all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
345 : messages to 0xffffffff.
346 :
347 : Section 4.3.2
348 :
349 : If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
350 : different subnet. The server MUST set the broadcast bit in the
351 : DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
352 : client, because the client may not have a correct network address
353 : or subnet mask, and the client may not be answering ARP requests.
354 : */
355 0 : if (req->message->giaddr) {
356 0 : destination = req->message->giaddr;
357 0 : if (type == DHCP_NAK)
358 0 : packet->dhcp.flags = htobe16(0x8000);
359 0 : } else if (req->message->ciaddr && type != DHCP_NAK)
360 0 : destination = req->message->ciaddr;
361 :
362 0 : if (destination != INADDR_ANY)
363 0 : return dhcp_server_send_udp(server, destination, &packet->dhcp,
364 : sizeof(DHCPMessage) + optoffset);
365 0 : else if (requested_broadcast(req) || type == DHCP_NAK)
366 0 : return dhcp_server_send_udp(server, INADDR_BROADCAST,
367 : &packet->dhcp,
368 : sizeof(DHCPMessage) + optoffset);
369 : else
370 : /* we cannot send UDP packet to specific MAC address when the
371 : address is not yet configured, so must fall back to raw
372 : packets */
373 0 : return dhcp_server_send_unicast_raw(server, packet,
374 : sizeof(DHCPPacket) + optoffset);
375 : }
376 :
377 0 : static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
378 : uint8_t type, size_t *_optoffset,
379 : DHCPRequest *req) {
380 0 : _cleanup_free_ DHCPPacket *packet = NULL;
381 0 : size_t optoffset = 0;
382 : int r;
383 :
384 0 : assert(server);
385 0 : assert(ret);
386 0 : assert(_optoffset);
387 0 : assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
388 :
389 0 : packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
390 0 : if (!packet)
391 0 : return -ENOMEM;
392 :
393 0 : r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
394 0 : be32toh(req->message->xid), type, ARPHRD_ETHER,
395 : req->max_optlen, &optoffset);
396 0 : if (r < 0)
397 0 : return r;
398 :
399 0 : packet->dhcp.flags = req->message->flags;
400 0 : packet->dhcp.giaddr = req->message->giaddr;
401 0 : memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
402 :
403 0 : *_optoffset = optoffset;
404 0 : *ret = packet;
405 0 : packet = NULL;
406 :
407 0 : return 0;
408 : }
409 :
410 0 : static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
411 : be32_t address) {
412 0 : _cleanup_free_ DHCPPacket *packet = NULL;
413 : size_t offset;
414 : be32_t lease_time;
415 : int r;
416 :
417 0 : r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
418 0 : if (r < 0)
419 0 : return r;
420 :
421 0 : packet->dhcp.yiaddr = address;
422 :
423 0 : lease_time = htobe32(req->lifetime);
424 0 : r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
425 : DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
426 : &lease_time);
427 0 : if (r < 0)
428 0 : return r;
429 :
430 0 : r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
431 0 : DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
432 0 : if (r < 0)
433 0 : return r;
434 :
435 0 : r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
436 0 : DHCP_OPTION_ROUTER, 4, &server->address);
437 0 : if (r < 0)
438 0 : return r;
439 :
440 0 : r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
441 0 : if (r < 0)
442 0 : return r;
443 :
444 0 : return 0;
445 : }
446 :
447 0 : static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
448 : be32_t address) {
449 0 : _cleanup_free_ DHCPPacket *packet = NULL;
450 : size_t offset;
451 : be32_t lease_time;
452 : int r;
453 :
454 0 : r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
455 0 : if (r < 0)
456 0 : return r;
457 :
458 0 : packet->dhcp.yiaddr = address;
459 :
460 0 : lease_time = htobe32(req->lifetime);
461 0 : r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
462 : DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
463 : &lease_time);
464 0 : if (r < 0)
465 0 : return r;
466 :
467 0 : r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
468 0 : DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
469 0 : if (r < 0)
470 0 : return r;
471 :
472 0 : r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
473 0 : DHCP_OPTION_ROUTER, 4, &server->address);
474 0 : if (r < 0)
475 0 : return r;
476 :
477 0 : r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
478 0 : if (r < 0)
479 0 : return r;
480 :
481 0 : return 0;
482 : }
483 :
484 0 : static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
485 0 : _cleanup_free_ DHCPPacket *packet = NULL;
486 : size_t offset;
487 : int r;
488 :
489 0 : r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
490 0 : if (r < 0)
491 0 : return r;
492 :
493 0 : r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
494 0 : if (r < 0)
495 0 : return r;
496 :
497 0 : return 0;
498 : }
499 :
500 0 : static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
501 : be32_t gateway, uint8_t chaddr[]) {
502 0 : _cleanup_free_ DHCPPacket *packet = NULL;
503 0 : size_t optoffset = 0;
504 : int r;
505 :
506 0 : assert(server);
507 0 : assert(address != INADDR_ANY);
508 0 : assert(chaddr);
509 :
510 0 : packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE);
511 0 : if (!packet)
512 0 : return -ENOMEM;
513 :
514 0 : r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
515 : DHCP_FORCERENEW, ARPHRD_ETHER,
516 : DHCP_MIN_OPTIONS_SIZE, &optoffset);
517 0 : if (r < 0)
518 0 : return r;
519 :
520 0 : r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
521 : &optoffset, 0, DHCP_OPTION_END, 0, NULL);
522 0 : if (r < 0)
523 0 : return r;
524 :
525 0 : memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
526 :
527 0 : r = dhcp_server_send_udp(server, address, &packet->dhcp,
528 : sizeof(DHCPMessage) + optoffset);
529 0 : if (r < 0)
530 0 : return r;
531 :
532 0 : return 0;
533 : }
534 :
535 0 : static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
536 : void *user_data) {
537 0 : DHCPRequest *req = user_data;
538 :
539 0 : assert(req);
540 :
541 0 : switch(code) {
542 : case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
543 0 : if (len == 4)
544 0 : req->lifetime = be32toh(*(be32_t*)option);
545 :
546 0 : break;
547 : case DHCP_OPTION_REQUESTED_IP_ADDRESS:
548 0 : if (len == 4)
549 0 : req->requested_ip = *(be32_t*)option;
550 :
551 0 : break;
552 : case DHCP_OPTION_SERVER_IDENTIFIER:
553 0 : if (len == 4)
554 0 : req->server_id = *(be32_t*)option;
555 :
556 0 : break;
557 : case DHCP_OPTION_CLIENT_IDENTIFIER:
558 0 : if (len >= 2) {
559 : uint8_t *data;
560 :
561 0 : data = memdup(option, len);
562 0 : if (!data)
563 0 : return -ENOMEM;
564 :
565 0 : free(req->client_id.data);
566 0 : req->client_id.data = data;
567 0 : req->client_id.length = len;
568 : }
569 :
570 0 : break;
571 : case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
572 0 : if (len == 2)
573 0 : req->max_optlen = be16toh(*(be16_t*)option) -
574 : - sizeof(DHCPPacket);
575 :
576 0 : break;
577 : }
578 :
579 0 : return 0;
580 : }
581 :
582 0 : static void dhcp_request_free(DHCPRequest *req) {
583 0 : if (!req)
584 0 : return;
585 :
586 0 : free(req->client_id.data);
587 0 : free(req);
588 : }
589 :
590 0 : DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
591 : #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
592 :
593 0 : static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
594 0 : assert(req);
595 0 : assert(message);
596 :
597 0 : req->message = message;
598 :
599 : /* set client id based on MAC address if client did not send an explicit
600 : one */
601 0 : if (!req->client_id.data) {
602 : uint8_t *data;
603 :
604 0 : data = new0(uint8_t, ETH_ALEN + 1);
605 0 : if (!data)
606 0 : return -ENOMEM;
607 :
608 0 : req->client_id.length = ETH_ALEN + 1;
609 0 : req->client_id.data = data;
610 0 : req->client_id.data[0] = 0x01;
611 0 : memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
612 : }
613 :
614 0 : if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
615 0 : req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
616 :
617 0 : if (!req->lifetime)
618 0 : req->lifetime = DHCP_DEFAULT_LEASE_TIME;
619 :
620 0 : return 0;
621 : }
622 :
623 0 : static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
624 0 : assert(server);
625 :
626 0 : if (!server->pool_size)
627 0 : return -EINVAL;
628 :
629 0 : if (be32toh(requested_ip) < be32toh(server->pool_start) ||
630 0 : be32toh(requested_ip) >= be32toh(server->pool_start) +
631 0 : + server->pool_size)
632 0 : return -EINVAL;
633 :
634 0 : return be32toh(requested_ip) - be32toh(server->pool_start);
635 : }
636 :
637 0 : int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
638 : size_t length) {
639 0 : _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
640 : DHCPLease *existing_lease;
641 : int type, r;
642 :
643 0 : assert(server);
644 0 : assert(message);
645 :
646 0 : if (message->op != BOOTREQUEST ||
647 0 : message->htype != ARPHRD_ETHER ||
648 0 : message->hlen != ETHER_ADDR_LEN)
649 0 : return 0;
650 :
651 0 : req = new0(DHCPRequest, 1);
652 0 : if (!req)
653 0 : return -ENOMEM;
654 :
655 0 : type = dhcp_option_parse(message, length, parse_request, req);
656 0 : if (type < 0)
657 0 : return 0;
658 :
659 0 : r = ensure_sane_request(req, message);
660 0 : if (r < 0)
661 : /* this only fails on critical errors */
662 0 : return r;
663 :
664 0 : existing_lease = hashmap_get(server->leases_by_client_id,
665 0 : &req->client_id);
666 :
667 0 : switch(type) {
668 : case DHCP_DISCOVER:
669 : {
670 0 : be32_t address = INADDR_ANY;
671 : unsigned i;
672 :
673 0 : log_dhcp_server(server, "DISCOVER (0x%x)",
674 : be32toh(req->message->xid));
675 :
676 0 : if (!server->pool_size)
677 : /* no pool allocated */
678 0 : return 0;
679 :
680 : /* for now pick a random free address from the pool */
681 0 : if (existing_lease)
682 0 : address = existing_lease->address;
683 : else {
684 0 : for (i = 0; i < server->pool_size; i++) {
685 0 : if (!server->bound_leases[server->next_offer]) {
686 0 : address = htobe32(be32toh(server->pool_start) + server->next_offer);
687 0 : break;
688 : } else
689 0 : server->next_offer = (server->next_offer + 1) % server->pool_size;
690 : }
691 : }
692 :
693 0 : if (address == INADDR_ANY)
694 : /* no free addresses left */
695 0 : return 0;
696 :
697 0 : r = server_send_offer(server, req, address);
698 0 : if (r < 0) {
699 : /* this only fails on critical errors */
700 0 : log_dhcp_server(server, "could not send offer: %s",
701 : strerror(-r));
702 0 : return r;
703 : } else {
704 0 : log_dhcp_server(server, "OFFER (0x%x)",
705 : be32toh(req->message->xid));
706 0 : return DHCP_OFFER;
707 : }
708 :
709 : break;
710 : }
711 : case DHCP_DECLINE:
712 0 : log_dhcp_server(server, "DECLINE (0x%x)",
713 : be32toh(req->message->xid));
714 :
715 : /* TODO: make sure we don't offer this address again */
716 :
717 0 : return 1;
718 :
719 : break;
720 : case DHCP_REQUEST:
721 : {
722 : be32_t address;
723 0 : bool init_reboot = false;
724 : int pool_offset;
725 :
726 : /* see RFC 2131, section 4.3.2 */
727 :
728 0 : if (req->server_id) {
729 0 : log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
730 : be32toh(req->message->xid));
731 :
732 : /* SELECTING */
733 0 : if (req->server_id != server->address)
734 : /* client did not pick us */
735 0 : return 0;
736 :
737 0 : if (req->message->ciaddr)
738 : /* this MUST be zero */
739 0 : return 0;
740 :
741 0 : if (!req->requested_ip)
742 : /* this must be filled in with the yiaddr
743 : from the chosen OFFER */
744 0 : return 0;
745 :
746 0 : address = req->requested_ip;
747 0 : } else if (req->requested_ip) {
748 0 : log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
749 : be32toh(req->message->xid));
750 :
751 : /* INIT-REBOOT */
752 0 : if (req->message->ciaddr)
753 : /* this MUST be zero */
754 0 : return 0;
755 :
756 : /* TODO: check more carefully if IP is correct */
757 0 : address = req->requested_ip;
758 0 : init_reboot = true;
759 : } else {
760 0 : log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
761 : be32toh(req->message->xid));
762 :
763 : /* REBINDING / RENEWING */
764 0 : if (!req->message->ciaddr)
765 : /* this MUST be filled in with clients IP address */
766 0 : return 0;
767 :
768 0 : address = req->message->ciaddr;
769 : }
770 :
771 0 : pool_offset = get_pool_offset(server, address);
772 :
773 : /* verify that the requested address is from the pool, and either
774 : owned by the current client or free */
775 0 : if (pool_offset >= 0 &&
776 0 : server->bound_leases[pool_offset] == existing_lease) {
777 : DHCPLease *lease;
778 0 : usec_t time_now = 0;
779 :
780 0 : if (!existing_lease) {
781 0 : lease = new0(DHCPLease, 1);
782 0 : lease->address = req->requested_ip;
783 0 : lease->client_id.data = memdup(req->client_id.data,
784 0 : req->client_id.length);
785 0 : if (!lease->client_id.data) {
786 0 : free(lease);
787 0 : return -ENOMEM;
788 : }
789 0 : lease->client_id.length = req->client_id.length;
790 0 : memcpy(&lease->chaddr, &req->message->chaddr,
791 : ETH_ALEN);
792 0 : lease->gateway = req->message->giaddr;
793 : } else
794 0 : lease = existing_lease;
795 :
796 0 : r = sd_event_now(server->event,
797 : clock_boottime_or_monotonic(),
798 : &time_now);
799 0 : if (r < 0)
800 0 : time_now = now(clock_boottime_or_monotonic());
801 0 : lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
802 :
803 0 : r = server_send_ack(server, req, address);
804 0 : if (r < 0) {
805 : /* this only fails on critical errors */
806 0 : log_dhcp_server(server, "could not send ack: %s",
807 : strerror(-r));
808 :
809 0 : if (!existing_lease)
810 0 : dhcp_lease_free(lease);
811 :
812 0 : return r;
813 : } else {
814 0 : log_dhcp_server(server, "ACK (0x%x)",
815 : be32toh(req->message->xid));
816 :
817 0 : server->bound_leases[pool_offset] = lease;
818 0 : hashmap_put(server->leases_by_client_id,
819 0 : &lease->client_id, lease);
820 :
821 0 : return DHCP_ACK;
822 : }
823 0 : } else if (init_reboot) {
824 0 : r = server_send_nak(server, req);
825 0 : if (r < 0) {
826 : /* this only fails on critical errors */
827 0 : log_dhcp_server(server, "could not send nak: %s",
828 : strerror(-r));
829 0 : return r;
830 : } else {
831 0 : log_dhcp_server(server, "NAK (0x%x)",
832 : be32toh(req->message->xid));
833 0 : return DHCP_NAK;
834 : }
835 : }
836 :
837 0 : break;
838 : }
839 : case DHCP_RELEASE: {
840 : int pool_offset;
841 :
842 0 : log_dhcp_server(server, "RELEASE (0x%x)",
843 : be32toh(req->message->xid));
844 :
845 0 : if (!existing_lease)
846 0 : return 0;
847 :
848 0 : if (existing_lease->address != req->message->ciaddr)
849 0 : return 0;
850 :
851 0 : pool_offset = get_pool_offset(server, req->message->ciaddr);
852 0 : if (pool_offset < 0)
853 0 : return 0;
854 :
855 0 : if (server->bound_leases[pool_offset] == existing_lease) {
856 0 : server->bound_leases[pool_offset] = NULL;
857 0 : hashmap_remove(server->leases_by_client_id, existing_lease);
858 0 : dhcp_lease_free(existing_lease);
859 :
860 0 : return 1;
861 : } else
862 0 : return 0;
863 : }
864 : }
865 :
866 0 : return 0;
867 : }
868 :
869 0 : static int server_receive_message(sd_event_source *s, int fd,
870 : uint32_t revents, void *userdata) {
871 0 : _cleanup_free_ DHCPMessage *message = NULL;
872 : uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
873 0 : sd_dhcp_server *server = userdata;
874 0 : struct iovec iov = {};
875 0 : struct msghdr msg = {
876 : .msg_iov = &iov,
877 : .msg_iovlen = 1,
878 : .msg_control = cmsgbuf,
879 : .msg_controllen = sizeof(cmsgbuf),
880 : };
881 : struct cmsghdr *cmsg;
882 0 : int buflen = 0, len, r;
883 :
884 0 : assert(server);
885 :
886 0 : r = ioctl(fd, FIONREAD, &buflen);
887 0 : if (r < 0)
888 0 : return r;
889 0 : if (buflen < 0)
890 0 : return -EIO;
891 :
892 0 : message = malloc0(buflen);
893 0 : if (!message)
894 0 : return -ENOMEM;
895 :
896 0 : iov.iov_base = message;
897 0 : iov.iov_len = buflen;
898 :
899 0 : len = recvmsg(fd, &msg, 0);
900 0 : if (len < buflen)
901 0 : return 0;
902 0 : else if ((size_t)len < sizeof(DHCPMessage))
903 0 : return 0;
904 :
905 0 : CMSG_FOREACH(cmsg, &msg) {
906 0 : if (cmsg->cmsg_level == IPPROTO_IP &&
907 0 : cmsg->cmsg_type == IP_PKTINFO &&
908 0 : cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
909 0 : struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
910 :
911 : /* TODO figure out if this can be done as a filter on
912 : * the socket, like for IPv6 */
913 0 : if (server->index != info->ipi_ifindex)
914 0 : return 0;
915 :
916 0 : break;
917 : }
918 : }
919 :
920 0 : return dhcp_server_handle_message(server, message, (size_t)len);
921 : }
922 :
923 2 : int sd_dhcp_server_start(sd_dhcp_server *server) {
924 : int r;
925 :
926 2 : assert_return(server, -EINVAL);
927 2 : assert_return(server->event, -EINVAL);
928 2 : assert_return(!server->receive_message, -EBUSY);
929 2 : assert_return(server->fd_raw == -1, -EBUSY);
930 2 : assert_return(server->fd == -1, -EBUSY);
931 2 : assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
932 :
933 1 : r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
934 1 : if (r < 0) {
935 1 : r = -errno;
936 1 : sd_dhcp_server_stop(server);
937 1 : return r;
938 : }
939 0 : server->fd_raw = r;
940 :
941 0 : r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
942 0 : if (r < 0) {
943 0 : sd_dhcp_server_stop(server);
944 0 : return r;
945 : }
946 0 : server->fd = r;
947 :
948 0 : r = sd_event_add_io(server->event, &server->receive_message,
949 : server->fd, EPOLLIN,
950 : server_receive_message, server);
951 0 : if (r < 0) {
952 0 : sd_dhcp_server_stop(server);
953 0 : return r;
954 : }
955 :
956 0 : r = sd_event_source_set_priority(server->receive_message,
957 0 : server->event_priority);
958 0 : if (r < 0) {
959 0 : sd_dhcp_server_stop(server);
960 0 : return r;
961 : }
962 :
963 0 : log_dhcp_server(server, "STARTED");
964 :
965 0 : return 0;
966 : }
967 :
968 0 : int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
969 : unsigned i;
970 0 : int r = 0;
971 :
972 0 : assert_return(server, -EINVAL);
973 0 : assert(server->bound_leases);
974 :
975 0 : for (i = 0; i < server->pool_size; i++) {
976 0 : DHCPLease *lease = server->bound_leases[i];
977 :
978 0 : if (!lease)
979 0 : continue;
980 :
981 0 : r = server_send_forcerenew(server, lease->address,
982 : lease->gateway,
983 0 : lease->chaddr);
984 0 : if (r < 0)
985 0 : return r;
986 : else
987 0 : log_dhcp_server(server, "FORCERENEW");
988 : }
989 :
990 0 : return r;
991 : }
|