chiark / gitweb /
sd-dhcp-server: add support for clients requesting lease lifetime
[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 "sd-dhcp-server.h"
27 #include "dhcp-server-internal.h"
28 #include "dhcp-internal.h"
29
30 #define DHCP_DEFAULT_LEASE_TIME         60
31
32 int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *address,
33                                   size_t size) {
34         assert_return(server, -EINVAL);
35         assert_return(address, -EINVAL);
36         assert_return(address->s_addr, -EINVAL);
37         assert_return(size, -EINVAL);
38         assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
39         assert_return(!server->pool_size, -EBUSY);
40
41         server->pool_start = address->s_addr;
42         server->pool_size = size;
43
44         return 0;
45 }
46
47 int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address) {
48         assert_return(server, -EINVAL);
49         assert_return(address, -EINVAL);
50         assert_return(address->s_addr, -EINVAL);
51         assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
52
53         server->address = address->s_addr;
54
55         return 0;
56 }
57
58 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
59         if (server)
60                 assert_se(REFCNT_INC(server->n_ref) >= 2);
61
62         return server;
63 }
64
65 sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
66         if (server && REFCNT_DEC(server->n_ref) <= 0) {
67                 log_dhcp_server(server, "UNREF");
68
69                 sd_dhcp_server_stop(server);
70
71                 sd_event_unref(server->event);
72                 free(server);
73         }
74
75         return NULL;
76 }
77
78 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
79         _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
80
81         assert_return(ret, -EINVAL);
82         assert_return(ifindex > 0, -EINVAL);
83
84         server = new0(sd_dhcp_server, 1);
85         if (!server)
86                 return -ENOMEM;
87
88         server->n_ref = REFCNT_INIT;
89         server->fd_raw = -1;
90         server->fd = -1;
91         server->address = htobe32(INADDR_ANY);
92         server->index = ifindex;
93
94         *ret = server;
95         server = NULL;
96
97         return 0;
98 }
99
100 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
101         int r;
102
103         assert_return(server, -EINVAL);
104         assert_return(!server->event, -EBUSY);
105
106         if (event)
107                 server->event = sd_event_ref(event);
108         else {
109                 r = sd_event_default(&server->event);
110                 if (r < 0)
111                         return r;
112         }
113
114         server->event_priority = priority;
115
116         return 0;
117 }
118
119 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
120         assert_return(server, -EINVAL);
121
122         server->event = sd_event_unref(server->event);
123
124         return 0;
125 }
126
127 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
128         assert_return(server, NULL);
129
130         return server->event;
131 }
132
133 int sd_dhcp_server_stop(sd_dhcp_server *server) {
134         assert_return(server, -EINVAL);
135
136         server->receive_message =
137                 sd_event_source_unref(server->receive_message);
138
139         server->fd_raw = safe_close(server->fd_raw);
140         server->fd = safe_close(server->fd);
141
142         log_dhcp_server(server, "STOPPED");
143
144         return 0;
145 }
146
147 static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet,
148                                         size_t len) {
149         union sockaddr_union link = {
150                 .ll.sll_family = AF_PACKET,
151                 .ll.sll_protocol = htons(ETH_P_IP),
152                 .ll.sll_ifindex = server->index,
153                 .ll.sll_halen = ETH_ALEN,
154         };
155         int r;
156
157         assert(server);
158         assert(server->index > 0);
159         assert(server->address);
160         assert(packet);
161         assert(len > sizeof(DHCPPacket));
162
163         memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
164
165         dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
166                                       packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len);
167
168         r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
169         if (r < 0)
170                 return r;
171
172         return 0;
173 }
174
175 static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
176                                 DHCPMessage *message, size_t len) {
177         union sockaddr_union dest = {
178                 .in.sin_family = AF_INET,
179                 .in.sin_port = htobe16(DHCP_PORT_CLIENT),
180                 .in.sin_addr.s_addr = destination,
181         };
182         struct iovec iov = {
183                 .iov_base = message,
184                 .iov_len = len,
185         };
186         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {};
187         struct msghdr msg = {
188                 .msg_name = &dest,
189                 .msg_namelen = sizeof(dest.in),
190                 .msg_iov = &iov,
191                 .msg_iovlen = 1,
192                 .msg_control = cmsgbuf,
193                 .msg_controllen = sizeof(cmsgbuf),
194         };
195         struct cmsghdr *cmsg;
196         struct in_pktinfo *pktinfo;
197         int r;
198
199         assert(server);
200         assert(server->fd > 0);
201         assert(message);
202         assert(len > sizeof(DHCPMessage));
203
204         cmsg = CMSG_FIRSTHDR(&msg);
205         assert(cmsg);
206
207         cmsg->cmsg_level = IPPROTO_IP;
208         cmsg->cmsg_type = IP_PKTINFO;
209         cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
210
211         /* we attach source interface and address info to the message
212            rather than binding the socket. This will be mostly useful
213            when we gain support for arbitrary number of server addresses
214          */
215         pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg);
216         assert(pktinfo);
217
218         pktinfo->ipi_ifindex = server->index;
219         pktinfo->ipi_spec_dst.s_addr = server->address;
220
221         r = sendmsg(server->fd, &msg, 0);
222         if (r < 0)
223                 return -errno;
224
225         return 0;
226 }
227
228 static bool requested_broadcast(DHCPRequest *req) {
229         assert(req);
230
231         return req->message->flags & htobe16(0x8000);
232 }
233
234 int dhcp_server_send_packet(sd_dhcp_server *server,
235                             DHCPRequest *req, DHCPPacket *packet,
236                             int type, size_t optoffset) {
237         be32_t destination = INADDR_ANY;
238         int r;
239
240         assert(server);
241         assert(req);
242         assert(req->max_optlen);
243         assert(optoffset <= req->max_optlen);
244         assert(packet);
245
246         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
247                                DHCP_OPTION_SERVER_IDENTIFIER,
248                                4, &server->address);
249         if (r < 0)
250                 return r;
251
252         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
253                                DHCP_OPTION_END, 0, NULL);
254         if (r < 0)
255                 return r;
256
257         /* RFC 2131 Section 4.1
258
259            If the â€™giaddr’ field in a DHCP message from a client is non-zero,
260            the server sends any return messages to the â€™DHCP server’ port on the
261            BOOTP relay agent whose address appears in â€™giaddr’. If the â€™giaddr’
262            field is zero and the â€™ciaddr’ field is nonzero, then the server
263            unicasts DHCPOFFER and DHCPACK messages to the address in â€™ciaddr’.
264            If â€™giaddr’ is zero and â€™ciaddr’ is zero, and the broadcast bit is
265            set, then the server broadcasts DHCPOFFER and DHCPACK messages to
266            0xffffffff. If the broadcast bit is not set and â€™giaddr’ is zero and
267            â€™ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
268            messages to the client’s hardware address and â€™yiaddr’ address. In
269            all cases, when â€™giaddr’ is zero, the server broadcasts any DHCPNAK
270            messages to 0xffffffff.
271
272            Section 4.3.2
273
274            If â€™giaddr’ is set in the DHCPREQUEST message, the client is on a
275            different subnet. The server MUST set the broadcast bit in the
276            DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
277            client, because the client may not have a correct network address
278            or subnet mask, and the client may not be answering ARP requests.
279          */
280         if (req->message->giaddr) {
281                 destination = req->message->giaddr;
282                 if (type == DHCP_NAK)
283                         packet->dhcp.flags = htobe16(0x8000);
284         } else if (req->message->ciaddr && type != DHCP_NAK)
285                 destination = req->message->ciaddr;
286
287         if (destination || requested_broadcast(req) || type == DHCP_NAK)
288                 return dhcp_server_send_udp(server, destination, &packet->dhcp,
289                                             sizeof(DHCPMessage) + optoffset);
290         else
291                 /* we cannot send UDP packet to specific MAC address when the address is
292                    not yet configured, so must fall back to raw packets */
293                 return dhcp_server_send_unicast_raw(server, packet,
294                                                     sizeof(DHCPPacket) + optoffset);
295 }
296
297 static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
298                                uint8_t type, size_t *_optoffset, DHCPRequest *req) {
299         _cleanup_free_ DHCPPacket *packet = NULL;
300         size_t optoffset;
301         int r;
302
303         assert(server);
304         assert(ret);
305         assert(_optoffset);
306         assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK));
307
308         packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
309         if (!packet)
310                 return -ENOMEM;
311
312         r = dhcp_message_init(&packet->dhcp, BOOTREPLY, be32toh(req->message->xid),
313                               type, req->max_optlen, &optoffset);
314         if (r < 0)
315                 return r;
316
317         packet->dhcp.flags = req->message->flags;
318         packet->dhcp.giaddr = req->message->giaddr;
319         memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
320
321         *_optoffset = optoffset;
322         *ret = packet;
323         packet = NULL;
324
325         return 0;
326 }
327
328 static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
329         _cleanup_free_ DHCPPacket *packet = NULL;
330         size_t offset;
331         be32_t lease_time;
332         int r;
333
334         r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
335         if (r < 0)
336                 return r;
337
338         packet->dhcp.yiaddr = address;
339
340         lease_time = htobe32(req->lifetime);
341         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
342                                DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
343         if (r < 0)
344                 return r;
345
346         r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
347         if (r < 0)
348                 return r;
349
350         return 0;
351 }
352
353 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
354         _cleanup_free_ DHCPPacket *packet = NULL;
355         size_t offset;
356         be32_t lease_time;
357         int r;
358
359         r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
360         if (r < 0)
361                 return r;
362
363         packet->dhcp.yiaddr = address;
364
365         lease_time = htobe32(req->lifetime);
366         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
367                                DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
368         if (r < 0)
369                 return r;
370
371         r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
372         if (r < 0)
373                 return r;
374
375         return 0;
376 }
377
378 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
379         _cleanup_free_ DHCPPacket *packet = NULL;
380         size_t offset;
381         int r;
382
383         r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
384         if (r < 0)
385                 return r;
386
387         r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
388         if (r < 0)
389                 return r;
390
391         return 0;
392 }
393
394 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
395                          void *user_data) {
396         DHCPRequest *req = user_data;
397
398         assert(req);
399
400         switch(code) {
401         case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
402                 if (len == 4)
403                         req->lifetime = be32toh(*(be32_t*)option);
404
405                 break;
406         case DHCP_OPTION_REQUESTED_IP_ADDRESS:
407                 if (len == 4)
408                         req->requested_ip = *(be32_t*)option;
409
410                 break;
411         case DHCP_OPTION_SERVER_IDENTIFIER:
412                 if (len == 4)
413                         req->server_id = *(be32_t*)option;
414
415                 break;
416         case DHCP_OPTION_CLIENT_IDENTIFIER:
417                 if (len >= 2) {
418                         uint8_t *data;
419
420                         data = memdup(option, len);
421                         if (!data)
422                                 return -ENOMEM;
423
424                         free(req->client_id.data);
425                         req->client_id.data = data;
426                         req->client_id.length = len;
427                 }
428
429                 break;
430         case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
431                 if (len == 2)
432                         req->max_optlen = be16toh(*(be16_t*)option) -
433                                           - sizeof(DHCPPacket);
434
435                 break;
436         }
437
438         return 0;
439 }
440
441 static void dhcp_request_free(DHCPRequest *req) {
442         if (!req)
443                 return;
444
445         free(req->client_id.data);
446         free(req);
447 }
448
449 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
450 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
451
452 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
453         assert(req);
454         assert(message);
455
456         req->message = message;
457
458         /* set client id based on mac address if client did not send an explicit one */
459         if (!req->client_id.data) {
460                 uint8_t *data;
461
462                 data = new0(uint8_t, ETH_ALEN + 1);
463                 if (!data)
464                         return -ENOMEM;
465
466                 req->client_id.length = ETH_ALEN + 1;
467                 req->client_id.data = data;
468                 req->client_id.data[0] = 0x01;
469                 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
470         }
471
472         if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
473                 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
474
475         if (!req->lifetime)
476                 req->lifetime = DHCP_DEFAULT_LEASE_TIME;
477
478         return 0;
479 }
480
481 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
482                                size_t length) {
483         _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
484         int type, r;
485
486         assert(server);
487         assert(message);
488
489         if (message->op != BOOTREQUEST ||
490             message->htype != ARPHRD_ETHER ||
491             message->hlen != ETHER_ADDR_LEN)
492                 return 0;
493
494         req = new0(DHCPRequest, 1);
495         if (!req)
496                 return -ENOMEM;
497
498         type = dhcp_option_parse(message, length, parse_request, req);
499         if (type < 0)
500                 return 0;
501
502         r = ensure_sane_request(req, message);
503         if (r < 0)
504                 /* this only fails on critical errors */
505                 return r;
506
507         switch(type) {
508         case DHCP_DISCOVER:
509         {
510                 be32_t address;
511
512                 log_dhcp_server(server, "DISCOVER (0x%x)",
513                                 be32toh(req->message->xid));
514
515                 if (!server->pool_size)
516                         /* no pool allocated */
517                         return 0;
518
519                 /* for now pick a random address from the pool */
520                 address = htobe32(be32toh(server->pool_start) +
521                                       (random_u32() % server->pool_size));
522
523                 r = server_send_offer(server, req, address);
524                 if (r < 0) {
525                         /* this only fails on critical errors */
526                         log_dhcp_server(server, "could not send offer: %s",
527                                         strerror(-r));
528                         return r;
529                 } else {
530                         log_dhcp_server(server, "OFFER (0x%x)",
531                                         be32toh(req->message->xid));
532                         return DHCP_OFFER;
533                 }
534
535                 break;
536         }
537         case DHCP_REQUEST:
538         {
539                 be32_t address;
540                 bool init_reboot = false;
541
542                 /* see RFC 2131, section 4.3.2 */
543
544                 if (req->server_id) {
545                         log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
546                                         be32toh(req->message->xid));
547
548                         /* SELECTING */
549                         if (req->server_id != server->address)
550                                 /* client did not pick us */
551                                 return 0;
552
553                         if (req->message->ciaddr)
554                                 /* this MUST be zero */
555                                 return 0;
556
557                         if (!req->requested_ip)
558                                 /* this must be filled in with the yiaddr
559                                    from the chosen OFFER */
560                                 return 0;
561
562                         address = req->requested_ip;
563                 } else if (req->requested_ip) {
564                         log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
565                                         be32toh(req->message->xid));
566
567                         /* INIT-REBOOT */
568                         if (req->message->ciaddr)
569                                 /* this MUST be zero */
570                                 return 0;
571
572                         /* TODO: check more carefully if IP is correct */
573                         address = req->requested_ip;
574                         init_reboot = true;
575                 } else {
576                         log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
577                                         be32toh(req->message->xid));
578
579                         /* REBINDING / RENEWING */
580                         if (!req->message->ciaddr)
581                                 /* this MUST be filled in with clients IP address */
582                                 return 0;
583
584                         address = req->message->ciaddr;
585                 }
586
587                 /* for now we just verify that the address is from the pool, not
588                    whether or not it is taken */
589                 if (htobe32(req->requested_ip) >= htobe32(server->pool_start) &&
590                     htobe32(req->requested_ip) < htobe32(server->pool_start) +
591                                                   + server->pool_size) {
592                         r = server_send_ack(server, req, address);
593                         if (r < 0) {
594                                 /* this only fails on critical errors */
595                                 log_dhcp_server(server, "could not send ack: %s",
596                                                 strerror(-r));
597                                 return r;
598                         } else {
599                                 log_dhcp_server(server, "ACK (0x%x)",
600                                                 be32toh(req->message->xid));
601                                 return DHCP_ACK;
602                         }
603                 } else if (init_reboot) {
604                         r = server_send_nak(server, req);
605                         if (r < 0) {
606                                 /* this only fails on critical errors */
607                                 log_dhcp_server(server, "could not send nak: %s",
608                                                 strerror(-r));
609                                 return r;
610                         } else {
611                                 log_dhcp_server(server, "NAK (0x%x)",
612                                                 be32toh(req->message->xid));
613                                 return DHCP_NAK;
614                         }
615                 }
616
617                 break;
618         }
619         }
620
621         return 0;
622 }
623
624 static int server_receive_message(sd_event_source *s, int fd,
625                                   uint32_t revents, void *userdata) {
626         _cleanup_free_ DHCPMessage *message = NULL;
627         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
628         sd_dhcp_server *server = userdata;
629         struct iovec iov = {};
630         struct msghdr msg = {
631                 .msg_iov = &iov,
632                 .msg_iovlen = 1,
633                 .msg_control = cmsgbuf,
634                 .msg_controllen = sizeof(cmsgbuf),
635         };
636         struct cmsghdr *cmsg;
637         int buflen = 0, len, r;
638
639         assert(server);
640
641         r = ioctl(fd, FIONREAD, &buflen);
642         if (r < 0)
643                 return r;
644         if (buflen < 0)
645                 return -EIO;
646
647         message = malloc0(buflen);
648         if (!message)
649                 return -ENOMEM;
650
651         iov.iov_base = message;
652         iov.iov_len = buflen;
653
654         len = recvmsg(fd, &msg, 0);
655         if (len < buflen)
656                 return 0;
657         else if ((size_t)len < sizeof(DHCPMessage))
658                 return 0;
659
660         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
661                 if (cmsg->cmsg_level == IPPROTO_IP &&
662                     cmsg->cmsg_type == IP_PKTINFO &&
663                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
664                         struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
665
666                         /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
667                         if (server->index != info->ipi_ifindex)
668                                 return 0;
669
670                         break;
671                 }
672         }
673
674         return dhcp_server_handle_message(server, message, (size_t)len);
675 }
676
677 int sd_dhcp_server_start(sd_dhcp_server *server) {
678         int r;
679
680         assert_return(server, -EINVAL);
681         assert_return(server->event, -EINVAL);
682         assert_return(!server->receive_message, -EBUSY);
683         assert_return(server->fd_raw == -1, -EBUSY);
684         assert_return(server->fd == -1, -EBUSY);
685         assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
686
687         r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
688         if (r < 0) {
689                 r = -errno;
690                 sd_dhcp_server_stop(server);
691                 return r;
692         }
693         server->fd_raw = r;
694
695         r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
696         if (r < 0) {
697                 sd_dhcp_server_stop(server);
698                 return r;
699         }
700         server->fd = r;
701
702         r = sd_event_add_io(server->event, &server->receive_message,
703                             server->fd, EPOLLIN,
704                             server_receive_message, server);
705         if (r < 0) {
706                 sd_dhcp_server_stop(server);
707                 return r;
708         }
709
710         r = sd_event_source_set_priority(server->receive_message,
711                                          server->event_priority);
712         if (r < 0) {
713                 sd_dhcp_server_stop(server);
714                 return r;
715         }
716
717         log_dhcp_server(server, "STARTED");
718
719         return 0;
720 }