chiark / gitweb /
bfd0d93204573f463711290611d3f97a57bdc155
[elogind.git] / src / core / loopback-setup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <sys/socket.h>
24 #include <net/if.h>
25 #include <asm/types.h>
26 #include <netinet/in.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <linux/netlink.h>
31 #include <linux/rtnetlink.h>
32
33 #include "util.h"
34 #include "macro.h"
35 #include "loopback-setup.h"
36 #include "socket-util.h"
37
38 #define NLMSG_TAIL(nmsg)                                                \
39         ((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
40
41 static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type, const void *data, size_t data_length) {
42         size_t length;
43         struct rtattr *rta;
44
45         length = RTA_LENGTH(data_length);
46
47         if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length)
48                 return -E2BIG;
49
50         rta = NLMSG_TAIL(n);
51         rta->rta_type = type;
52         rta->rta_len = length;
53         memcpy(RTA_DATA(rta), data, data_length);
54         n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length);
55
56         return 0;
57 }
58
59 static ssize_t sendto_loop(int fd, const void *buf, size_t buf_len, int flags, const struct sockaddr *sa, socklen_t sa_len) {
60
61         for (;;) {
62                 ssize_t l;
63
64                 l = sendto(fd, buf, buf_len, flags, sa, sa_len);
65                 if (l >= 0)
66                         return l;
67
68                 if (errno != EINTR)
69                         return -errno;
70         }
71 }
72
73 static ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) {
74
75         for (;;) {
76                 ssize_t l;
77
78                 l = recvfrom(fd, buf, buf_len, flags, sa, sa_len);
79                 if (l >= 0)
80                         return l;
81
82                 if (errno != EINTR)
83                         return -errno;
84         }
85 }
86
87 static int add_adresses(int fd, int if_loopback, unsigned *requests) {
88         union {
89                 struct sockaddr sa;
90                 struct sockaddr_nl nl;
91         } sa;
92         union {
93                 struct nlmsghdr header;
94                 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
95                             NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
96                             RTA_LENGTH(sizeof(struct in6_addr))];
97         } request;
98
99         struct ifaddrmsg *ifaddrmsg;
100         uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
101         int r;
102
103         zero(request);
104
105         request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
106         request.header.nlmsg_type = RTM_NEWADDR;
107         request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK;
108         request.header.nlmsg_seq = *requests + 1;
109
110         ifaddrmsg = NLMSG_DATA(&request.header);
111         ifaddrmsg->ifa_family = AF_INET;
112         ifaddrmsg->ifa_prefixlen = 8;
113         ifaddrmsg->ifa_flags = IFA_F_PERMANENT;
114         ifaddrmsg->ifa_scope = RT_SCOPE_HOST;
115         ifaddrmsg->ifa_index = if_loopback;
116
117         r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address));
118         if (r < 0)
119                 return r;
120
121         zero(sa);
122         sa.nl.nl_family = AF_NETLINK;
123
124         if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
125                 return -errno;
126         (*requests)++;
127
128         if (!socket_ipv6_is_supported())
129                 return 0;
130
131         request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
132         request.header.nlmsg_seq = *requests + 1;
133
134         ifaddrmsg->ifa_family = AF_INET6;
135         ifaddrmsg->ifa_prefixlen = 128;
136
137         r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback));
138         if (r < 0)
139                 return r;
140
141         if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
142                 return -errno;
143         (*requests)++;
144
145         return 0;
146 }
147
148 static int start_interface(int fd, int if_loopback, unsigned *requests) {
149         union {
150                 struct sockaddr sa;
151                 struct sockaddr_nl nl;
152         } sa;
153         union {
154                 struct nlmsghdr header;
155                 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
156                             NLMSG_ALIGN(sizeof(struct ifinfomsg))];
157         } request;
158
159         struct ifinfomsg *ifinfomsg;
160
161         zero(request);
162
163         request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
164         request.header.nlmsg_type = RTM_NEWLINK;
165         request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
166         request.header.nlmsg_seq = *requests + 1;
167
168         ifinfomsg = NLMSG_DATA(&request.header);
169         ifinfomsg->ifi_family = AF_UNSPEC;
170         ifinfomsg->ifi_index = if_loopback;
171         ifinfomsg->ifi_flags = IFF_UP;
172         ifinfomsg->ifi_change = IFF_UP;
173
174         zero(sa);
175         sa.nl.nl_family = AF_NETLINK;
176
177         if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
178                 return -errno;
179
180         (*requests)++;
181
182         return 0;
183 }
184
185 static int read_response(int fd, unsigned requests_max) {
186         union {
187                 struct sockaddr sa;
188                 struct sockaddr_nl nl;
189         } sa;
190         union {
191                 struct nlmsghdr header;
192                 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
193                             NLMSG_ALIGN(sizeof(struct nlmsgerr))];
194         } response;
195
196         ssize_t l;
197         socklen_t sa_len = sizeof(sa);
198         struct nlmsgerr *nlmsgerr;
199
200         l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len);
201         if (l < 0)
202                 return -errno;
203
204         if (sa_len != sizeof(sa.nl) ||
205             sa.nl.nl_family != AF_NETLINK)
206                 return -EIO;
207
208         if (sa.nl.nl_pid != 0)
209                 return 0;
210
211         if ((size_t) l < sizeof(struct nlmsghdr))
212                 return -EIO;
213
214         if (response.header.nlmsg_type != NLMSG_ERROR ||
215             (pid_t) response.header.nlmsg_pid != getpid() ||
216             response.header.nlmsg_seq >= requests_max)
217                 return 0;
218
219         if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
220             response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
221                 return -EIO;
222
223         nlmsgerr = NLMSG_DATA(&response.header);
224
225         if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST)
226                 return nlmsgerr->error;
227
228         return response.header.nlmsg_seq;
229 }
230
231 static int check_loopback(void) {
232         int r;
233         int _cleanup_close_ fd;
234         union {
235                 struct sockaddr sa;
236                 struct sockaddr_in in;
237         } sa;
238
239         /* If we failed to set up the loop back device, check whether
240          * it might already be set up */
241
242         fd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
243         if (fd < 0)
244                 return -errno;
245
246         zero(sa);
247         sa.in.sin_family = AF_INET;
248         sa.in.sin_addr.s_addr = INADDR_LOOPBACK;
249
250         if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0)
251                 r = 1;
252         else
253                 r = errno == EADDRNOTAVAIL ? 0 : -errno;
254
255         return r;
256 }
257
258 int loopback_setup(void) {
259         int r, if_loopback;
260         union {
261                 struct sockaddr sa;
262                 struct sockaddr_nl nl;
263         } sa;
264         unsigned requests = 0, i;
265         int _cleanup_close_ fd = -1;
266         bool eperm = false;
267
268         errno = 0;
269         if_loopback = (int) if_nametoindex("lo");
270         if (if_loopback <= 0)
271                 return errno ? -errno : -ENODEV;
272
273         fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
274         if (fd < 0)
275                 return -errno;
276
277         zero(sa);
278         sa.nl.nl_family = AF_NETLINK;
279         if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
280                 r = -errno;
281                 goto error;
282         }
283
284         r = add_adresses(fd, if_loopback, &requests);
285         if (r < 0)
286                 goto error;
287
288         r = start_interface(fd, if_loopback, &requests);
289         if (r < 0)
290                 goto error;
291
292         for (i = 0; i < requests; i++) {
293                 r = read_response(fd, requests);
294
295                 if (r == -EPERM)
296                         eperm = true;
297                 else if (r  < 0)
298                         goto error;
299         }
300
301         if (eperm && check_loopback() < 0) {
302                 r = -EPERM;
303                 goto error;
304         }
305
306         return 0;
307
308 error:
309         log_warning("Failed to configure loopback device: %s", strerror(-r));
310         return r;
311 }