chiark / gitweb /
sd-dhcp-server: add dummy DECLINE support
[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_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 }