chiark / gitweb /
sd-dhcp-server: remove unused cleanup function
[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 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 || requested_broadcast(req) || type == DHCP_NAK)
344                 return dhcp_server_send_udp(server, destination, &packet->dhcp,
345                                             sizeof(DHCPMessage) + optoffset);
346         else
347                 /* we cannot send UDP packet to specific MAC address when the address is
348                    not yet configured, so must fall back to raw packets */
349                 return dhcp_server_send_unicast_raw(server, packet,
350                                                     sizeof(DHCPPacket) + optoffset);
351 }
352
353 static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
354                                uint8_t type, size_t *_optoffset, DHCPRequest *req) {
355         _cleanup_free_ DHCPPacket *packet = NULL;
356         size_t optoffset;
357         int r;
358
359         assert(server);
360         assert(ret);
361         assert(_optoffset);
362         assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
363
364         packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
365         if (!packet)
366                 return -ENOMEM;
367
368         r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
369                               type, req->max_optlen, &optoffset);
370         if (r < 0)
371                 return r;
372
373         packet->dhcp.flags = req->message->flags;
374         packet->dhcp.giaddr = req->message->giaddr;
375         memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
376
377         *_optoffset = optoffset;
378         *ret = packet;
379         packet = NULL;
380
381         return 0;
382 }
383
384 static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
385         _cleanup_free_ DHCPPacket *packet = NULL;
386         size_t offset;
387         be32_t lease_time;
388         int r;
389
390         r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
391         if (r < 0)
392                 return r;
393
394         packet->dhcp.yiaddr = address;
395
396         lease_time = htobe32(req->lifetime);
397         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
398                                DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
399         if (r < 0)
400                 return r;
401
402         r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
403         if (r < 0)
404                 return r;
405
406         return 0;
407 }
408
409 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
410         _cleanup_free_ DHCPPacket *packet = NULL;
411         size_t offset;
412         be32_t lease_time;
413         int r;
414
415         r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
416         if (r < 0)
417                 return r;
418
419         packet->dhcp.yiaddr = address;
420
421         lease_time = htobe32(req->lifetime);
422         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
423                                DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
424         if (r < 0)
425                 return r;
426
427         r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
428         if (r < 0)
429                 return r;
430
431         return 0;
432 }
433
434 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
435         _cleanup_free_ DHCPPacket *packet = NULL;
436         size_t offset;
437         int r;
438
439         r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
440         if (r < 0)
441                 return r;
442
443         r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
444         if (r < 0)
445                 return r;
446
447         return 0;
448 }
449
450 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
451                          void *user_data) {
452         DHCPRequest *req = user_data;
453
454         assert(req);
455
456         switch(code) {
457         case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
458                 if (len == 4)
459                         req->lifetime = be32toh(*(be32_t*)option);
460
461                 break;
462         case DHCP_OPTION_REQUESTED_IP_ADDRESS:
463                 if (len == 4)
464                         req->requested_ip = *(be32_t*)option;
465
466                 break;
467         case DHCP_OPTION_SERVER_IDENTIFIER:
468                 if (len == 4)
469                         req->server_id = *(be32_t*)option;
470
471                 break;
472         case DHCP_OPTION_CLIENT_IDENTIFIER:
473                 if (len >= 2) {
474                         uint8_t *data;
475
476                         data = memdup(option, len);
477                         if (!data)
478                                 return -ENOMEM;
479
480                         free(req->client_id.data);
481                         req->client_id.data = data;
482                         req->client_id.length = len;
483                 }
484
485                 break;
486         case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
487                 if (len == 2)
488                         req->max_optlen = be16toh(*(be16_t*)option) -
489                                           - sizeof(DHCPPacket);
490
491                 break;
492         }
493
494         return 0;
495 }
496
497 static void dhcp_request_free(DHCPRequest *req) {
498         if (!req)
499                 return;
500
501         free(req->client_id.data);
502         free(req);
503 }
504
505 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
506 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
507
508 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
509         assert(req);
510         assert(message);
511
512         req->message = message;
513
514         /* set client id based on mac address if client did not send an explicit one */
515         if (!req->client_id.data) {
516                 uint8_t *data;
517
518                 data = new0(uint8_t, ETH_ALEN + 1);
519                 if (!data)
520                         return -ENOMEM;
521
522                 req->client_id.length = ETH_ALEN + 1;
523                 req->client_id.data = data;
524                 req->client_id.data[0] = 0x01;
525                 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
526         }
527
528         if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
529                 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
530
531         if (!req->lifetime)
532                 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
533
534         return 0;
535 }
536
537 static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
538         assert(server);
539
540         if (!server->pool_size)
541                 return -EINVAL;
542
543         if (be32toh(requested_ip) < be32toh(server->pool_start) ||
544             be32toh(requested_ip) >= be32toh(server->pool_start) +
545                                              + server->pool_size)
546                 return -EINVAL;
547
548         return be32toh(requested_ip) - be32toh(server->pool_start);
549 }
550
551 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
552                                size_t length) {
553         _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
554         DHCPLease *existing_lease;
555         int type, r;
556
557         assert(server);
558         assert(message);
559
560         if (message->op != BOOTREQUEST ||
561             message->htype != ARPHRD_ETHER ||
562             message->hlen != ETHER_ADDR_LEN)
563                 return 0;
564
565         req = new0(DHCPRequest, 1);
566         if (!req)
567                 return -ENOMEM;
568
569         type = dhcp_option_parse(message, length, parse_request, req);
570         if (type < 0)
571                 return 0;
572
573         r = ensure_sane_request(req, message);
574         if (r < 0)
575                 /* this only fails on critical errors */
576                 return r;
577
578         existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
579
580         switch(type) {
581         case DHCP_DISCOVER:
582         {
583                 be32_t address = INADDR_ANY;
584                 unsigned i;
585
586                 log_dhcp_server(server, "DISCOVER (0x%x)",
587                                 be32toh(req->message->xid));
588
589                 if (!server->pool_size)
590                         /* no pool allocated */
591                         return 0;
592
593                 /* for now pick a random free address from the pool */
594                 if (existing_lease)
595                         address = existing_lease->address;
596                 else {
597                         for (i = 0; i < server->pool_size; i++) {
598                                 if (!server->bound_leases[server->next_offer]) {
599                                         address = htobe32(be32toh(server->pool_start) + server->next_offer);
600                                         break;
601                                 } else
602                                         server->next_offer = (server->next_offer + 1) % server->pool_size;
603                         }
604                 }
605
606                 if (address == INADDR_ANY)
607                         /* no free addresses left */
608                         return 0;
609
610                 r = server_send_offer(server, req, address);
611                 if (r < 0) {
612                         /* this only fails on critical errors */
613                         log_dhcp_server(server, "could not send offer: %s",
614                                         strerror(-r));
615                         return r;
616                 } else {
617                         log_dhcp_server(server, "OFFER (0x%x)",
618                                         be32toh(req->message->xid));
619                         return DHCP_OFFER;
620                 }
621
622                 break;
623         }
624         case DHCP_DECLINE:
625                 log_dhcp_server(server, "DECLINE (0x%x)",
626                                 be32toh(req->message->xid));
627
628                 /* TODO: make sure we don't offer this address again */
629
630                 return 1;
631
632                 break;
633         case DHCP_REQUEST:
634         {
635                 be32_t address;
636                 bool init_reboot = false;
637                 int pool_offset;
638
639                 /* see RFC 2131, section 4.3.2 */
640
641                 if (req->server_id) {
642                         log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
643                                         be32toh(req->message->xid));
644
645                         /* SELECTING */
646                         if (req->server_id != server->address)
647                                 /* client did not pick us */
648                                 return 0;
649
650                         if (req->message->ciaddr)
651                                 /* this MUST be zero */
652                                 return 0;
653
654                         if (!req->requested_ip)
655                                 /* this must be filled in with the yiaddr
656                                    from the chosen OFFER */
657                                 return 0;
658
659                         address = req->requested_ip;
660                 } else if (req->requested_ip) {
661                         log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
662                                         be32toh(req->message->xid));
663
664                         /* INIT-REBOOT */
665                         if (req->message->ciaddr)
666                                 /* this MUST be zero */
667                                 return 0;
668
669                         /* TODO: check more carefully if IP is correct */
670                         address = req->requested_ip;
671                         init_reboot = true;
672                 } else {
673                         log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
674                                         be32toh(req->message->xid));
675
676                         /* REBINDING / RENEWING */
677                         if (!req->message->ciaddr)
678                                 /* this MUST be filled in with clients IP address */
679                                 return 0;
680
681                         address = req->message->ciaddr;
682                 }
683
684                 pool_offset = get_pool_offset(server, address);
685
686                 /* verify that the requested address is from the pool, and either
687                    owned by the current client or free */
688                 if (pool_offset >= 0 &&
689                     server->bound_leases[pool_offset] == existing_lease) {
690                         DHCPLease *lease;
691                         usec_t time_now;
692
693                         if (!existing_lease) {
694                                 lease = new0(DHCPLease, 1);
695                                 lease->address = req->requested_ip;
696                                 lease->client_id.data = memdup(req->client_id.data,
697                                                                req->client_id.length);
698                                 if (!lease->client_id.data) {
699                                         free(lease);
700                                         return -ENOMEM;
701                                 }
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         case DHCP_RELEASE: {
747                 int pool_offset;
748
749                 log_dhcp_server(server, "RELEASE (0x%x)",
750                                 be32toh(req->message->xid));
751
752                 if (!existing_lease)
753                         return 0;
754
755                 if (existing_lease->address != req->message->ciaddr)
756                         return 0;
757
758                 pool_offset = get_pool_offset(server, req->message->ciaddr);
759                 if (pool_offset < 0)
760                         return 0;
761
762                 if (server->bound_leases[pool_offset] == existing_lease) {
763                         server->bound_leases[pool_offset] = NULL;
764                         hashmap_remove(server->leases_by_client_id, existing_lease);
765                         dhcp_lease_free(existing_lease);
766
767                         return 1;
768                 } else
769                         return 0;
770         }
771         }
772
773         return 0;
774 }
775
776 static int server_receive_message(sd_event_source *s, int fd,
777                                   uint32_t revents, void *userdata) {
778         _cleanup_free_ DHCPMessage *message = NULL;
779         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
780         sd_dhcp_server *server = userdata;
781         struct iovec iov = {};
782         struct msghdr msg = {
783                 .msg_iov = &iov,
784                 .msg_iovlen = 1,
785                 .msg_control = cmsgbuf,
786                 .msg_controllen = sizeof(cmsgbuf),
787         };
788         struct cmsghdr *cmsg;
789         int buflen = 0, len, r;
790
791         assert(server);
792
793         r = ioctl(fd, FIONREAD, &buflen);
794         if (r < 0)
795                 return r;
796         if (buflen < 0)
797                 return -EIO;
798
799         message = malloc0(buflen);
800         if (!message)
801                 return -ENOMEM;
802
803         iov.iov_base = message;
804         iov.iov_len = buflen;
805
806         len = recvmsg(fd, &msg, 0);
807         if (len < buflen)
808                 return 0;
809         else if ((size_t)len < sizeof(DHCPMessage))
810                 return 0;
811
812         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
813                 if (cmsg->cmsg_level == IPPROTO_IP &&
814                     cmsg->cmsg_type == IP_PKTINFO &&
815                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
816                         struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
817
818                         /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
819                         if (server->index != info->ipi_ifindex)
820                                 return 0;
821
822                         break;
823                 }
824         }
825
826         return dhcp_server_handle_message(server, message, (size_t)len);
827 }
828
829 int sd_dhcp_server_start(sd_dhcp_server *server) {
830         int r;
831
832         assert_return(server, -EINVAL);
833         assert_return(server->event, -EINVAL);
834         assert_return(!server->receive_message, -EBUSY);
835         assert_return(server->fd_raw == -1, -EBUSY);
836         assert_return(server->fd == -1, -EBUSY);
837         assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
838
839         r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
840         if (r < 0) {
841                 r = -errno;
842                 sd_dhcp_server_stop(server);
843                 return r;
844         }
845         server->fd_raw = r;
846
847         r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
848         if (r < 0) {
849                 sd_dhcp_server_stop(server);
850                 return r;
851         }
852         server->fd = r;
853
854         r = sd_event_add_io(server->event, &server->receive_message,
855                             server->fd, EPOLLIN,
856                             server_receive_message, server);
857         if (r < 0) {
858                 sd_dhcp_server_stop(server);
859                 return r;
860         }
861
862         r = sd_event_source_set_priority(server->receive_message,
863                                          server->event_priority);
864         if (r < 0) {
865                 sd_dhcp_server_stop(server);
866                 return r;
867         }
868
869         log_dhcp_server(server, "STARTED");
870
871         return 0;
872 }