chiark / gitweb /
sd-dhcp-server: track bound leases
[elogind.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_REQUEST:
627         {
628                 be32_t address;
629                 bool init_reboot = false;
630                 int pool_offset;
631
632                 /* see RFC 2131, section 4.3.2 */
633
634                 if (req->server_id) {
635                         log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
636                                         be32toh(req->message->xid));
637
638                         /* SELECTING */
639                         if (req->server_id != server->address)
640                                 /* client did not pick us */
641                                 return 0;
642
643                         if (req->message->ciaddr)
644                                 /* this MUST be zero */
645                                 return 0;
646
647                         if (!req->requested_ip)
648                                 /* this must be filled in with the yiaddr
649                                    from the chosen OFFER */
650                                 return 0;
651
652                         address = req->requested_ip;
653                 } else if (req->requested_ip) {
654                         log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
655                                         be32toh(req->message->xid));
656
657                         /* INIT-REBOOT */
658                         if (req->message->ciaddr)
659                                 /* this MUST be zero */
660                                 return 0;
661
662                         /* TODO: check more carefully if IP is correct */
663                         address = req->requested_ip;
664                         init_reboot = true;
665                 } else {
666                         log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
667                                         be32toh(req->message->xid));
668
669                         /* REBINDING / RENEWING */
670                         if (!req->message->ciaddr)
671                                 /* this MUST be filled in with clients IP address */
672                                 return 0;
673
674                         address = req->message->ciaddr;
675                 }
676
677                 pool_offset = get_pool_offset(server, address);
678
679                 /* verify that the requested address is from the pool, and either
680                    owned by the current client or free */
681                 if (pool_offset >= 0 &&
682                     server->bound_leases[pool_offset] == existing_lease) {
683                         DHCPLease *lease;
684                         usec_t time_now;
685
686                         if (!existing_lease) {
687                                 lease = new0(DHCPLease, 1);
688                                 lease->address = req->requested_ip;
689                                 lease->client_id.data = memdup(req->client_id.data,
690                                                                req->client_id.length);
691                                 if (!lease->client_id.data)
692                                         return -ENOMEM;
693                                 lease->client_id.length = req->client_id.length;
694                         } else
695                                 lease = existing_lease;
696
697                         r = sd_event_now(server->event, CLOCK_MONOTONIC, &time_now);
698                         if (r < 0)
699                                 time_now = now(CLOCK_MONOTONIC);
700                         lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
701
702                         r = server_send_ack(server, req, address);
703                         if (r < 0) {
704                                 /* this only fails on critical errors */
705                                 log_dhcp_server(server, "could not send ack: %s",
706                                                 strerror(-r));
707
708                                 if (!existing_lease)
709                                         dhcp_lease_free(lease);
710
711                                 return r;
712                         } else {
713                                 log_dhcp_server(server, "ACK (0x%x)",
714                                                 be32toh(req->message->xid));
715
716                                 server->bound_leases[pool_offset] = lease;
717                                 hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
718
719                                 return DHCP_ACK;
720                         }
721                 } else if (init_reboot) {
722                         r = server_send_nak(server, req);
723                         if (r < 0) {
724                                 /* this only fails on critical errors */
725                                 log_dhcp_server(server, "could not send nak: %s",
726                                                 strerror(-r));
727                                 return r;
728                         } else {
729                                 log_dhcp_server(server, "NAK (0x%x)",
730                                                 be32toh(req->message->xid));
731                                 return DHCP_NAK;
732                         }
733                 }
734
735                 break;
736         }
737         }
738
739         return 0;
740 }
741
742 static int server_receive_message(sd_event_source *s, int fd,
743                                   uint32_t revents, void *userdata) {
744         _cleanup_free_ DHCPMessage *message = NULL;
745         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
746         sd_dhcp_server *server = userdata;
747         struct iovec iov = {};
748         struct msghdr msg = {
749                 .msg_iov = &iov,
750                 .msg_iovlen = 1,
751                 .msg_control = cmsgbuf,
752                 .msg_controllen = sizeof(cmsgbuf),
753         };
754         struct cmsghdr *cmsg;
755         int buflen = 0, len, r;
756
757         assert(server);
758
759         r = ioctl(fd, FIONREAD, &buflen);
760         if (r < 0)
761                 return r;
762         if (buflen < 0)
763                 return -EIO;
764
765         message = malloc0(buflen);
766         if (!message)
767                 return -ENOMEM;
768
769         iov.iov_base = message;
770         iov.iov_len = buflen;
771
772         len = recvmsg(fd, &msg, 0);
773         if (len < buflen)
774                 return 0;
775         else if ((size_t)len < sizeof(DHCPMessage))
776                 return 0;
777
778         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
779                 if (cmsg->cmsg_level == IPPROTO_IP &&
780                     cmsg->cmsg_type == IP_PKTINFO &&
781                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
782                         struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
783
784                         /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
785                         if (server->index != info->ipi_ifindex)
786                                 return 0;
787
788                         break;
789                 }
790         }
791
792         return dhcp_server_handle_message(server, message, (size_t)len);
793 }
794
795 int sd_dhcp_server_start(sd_dhcp_server *server) {
796         int r;
797
798         assert_return(server, -EINVAL);
799         assert_return(server->event, -EINVAL);
800         assert_return(!server->receive_message, -EBUSY);
801         assert_return(server->fd_raw == -1, -EBUSY);
802         assert_return(server->fd == -1, -EBUSY);
803         assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
804
805         r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
806         if (r < 0) {
807                 r = -errno;
808                 sd_dhcp_server_stop(server);
809                 return r;
810         }
811         server->fd_raw = r;
812
813         r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
814         if (r < 0) {
815                 sd_dhcp_server_stop(server);
816                 return r;
817         }
818         server->fd = r;
819
820         r = sd_event_add_io(server->event, &server->receive_message,
821                             server->fd, EPOLLIN,
822                             server_receive_message, server);
823         if (r < 0) {
824                 sd_dhcp_server_stop(server);
825                 return r;
826         }
827
828         r = sd_event_source_set_priority(server->receive_message,
829                                          server->event_priority);
830         if (r < 0) {
831                 sd_dhcp_server_stop(server);
832                 return r;
833         }
834
835         log_dhcp_server(server, "STARTED");
836
837         return 0;
838 }