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;
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))];
99 struct ifaddrmsg *ifaddrmsg;
100 uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
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;
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;
117 r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address));
122 sa.nl.nl_family = AF_NETLINK;
124 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
128 if (!socket_ipv6_is_supported())
131 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
132 request.header.nlmsg_seq = *requests + 1;
134 ifaddrmsg->ifa_family = AF_INET6;
135 ifaddrmsg->ifa_prefixlen = 128;
137 r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &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;
154 struct nlmsghdr header;
155 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
156 NLMSG_ALIGN(sizeof(struct ifinfomsg))];
159 struct ifinfomsg *ifinfomsg;
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;
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;
175 sa.nl.nl_family = AF_NETLINK;
177 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
185 static int read_response(int fd, unsigned requests_max) {
188 struct sockaddr_nl nl;
191 struct nlmsghdr header;
192 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
193 NLMSG_ALIGN(sizeof(struct nlmsgerr))];
197 socklen_t sa_len = sizeof(sa);
198 struct nlmsgerr *nlmsgerr;
200 l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len);
204 if (sa_len != sizeof(sa.nl) ||
205 sa.nl.nl_family != AF_NETLINK)
208 if (sa.nl.nl_pid != 0)
211 if ((size_t) l < sizeof(struct nlmsghdr))
214 if (response.header.nlmsg_type != NLMSG_ERROR ||
215 (pid_t) response.header.nlmsg_pid != getpid() ||
216 response.header.nlmsg_seq >= requests_max)
219 if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
220 response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
223 nlmsgerr = NLMSG_DATA(&response.header);
225 if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST)
226 return nlmsgerr->error;
228 return response.header.nlmsg_seq;
231 static int check_loopback(void) {
235 struct sockaddr_in in;
238 /* If we failed to set up the loop back device, check whether
239 * it might already be set up */
241 fd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
246 sa.in.sin_family = AF_INET;
247 sa.in.sin_addr.s_addr = INADDR_LOOPBACK;
249 if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0)
252 r = errno == EADDRNOTAVAIL ? 0 : -errno;
254 close_nointr_nofail(fd);
259 int loopback_setup(void) {
263 struct sockaddr_nl nl;
265 unsigned requests = 0, i;
270 if_loopback = (int) if_nametoindex("lo");
271 if (if_loopback <= 0)
272 return errno ? -errno : -ENODEV;
274 fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
279 sa.nl.nl_family = AF_NETLINK;
280 if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
285 r = add_adresses(fd, if_loopback, &requests);
289 r = start_interface(fd, if_loopback, &requests);
293 for (i = 0; i < requests; i++) {
294 r = read_response(fd, requests);
302 if (eperm && check_loopback() < 0) {
311 log_warning("Failed to configure loopback device: %s", strerror(-r));
314 close_nointr_nofail(fd);