1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
23 #include <sys/socket.h>
25 #include <asm/types.h>
26 #include <netinet/in.h>
30 #include <linux/netlink.h>
31 #include <linux/rtnetlink.h>
35 #include "loopback-setup.h"
36 #include "socket-util.h"
38 #define NLMSG_TAIL(nmsg) \
39 ((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
41 static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type, const void *data, size_t data_length) {
45 length = RTA_LENGTH(data_length);
47 if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length)
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);
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) {
64 l = sendto(fd, buf, buf_len, flags, sa, sa_len);
73 static ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) {
78 l = recvfrom(fd, buf, buf_len, flags, sa, sa_len);
87 static int add_adresses(int fd, int if_loopback, unsigned *requests) {
90 struct sockaddr_nl nl;
92 .nl.nl_family = AF_NETLINK,
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))];
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,
107 struct ifaddrmsg *ifaddrmsg;
108 uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
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;
118 r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL,
119 &ipv4_address, sizeof(ipv4_address));
123 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
127 if (!socket_ipv6_is_supported())
130 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
131 request.header.nlmsg_seq = *requests + 1;
133 ifaddrmsg->ifa_family = AF_INET6;
134 ifaddrmsg->ifa_prefixlen = 128;
136 r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL,
137 &in6addr_loopback, sizeof(in6addr_loopback));
141 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
148 static int start_interface(int fd, int if_loopback, unsigned *requests) {
151 struct sockaddr_nl nl;
153 .nl.nl_family = AF_NETLINK,
157 struct nlmsghdr header;
158 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
159 NLMSG_ALIGN(sizeof(struct ifinfomsg))];
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,
167 struct ifinfomsg *ifinfomsg;
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;
175 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
183 static int read_response(int fd, unsigned requests_max) {
186 struct sockaddr_nl nl;
189 struct nlmsghdr header;
190 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
191 NLMSG_ALIGN(sizeof(struct nlmsgerr))];
195 socklen_t sa_len = sizeof(sa);
196 struct nlmsgerr *nlmsgerr;
198 l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len);
202 if (sa_len != sizeof(sa.nl) ||
203 sa.nl.nl_family != AF_NETLINK)
206 if (sa.nl.nl_pid != 0)
209 if ((size_t) l < sizeof(struct nlmsghdr))
212 if (response.header.nlmsg_type != NLMSG_ERROR ||
213 (pid_t) response.header.nlmsg_pid != getpid() ||
214 response.header.nlmsg_seq >= requests_max)
217 if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
218 response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
221 nlmsgerr = NLMSG_DATA(&response.header);
223 if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST)
224 return nlmsgerr->error;
226 return response.header.nlmsg_seq;
229 static int check_loopback(void) {
231 _cleanup_close_ int fd;
234 struct sockaddr_in in;
236 .in.sin_family = AF_INET,
237 .in.sin_addr.s_addr = INADDR_LOOPBACK,
240 /* If we failed to set up the loop back device, check whether
241 * it might already be set up */
243 fd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
247 if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0)
250 r = errno == EADDRNOTAVAIL ? 0 : -errno;
255 int loopback_setup(void) {
259 struct sockaddr_nl nl;
261 .nl.nl_family = AF_NETLINK,
263 unsigned requests = 0, i;
264 _cleanup_close_ int fd = -1;
268 if_loopback = (int) if_nametoindex("lo");
269 if (if_loopback <= 0)
270 return errno ? -errno : -ENODEV;
272 fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
276 if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
281 r = add_adresses(fd, if_loopback, &requests);
285 r = start_interface(fd, if_loopback, &requests);
289 for (i = 0; i < requests; i++) {
290 r = read_response(fd, requests);
298 if (eperm && check_loopback() < 0) {
306 log_warning("Failed to configure loopback device: %s", strerror(-r));