]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/loopback-setup.c
rtnl: add call_async and call_async_cancel
[thirdparty/systemd.git] / src / core / loopback-setup.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
af5bc85d
LP
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
5430f7f2
LP
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
af5bc85d
LP
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
5430f7f2 16 Lesser General Public License for more details.
af5bc85d 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
af5bc85d
LP
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"
5bfcc1c6 36#include "socket-util.h"
af5bc85d
LP
37
38#define NLMSG_TAIL(nmsg) \
39 ((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
40
41static 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
59static 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
2c3ff76e
LP
64 l = sendto(fd, buf, buf_len, flags, sa, sa_len);
65 if (l >= 0)
af5bc85d
LP
66 return l;
67
68 if (errno != EINTR)
69 return -errno;
70 }
71}
72
73static 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
2c3ff76e
LP
78 l = recvfrom(fd, buf, buf_len, flags, sa, sa_len);
79 if (l >= 0)
af5bc85d
LP
80 return l;
81
82 if (errno != EINTR)
83 return -errno;
84 }
85}
86
5bfcc1c6 87static int add_adresses(int fd, int if_loopback, unsigned *requests) {
af5bc85d
LP
88 union {
89 struct sockaddr sa;
90 struct sockaddr_nl nl;
b92bea5d
ZJS
91 } sa = {
92 .nl.nl_family = AF_NETLINK,
93 };
94
af5bc85d
LP
95 union {
96 struct nlmsghdr header;
97 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
98 NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
99 RTA_LENGTH(sizeof(struct in6_addr))];
b92bea5d
ZJS
100 } request = {
101 .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
102 .header.nlmsg_type = RTM_NEWADDR,
103 .header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK,
104 .header.nlmsg_seq = *requests + 1,
105 };
af5bc85d
LP
106
107 struct ifaddrmsg *ifaddrmsg;
108 uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
109 int r;
110
af5bc85d
LP
111 ifaddrmsg = NLMSG_DATA(&request.header);
112 ifaddrmsg->ifa_family = AF_INET;
113 ifaddrmsg->ifa_prefixlen = 8;
114 ifaddrmsg->ifa_flags = IFA_F_PERMANENT;
115 ifaddrmsg->ifa_scope = RT_SCOPE_HOST;
116 ifaddrmsg->ifa_index = if_loopback;
117
b92bea5d
ZJS
118 r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL,
119 &ipv4_address, sizeof(ipv4_address));
2c3ff76e 120 if (r < 0)
af5bc85d
LP
121 return r;
122
af5bc85d
LP
123 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
124 return -errno;
5bfcc1c6
FF
125 (*requests)++;
126
127 if (!socket_ipv6_is_supported())
128 return 0;
af5bc85d
LP
129
130 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
5bfcc1c6 131 request.header.nlmsg_seq = *requests + 1;
af5bc85d
LP
132
133 ifaddrmsg->ifa_family = AF_INET6;
134 ifaddrmsg->ifa_prefixlen = 128;
135
b92bea5d
ZJS
136 r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL,
137 &in6addr_loopback, sizeof(in6addr_loopback));
2c3ff76e 138 if (r < 0)
af5bc85d
LP
139 return r;
140
141 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
142 return -errno;
5bfcc1c6 143 (*requests)++;
af5bc85d
LP
144
145 return 0;
146}
147
5bfcc1c6 148static int start_interface(int fd, int if_loopback, unsigned *requests) {
af5bc85d
LP
149 union {
150 struct sockaddr sa;
151 struct sockaddr_nl nl;
b92bea5d
ZJS
152 } sa = {
153 .nl.nl_family = AF_NETLINK,
154 };
155
af5bc85d
LP
156 union {
157 struct nlmsghdr header;
158 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
159 NLMSG_ALIGN(sizeof(struct ifinfomsg))];
b92bea5d
ZJS
160 } request = {
161 .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
162 .header.nlmsg_type = RTM_NEWLINK,
163 .header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK,
164 .header.nlmsg_seq = *requests + 1,
165 };
af5bc85d
LP
166
167 struct ifinfomsg *ifinfomsg;
168
af5bc85d
LP
169 ifinfomsg = NLMSG_DATA(&request.header);
170 ifinfomsg->ifi_family = AF_UNSPEC;
171 ifinfomsg->ifi_index = if_loopback;
172 ifinfomsg->ifi_flags = IFF_UP;
173 ifinfomsg->ifi_change = IFF_UP;
174
af5bc85d
LP
175 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
176 return -errno;
177
5bfcc1c6
FF
178 (*requests)++;
179
af5bc85d
LP
180 return 0;
181}
182
5bfcc1c6 183static int read_response(int fd, unsigned requests_max) {
af5bc85d
LP
184 union {
185 struct sockaddr sa;
186 struct sockaddr_nl nl;
187 } sa;
188 union {
189 struct nlmsghdr header;
190 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
191 NLMSG_ALIGN(sizeof(struct nlmsgerr))];
192 } response;
193
194 ssize_t l;
195 socklen_t sa_len = sizeof(sa);
196 struct nlmsgerr *nlmsgerr;
197
2c3ff76e
LP
198 l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len);
199 if (l < 0)
af5bc85d
LP
200 return -errno;
201
202 if (sa_len != sizeof(sa.nl) ||
203 sa.nl.nl_family != AF_NETLINK)
204 return -EIO;
205
206 if (sa.nl.nl_pid != 0)
207 return 0;
208
209 if ((size_t) l < sizeof(struct nlmsghdr))
210 return -EIO;
211
212 if (response.header.nlmsg_type != NLMSG_ERROR ||
213 (pid_t) response.header.nlmsg_pid != getpid() ||
5bfcc1c6 214 response.header.nlmsg_seq >= requests_max)
af5bc85d
LP
215 return 0;
216
217 if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
218 response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
219 return -EIO;
220
221 nlmsgerr = NLMSG_DATA(&response.header);
222
2c3ff76e 223 if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST)
af5bc85d
LP
224 return nlmsgerr->error;
225
226 return response.header.nlmsg_seq;
227}
228
2c3ff76e 229static int check_loopback(void) {
e62d8c39 230 int r;
7fd1b19b 231 _cleanup_close_ int fd;
2c3ff76e
LP
232 union {
233 struct sockaddr sa;
234 struct sockaddr_in in;
b92bea5d
ZJS
235 } sa = {
236 .in.sin_family = AF_INET,
237 .in.sin_addr.s_addr = INADDR_LOOPBACK,
238 };
2c3ff76e
LP
239
240 /* If we failed to set up the loop back device, check whether
241 * it might already be set up */
242
243 fd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
244 if (fd < 0)
245 return -errno;
246
2c3ff76e
LP
247 if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0)
248 r = 1;
249 else
250 r = errno == EADDRNOTAVAIL ? 0 : -errno;
251
2c3ff76e
LP
252 return r;
253}
254
af5bc85d
LP
255int loopback_setup(void) {
256 int r, if_loopback;
257 union {
258 struct sockaddr sa;
259 struct sockaddr_nl nl;
b92bea5d
ZJS
260 } sa = {
261 .nl.nl_family = AF_NETLINK,
262 };
5bfcc1c6 263 unsigned requests = 0, i;
7fd1b19b 264 _cleanup_close_ int fd = -1;
2c3ff76e 265 bool eperm = false;
af5bc85d
LP
266
267 errno = 0;
2c3ff76e
LP
268 if_loopback = (int) if_nametoindex("lo");
269 if (if_loopback <= 0)
af5bc85d
LP
270 return errno ? -errno : -ENODEV;
271
2c3ff76e
LP
272 fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
273 if (fd < 0)
af5bc85d
LP
274 return -errno;
275
af5bc85d
LP
276 if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
277 r = -errno;
e62d8c39 278 goto error;
af5bc85d
LP
279 }
280
2c3ff76e
LP
281 r = add_adresses(fd, if_loopback, &requests);
282 if (r < 0)
e62d8c39 283 goto error;
af5bc85d 284
2c3ff76e
LP
285 r = start_interface(fd, if_loopback, &requests);
286 if (r < 0)
e62d8c39 287 goto error;
af5bc85d 288
5bfcc1c6 289 for (i = 0; i < requests; i++) {
2c3ff76e
LP
290 r = read_response(fd, requests);
291
292 if (r == -EPERM)
293 eperm = true;
294 else if (r < 0)
e62d8c39 295 goto error;
5bfcc1c6 296 }
af5bc85d 297
2c3ff76e
LP
298 if (eperm && check_loopback() < 0) {
299 r = -EPERM;
e62d8c39 300 goto error;
2c3ff76e
LP
301 }
302
e62d8c39 303 return 0;
af5bc85d 304
e62d8c39
ZJS
305error:
306 log_warning("Failed to configure loopback device: %s", strerror(-r));
af5bc85d
LP
307 return r;
308}