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