chiark / gitweb /
core: drop KillMode parameter from KillUnit() bus call
[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, fd;
233         union {
234                 struct sockaddr sa;
235                 struct sockaddr_in in;
236         } sa;
237
238         /* If we failed to set up the loop back device, check whether
239          * it might already be set up */
240
241         fd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
242         if (fd < 0)
243                 return -errno;
244
245         zero(sa);
246         sa.in.sin_family = AF_INET;
247         sa.in.sin_addr.s_addr = INADDR_LOOPBACK;
248
249         if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0)
250                 r = 1;
251         else
252                 r = errno == EADDRNOTAVAIL ? 0 : -errno;
253
254         close_nointr_nofail(fd);
255
256         return r;
257 }
258
259 int loopback_setup(void) {
260         int r, if_loopback;
261         union {
262                 struct sockaddr sa;
263                 struct sockaddr_nl nl;
264         } sa;
265         unsigned requests = 0, i;
266         int fd;
267         bool eperm = false;
268
269         errno = 0;
270         if_loopback = (int) if_nametoindex("lo");
271         if (if_loopback <= 0)
272                 return errno ? -errno : -ENODEV;
273
274         fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
275         if (fd < 0)
276                 return -errno;
277
278         zero(sa);
279         sa.nl.nl_family = AF_NETLINK;
280         if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
281                 r = -errno;
282                 goto finish;
283         }
284
285         r = add_adresses(fd, if_loopback, &requests);
286         if (r < 0)
287                 goto finish;
288
289         r = start_interface(fd, if_loopback, &requests);
290         if (r < 0)
291                 goto finish;
292
293         for (i = 0; i < requests; i++) {
294                 r = read_response(fd, requests);
295
296                 if (r == -EPERM)
297                         eperm = true;
298                 else if (r  < 0)
299                         goto finish;
300         }
301
302         if (eperm && check_loopback() < 0) {
303                 r = -EPERM;
304                 goto finish;
305         }
306
307         r = 0;
308
309 finish:
310         if (r < 0)
311                 log_warning("Failed to configure loopback device: %s", strerror(-r));
312
313         if (fd >= 0)
314                 close_nointr_nofail(fd);
315
316         return r;
317 }