chiark / gitweb /
sd-dhcp-server: add basic NAK 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 "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         /* for one minute */
341         lease_time = htobe32(DHCP_DEFAULT_LEASE_TIME);
342         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
343                                DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
344         if (r < 0)
345                 return r;
346
347         r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
348         if (r < 0)
349                 return r;
350
351         return 0;
352 }
353
354 static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
355         _cleanup_free_ DHCPPacket *packet = NULL;
356         size_t offset;
357         be32_t lease_time;
358         int r;
359
360         r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
361         if (r < 0)
362                 return r;
363
364         packet->dhcp.yiaddr = address;
365
366         /* for ten seconds */
367         lease_time = htobe32(DHCP_DEFAULT_LEASE_TIME);
368         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
369                                DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
370         if (r < 0)
371                 return r;
372
373         r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
374         if (r < 0)
375                 return r;
376
377         return 0;
378 }
379
380 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
381         _cleanup_free_ DHCPPacket *packet = NULL;
382         size_t offset;
383         int r;
384
385         r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
386         if (r < 0)
387                 return r;
388
389         r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
390         if (r < 0)
391                 return r;
392
393         return 0;
394 }
395
396 static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
397                          void *user_data) {
398         DHCPRequest *req = user_data;
399
400         assert(req);
401
402         switch(code) {
403         case DHCP_OPTION_REQUESTED_IP_ADDRESS:
404                 if (len == 4)
405                         req->requested_ip = *(be32_t*)option;
406
407                 break;
408         case DHCP_OPTION_SERVER_IDENTIFIER:
409                 if (len == 4)
410                         req->server_id = *(be32_t*)option;
411
412                 break;
413         case DHCP_OPTION_CLIENT_IDENTIFIER:
414                 if (len >= 2) {
415                         uint8_t *data;
416
417                         data = memdup(option, len);
418                         if (!data)
419                                 return -ENOMEM;
420
421                         free(req->client_id.data);
422                         req->client_id.data = data;
423                         req->client_id.length = len;
424                 }
425
426                 break;
427         case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
428                 if (len == 2)
429                         req->max_optlen = be16toh(*(be16_t*)option) -
430                                           - sizeof(DHCPPacket);
431
432                 break;
433         }
434
435         return 0;
436 }
437
438 static void dhcp_request_free(DHCPRequest *req) {
439         if (!req)
440                 return;
441
442         free(req->client_id.data);
443         free(req);
444 }
445
446 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
447 #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep)
448
449 static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) {
450         assert(req);
451         assert(message);
452
453         req->message = message;
454
455         /* set client id based on mac address if client did not send an explicit one */
456         if (!req->client_id.data) {
457                 uint8_t *data;
458
459                 data = new0(uint8_t, ETH_ALEN + 1);
460                 if (!data)
461                         return -ENOMEM;
462
463                 req->client_id.length = ETH_ALEN + 1;
464                 req->client_id.data = data;
465                 req->client_id.data[0] = 0x01;
466                 memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN);
467         }
468
469         if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
470                 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
471
472         return 0;
473 }
474
475 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
476                                size_t length) {
477         _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
478         int type, r;
479
480         assert(server);
481         assert(message);
482
483         if (message->op != BOOTREQUEST ||
484             message->htype != ARPHRD_ETHER ||
485             message->hlen != ETHER_ADDR_LEN)
486                 return 0;
487
488         req = new0(DHCPRequest, 1);
489         if (!req)
490                 return -ENOMEM;
491
492         type = dhcp_option_parse(message, length, parse_request, req);
493         if (type < 0)
494                 return 0;
495
496         r = ensure_sane_request(req, message);
497         if (r < 0)
498                 /* this only fails on critical errors */
499                 return r;
500
501         switch(type) {
502         case DHCP_DISCOVER:
503         {
504                 be32_t address;
505
506                 log_dhcp_server(server, "DISCOVER (0x%x)",
507                                 be32toh(req->message->xid));
508
509                 if (!server->pool_size)
510                         /* no pool allocated */
511                         return 0;
512
513                 /* for now pick a random address from the pool */
514                 address = htobe32(be32toh(server->pool_start) +
515                                       (random_u32() % server->pool_size));
516
517                 r = server_send_offer(server, req, address);
518                 if (r < 0) {
519                         /* this only fails on critical errors */
520                         log_dhcp_server(server, "could not send offer: %s",
521                                         strerror(-r));
522                         return r;
523                 } else {
524                         log_dhcp_server(server, "OFFER (0x%x)",
525                                         be32toh(req->message->xid));
526                         return DHCP_OFFER;
527                 }
528
529                 break;
530         }
531         case DHCP_REQUEST:
532         {
533                 be32_t address;
534                 bool init_reboot = false;
535
536                 /* see RFC 2131, section 4.3.2 */
537
538                 if (req->server_id) {
539                         log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
540                                         be32toh(req->message->xid));
541
542                         /* SELECTING */
543                         if (req->server_id != server->address)
544                                 /* client did not pick us */
545                                 return 0;
546
547                         if (req->message->ciaddr)
548                                 /* this MUST be zero */
549                                 return 0;
550
551                         if (!req->requested_ip)
552                                 /* this must be filled in with the yiaddr
553                                    from the chosen OFFER */
554                                 return 0;
555
556                         address = req->requested_ip;
557                 } else if (req->requested_ip) {
558                         log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
559                                         be32toh(req->message->xid));
560
561                         /* INIT-REBOOT */
562                         if (req->message->ciaddr)
563                                 /* this MUST be zero */
564                                 return 0;
565
566                         /* TODO: check more carefully if IP is correct */
567                         address = req->requested_ip;
568                         init_reboot = true;
569                 } else {
570                         log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
571                                         be32toh(req->message->xid));
572
573                         /* REBINDING / RENEWING */
574                         if (!req->message->ciaddr)
575                                 /* this MUST be filled in with clients IP address */
576                                 return 0;
577
578                         address = req->message->ciaddr;
579                 }
580
581                 /* for now we just verify that the address is from the pool, not
582                    whether or not it is taken */
583                 if (htobe32(req->requested_ip) >= htobe32(server->pool_start) &&
584                     htobe32(req->requested_ip) < htobe32(server->pool_start) +
585                                                   + server->pool_size) {
586                         r = server_send_ack(server, req, address);
587                         if (r < 0) {
588                                 /* this only fails on critical errors */
589                                 log_dhcp_server(server, "could not send ack: %s",
590                                                 strerror(-r));
591                                 return r;
592                         } else {
593                                 log_dhcp_server(server, "ACK (0x%x)",
594                                                 be32toh(req->message->xid));
595                                 return DHCP_ACK;
596                         }
597                 } else if (init_reboot) {
598                         r = server_send_nak(server, req);
599                         if (r < 0) {
600                                 /* this only fails on critical errors */
601                                 log_dhcp_server(server, "could not send nak: %s",
602                                                 strerror(-r));
603                                 return r;
604                         } else {
605                                 log_dhcp_server(server, "NAK (0x%x)",
606                                                 be32toh(req->message->xid));
607                                 return DHCP_NAK;
608                         }
609                 }
610
611                 break;
612         }
613         }
614
615         return 0;
616 }
617
618 static int server_receive_message(sd_event_source *s, int fd,
619                                   uint32_t revents, void *userdata) {
620         _cleanup_free_ DHCPMessage *message = NULL;
621         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))];
622         sd_dhcp_server *server = userdata;
623         struct iovec iov = {};
624         struct msghdr msg = {
625                 .msg_iov = &iov,
626                 .msg_iovlen = 1,
627                 .msg_control = cmsgbuf,
628                 .msg_controllen = sizeof(cmsgbuf),
629         };
630         struct cmsghdr *cmsg;
631         int buflen = 0, len, r;
632
633         assert(server);
634
635         r = ioctl(fd, FIONREAD, &buflen);
636         if (r < 0)
637                 return r;
638         if (buflen < 0)
639                 return -EIO;
640
641         message = malloc0(buflen);
642         if (!message)
643                 return -ENOMEM;
644
645         iov.iov_base = message;
646         iov.iov_len = buflen;
647
648         len = recvmsg(fd, &msg, 0);
649         if (len < buflen)
650                 return 0;
651         else if ((size_t)len < sizeof(DHCPMessage))
652                 return 0;
653
654         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
655                 if (cmsg->cmsg_level == IPPROTO_IP &&
656                     cmsg->cmsg_type == IP_PKTINFO &&
657                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
658                         struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg);
659
660                         /* TODO figure out if this can be done as a filter on the socket, like for IPv6 */
661                         if (server->index != info->ipi_ifindex)
662                                 return 0;
663
664                         break;
665                 }
666         }
667
668         return dhcp_server_handle_message(server, message, (size_t)len);
669 }
670
671 int sd_dhcp_server_start(sd_dhcp_server *server) {
672         int r;
673
674         assert_return(server, -EINVAL);
675         assert_return(server->event, -EINVAL);
676         assert_return(!server->receive_message, -EBUSY);
677         assert_return(server->fd_raw == -1, -EBUSY);
678         assert_return(server->fd == -1, -EBUSY);
679         assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH);
680
681         r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
682         if (r < 0) {
683                 r = -errno;
684                 sd_dhcp_server_stop(server);
685                 return r;
686         }
687         server->fd_raw = r;
688
689         r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
690         if (r < 0) {
691                 sd_dhcp_server_stop(server);
692                 return r;
693         }
694         server->fd = r;
695
696         r = sd_event_add_io(server->event, &server->receive_message,
697                             server->fd, EPOLLIN,
698                             server_receive_message, server);
699         if (r < 0) {
700                 sd_dhcp_server_stop(server);
701                 return r;
702         }
703
704         r = sd_event_source_set_priority(server->receive_message,
705                                          server->event_priority);
706         if (r < 0) {
707                 sd_dhcp_server_stop(server);
708                 return r;
709         }
710
711         log_dhcp_server(server, "STARTED");
712
713         return 0;
714 }