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