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 if ((l = sendto(fd, buf, buf_len, flags, sa, sa_len)) >= 0)
72 static ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) {
77 if ((l = recvfrom(fd, buf, buf_len, flags, sa, sa_len)) >= 0)
85 static int add_adresses(int fd, int if_loopback, unsigned *requests) {
88 struct sockaddr_nl nl;
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))];
97 struct ifaddrmsg *ifaddrmsg;
98 uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
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;
106 request.header.nlmsg_seq = *requests + 1;
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;
115 if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address))) < 0)
119 sa.nl.nl_family = AF_NETLINK;
121 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
125 if (!socket_ipv6_is_supported())
128 request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
129 request.header.nlmsg_seq = *requests + 1;
131 ifaddrmsg->ifa_family = AF_INET6;
132 ifaddrmsg->ifa_prefixlen = 128;
134 if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback))) < 0)
137 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
144 static int start_interface(int fd, int if_loopback, unsigned *requests) {
147 struct sockaddr_nl nl;
150 struct nlmsghdr header;
151 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
152 NLMSG_ALIGN(sizeof(struct ifinfomsg))];
155 struct ifinfomsg *ifinfomsg;
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;
162 request.header.nlmsg_seq = *requests + 1;
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;
171 sa.nl.nl_family = AF_NETLINK;
173 if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
181 static int read_response(int fd, unsigned requests_max) {
184 struct sockaddr_nl nl;
187 struct nlmsghdr header;
188 uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
189 NLMSG_ALIGN(sizeof(struct nlmsgerr))];
193 socklen_t sa_len = sizeof(sa);
194 struct nlmsgerr *nlmsgerr;
196 if ((l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len)) < 0)
199 if (sa_len != sizeof(sa.nl) ||
200 sa.nl.nl_family != AF_NETLINK)
203 if (sa.nl.nl_pid != 0)
206 if ((size_t) l < sizeof(struct nlmsghdr))
209 if (response.header.nlmsg_type != NLMSG_ERROR ||
210 (pid_t) response.header.nlmsg_pid != getpid() ||
211 response.header.nlmsg_seq >= requests_max)
214 if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
215 response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
218 nlmsgerr = NLMSG_DATA(&response.header);
220 if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST) {
221 log_warning("Netlink failure for request %i: %s", response.header.nlmsg_seq, strerror(-nlmsgerr->error));
222 return nlmsgerr->error;
225 return response.header.nlmsg_seq;
228 int loopback_setup(void) {
232 struct sockaddr_nl nl;
233 struct sockaddr_storage storage;
235 unsigned requests = 0, i;
239 if ((if_loopback = (int) if_nametoindex("lo")) <= 0)
240 return errno ? -errno : -ENODEV;
242 if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
246 sa.nl.nl_family = AF_NETLINK;
248 if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
253 if ((r = add_adresses(fd, if_loopback, &requests)) < 0)
256 if ((r = start_interface(fd, if_loopback, &requests)) < 0)
259 for (i = 0; i < requests; i++) {
260 if ((r = read_response(fd, requests)) < 0)
268 log_warning("Failed to configure loopback device: %s", strerror(-r));
271 close_nointr_nofail(fd);