]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/loopback-setup.c
Modernization
[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;
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;
5bfcc1c6 108 request.header.nlmsg_seq = *requests + 1;
af5bc85d
LP
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
2c3ff76e
LP
117 r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address));
118 if (r < 0)
af5bc85d
LP
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;
5bfcc1c6
FF
126 (*requests)++;
127
128 if (!socket_ipv6_is_supported())
129 return 0;
af5bc85d
LP
130
131 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
5bfcc1c6 132 request.header.nlmsg_seq = *requests + 1;
af5bc85d
LP
133
134 ifaddrmsg->ifa_family = AF_INET6;
135 ifaddrmsg->ifa_prefixlen = 128;
136
2c3ff76e
LP
137 r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback));
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;
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;
5bfcc1c6 166 request.header.nlmsg_seq = *requests + 1;
af5bc85d
LP
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
5bfcc1c6
FF
180 (*requests)++;
181
af5bc85d
LP
182 return 0;
183}
184
5bfcc1c6 185static int read_response(int fd, unsigned requests_max) {
af5bc85d
LP
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
2c3ff76e
LP
200 l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len);
201 if (l < 0)
af5bc85d
LP
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() ||
5bfcc1c6 216 response.header.nlmsg_seq >= requests_max)
af5bc85d
LP
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
2c3ff76e 225 if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST)
af5bc85d
LP
226 return nlmsgerr->error;
227
228 return response.header.nlmsg_seq;
229}
230
2c3ff76e 231static int check_loopback(void) {
e62d8c39
ZJS
232 int r;
233 int _cleanup_close_ fd;
2c3ff76e
LP
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
2c3ff76e
LP
255 return r;
256}
257
af5bc85d
LP
258int loopback_setup(void) {
259 int r, if_loopback;
260 union {
261 struct sockaddr sa;
262 struct sockaddr_nl nl;
af5bc85d 263 } sa;
5bfcc1c6 264 unsigned requests = 0, i;
e62d8c39 265 int _cleanup_close_ fd = -1;
2c3ff76e 266 bool eperm = false;
af5bc85d
LP
267
268 errno = 0;
2c3ff76e
LP
269 if_loopback = (int) if_nametoindex("lo");
270 if (if_loopback <= 0)
af5bc85d
LP
271 return errno ? -errno : -ENODEV;
272
2c3ff76e
LP
273 fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
274 if (fd < 0)
af5bc85d
LP
275 return -errno;
276
277 zero(sa);
278 sa.nl.nl_family = AF_NETLINK;
af5bc85d
LP
279 if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
280 r = -errno;
e62d8c39 281 goto error;
af5bc85d
LP
282 }
283
2c3ff76e
LP
284 r = add_adresses(fd, if_loopback, &requests);
285 if (r < 0)
e62d8c39 286 goto error;
af5bc85d 287
2c3ff76e
LP
288 r = start_interface(fd, if_loopback, &requests);
289 if (r < 0)
e62d8c39 290 goto error;
af5bc85d 291
5bfcc1c6 292 for (i = 0; i < requests; i++) {
2c3ff76e
LP
293 r = read_response(fd, requests);
294
295 if (r == -EPERM)
296 eperm = true;
297 else if (r < 0)
e62d8c39 298 goto error;
5bfcc1c6 299 }
af5bc85d 300
2c3ff76e
LP
301 if (eperm && check_loopback() < 0) {
302 r = -EPERM;
e62d8c39 303 goto error;
2c3ff76e
LP
304 }
305
e62d8c39 306 return 0;
af5bc85d 307
e62d8c39
ZJS
308error:
309 log_warning("Failed to configure loopback device: %s", strerror(-r));
af5bc85d
LP
310 return r;
311}