]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/loopback-setup.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / 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
64 if ((l = sendto(fd, buf, buf_len, flags, sa, sa_len)) >= 0)
65 return l;
66
67 if (errno != EINTR)
68 return -errno;
69 }
70}
71
72static ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) {
73
74 for (;;) {
75 ssize_t l;
76
77 if ((l = recvfrom(fd, buf, buf_len, flags, sa, sa_len)) >= 0)
78 return l;
79
80 if (errno != EINTR)
81 return -errno;
82 }
83}
84
5bfcc1c6 85static int add_adresses(int fd, int if_loopback, unsigned *requests) {
af5bc85d
LP
86 union {
87 struct sockaddr sa;
88 struct sockaddr_nl nl;
89 } sa;
90 union {
91 struct nlmsghdr header;
92 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
93 NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
94 RTA_LENGTH(sizeof(struct in6_addr))];
95 } request;
96
97 struct ifaddrmsg *ifaddrmsg;
98 uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
99 int r;
100
101 zero(request);
102
103 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
104 request.header.nlmsg_type = RTM_NEWADDR;
105 request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK;
5bfcc1c6 106 request.header.nlmsg_seq = *requests + 1;
af5bc85d
LP
107
108 ifaddrmsg = NLMSG_DATA(&request.header);
109 ifaddrmsg->ifa_family = AF_INET;
110 ifaddrmsg->ifa_prefixlen = 8;
111 ifaddrmsg->ifa_flags = IFA_F_PERMANENT;
112 ifaddrmsg->ifa_scope = RT_SCOPE_HOST;
113 ifaddrmsg->ifa_index = if_loopback;
114
115 if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address))) < 0)
116 return r;
117
118 zero(sa);
119 sa.nl.nl_family = AF_NETLINK;
120
121 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
122 return -errno;
5bfcc1c6
FF
123 (*requests)++;
124
125 if (!socket_ipv6_is_supported())
126 return 0;
af5bc85d
LP
127
128 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
5bfcc1c6 129 request.header.nlmsg_seq = *requests + 1;
af5bc85d
LP
130
131 ifaddrmsg->ifa_family = AF_INET6;
132 ifaddrmsg->ifa_prefixlen = 128;
133
134 if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback))) < 0)
135 return r;
136
137 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
138 return -errno;
5bfcc1c6 139 (*requests)++;
af5bc85d
LP
140
141 return 0;
142}
143
5bfcc1c6 144static int start_interface(int fd, int if_loopback, unsigned *requests) {
af5bc85d
LP
145 union {
146 struct sockaddr sa;
147 struct sockaddr_nl nl;
148 } sa;
149 union {
150 struct nlmsghdr header;
151 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
152 NLMSG_ALIGN(sizeof(struct ifinfomsg))];
153 } request;
154
155 struct ifinfomsg *ifinfomsg;
156
157 zero(request);
158
159 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
160 request.header.nlmsg_type = RTM_NEWLINK;
161 request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
5bfcc1c6 162 request.header.nlmsg_seq = *requests + 1;
af5bc85d
LP
163
164 ifinfomsg = NLMSG_DATA(&request.header);
165 ifinfomsg->ifi_family = AF_UNSPEC;
166 ifinfomsg->ifi_index = if_loopback;
167 ifinfomsg->ifi_flags = IFF_UP;
168 ifinfomsg->ifi_change = IFF_UP;
169
170 zero(sa);
171 sa.nl.nl_family = AF_NETLINK;
172
173 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
174 return -errno;
175
5bfcc1c6
FF
176 (*requests)++;
177
af5bc85d
LP
178 return 0;
179}
180
5bfcc1c6 181static int read_response(int fd, unsigned requests_max) {
af5bc85d
LP
182 union {
183 struct sockaddr sa;
184 struct sockaddr_nl nl;
185 } sa;
186 union {
187 struct nlmsghdr header;
188 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
189 NLMSG_ALIGN(sizeof(struct nlmsgerr))];
190 } response;
191
192 ssize_t l;
193 socklen_t sa_len = sizeof(sa);
194 struct nlmsgerr *nlmsgerr;
195
196 if ((l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len)) < 0)
197 return -errno;
198
199 if (sa_len != sizeof(sa.nl) ||
200 sa.nl.nl_family != AF_NETLINK)
201 return -EIO;
202
203 if (sa.nl.nl_pid != 0)
204 return 0;
205
206 if ((size_t) l < sizeof(struct nlmsghdr))
207 return -EIO;
208
209 if (response.header.nlmsg_type != NLMSG_ERROR ||
210 (pid_t) response.header.nlmsg_pid != getpid() ||
5bfcc1c6 211 response.header.nlmsg_seq >= requests_max)
af5bc85d
LP
212 return 0;
213
214 if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
215 response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
216 return -EIO;
217
218 nlmsgerr = NLMSG_DATA(&response.header);
219
86700cf7 220 if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST) {
68d157d8 221 log_warning("Netlink failure for request %i: %s", response.header.nlmsg_seq, strerror(-nlmsgerr->error));
af5bc85d 222 return nlmsgerr->error;
86700cf7 223 }
af5bc85d
LP
224
225 return response.header.nlmsg_seq;
226}
227
228int loopback_setup(void) {
229 int r, if_loopback;
230 union {
231 struct sockaddr sa;
232 struct sockaddr_nl nl;
233 struct sockaddr_storage storage;
234 } sa;
5bfcc1c6 235 unsigned requests = 0, i;
af5bc85d
LP
236 int fd;
237
238 errno = 0;
239 if ((if_loopback = (int) if_nametoindex("lo")) <= 0)
240 return errno ? -errno : -ENODEV;
241
242 if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
243 return -errno;
244
245 zero(sa);
246 sa.nl.nl_family = AF_NETLINK;
247
248 if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
249 r = -errno;
250 goto finish;
251 }
252
5bfcc1c6 253 if ((r = add_adresses(fd, if_loopback, &requests)) < 0)
af5bc85d
LP
254 goto finish;
255
5bfcc1c6 256 if ((r = start_interface(fd, if_loopback, &requests)) < 0)
af5bc85d
LP
257 goto finish;
258
5bfcc1c6
FF
259 for (i = 0; i < requests; i++) {
260 if ((r = read_response(fd, requests)) < 0)
af5bc85d 261 goto finish;
5bfcc1c6 262 }
af5bc85d
LP
263
264 r = 0;
265
266finish:
267 if (r < 0)
a2c422cb 268 log_warning("Failed to configure loopback device: %s", strerror(-r));
af5bc85d
LP
269
270 if (fd >= 0)
271 close_nointr_nofail(fd);
272
273 return r;
274}