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