loopback-setup.c revision 08e1fb68d78b4adf26cce8387fc428b9e370bcf4
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak/***
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak This file is part of systemd.
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak Copyright 2010 Lennart Poettering
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak systemd is free software; you can redistribute it and/or modify it
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak under the terms of the GNU Lesser General Public License as published by
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak the Free Software Foundation; either version 2.1 of the License, or
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak (at your option) any later version.
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak systemd is distributed in the hope that it will be useful, but
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak WITHOUT ANY WARRANTY; without even the implied warranty of
27e52281f1522522b170cafc76b08b58aa70ccaand MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak Lesser General Public License for more details.
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak You should have received a copy of the GNU Lesser General Public License
4b5981e276e93df97c34e4da05ca5cf8bbd937dand along with systemd; If not, see <http://www.gnu.org/licenses/>.
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak***/
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include <errno.h>
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include <sys/socket.h>
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include <net/if.h>
b12f74e1aaac71d21e4b9a376b31d7307a8d87d8nd#include <asm/types.h>
b12f74e1aaac71d21e4b9a376b31d7307a8d87d8nd#include <netinet/in.h>
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include <string.h>
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include <stdlib.h>
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include <unistd.h>
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include <linux/netlink.h>
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include <linux/rtnetlink.h>
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include "util.h"
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include "macro.h"
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include "loopback-setup.h"
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#include "socket-util.h"
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak#define NLMSG_TAIL(nmsg) \
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak ((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniakstatic int add_rtattr(struct nlmsghdr *n, size_t max_length, int type, const void *data, size_t data_length) {
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak size_t length;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak struct rtattr *rta;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak length = RTA_LENGTH(data_length);
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length)
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak return -E2BIG;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak rta = NLMSG_TAIL(n);
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak rta->rta_type = type;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak rta->rta_len = length;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak memcpy(RTA_DATA(rta), data, data_length);
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length);
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak return 0;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak}
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniakstatic ssize_t sendto_loop(int fd, const void *buf, size_t buf_len, int flags, const struct sockaddr *sa, socklen_t sa_len) {
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak for (;;) {
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak ssize_t l;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak if ((l = sendto(fd, buf, buf_len, flags, sa, sa_len)) >= 0)
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak return l;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak if (errno != EINTR)
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak return -errno;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak }
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak}
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniakstatic ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) {
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak for (;;) {
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak ssize_t l;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak if ((l = recvfrom(fd, buf, buf_len, flags, sa, sa_len)) >= 0)
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak return l;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak if (errno != EINTR)
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak return -errno;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak }
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak}
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniakstatic int add_adresses(int fd, int if_loopback, unsigned *requests) {
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak union {
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak struct sockaddr sa;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak struct sockaddr_nl nl;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak } sa;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak union {
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak struct nlmsghdr header;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak RTA_LENGTH(sizeof(struct in6_addr))];
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak } request;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak struct ifaddrmsg *ifaddrmsg;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak int r;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak zero(request);
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak request.header.nlmsg_type = RTM_NEWADDR;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK;
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak request.header.nlmsg_seq = *requests + 1;
6eed902e5b4d3e016e220bfbf8769a87c4cb242enoodl
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak ifaddrmsg = NLMSG_DATA(&request.header);
5652dbe450e4fcfdf36d4cfb42d7f2345ded29a4maczniak ifaddrmsg->ifa_family = AF_INET;
ifaddrmsg->ifa_prefixlen = 8;
ifaddrmsg->ifa_flags = IFA_F_PERMANENT;
ifaddrmsg->ifa_scope = RT_SCOPE_HOST;
ifaddrmsg->ifa_index = if_loopback;
if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address))) < 0)
return r;
zero(sa);
sa.nl.nl_family = AF_NETLINK;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
(*requests)++;
if (!socket_ipv6_is_supported())
return 0;
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
request.header.nlmsg_seq = *requests + 1;
ifaddrmsg->ifa_family = AF_INET6;
ifaddrmsg->ifa_prefixlen = 128;
if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback))) < 0)
return r;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
(*requests)++;
return 0;
}
static int start_interface(int fd, int if_loopback, unsigned *requests) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
} sa;
union {
struct nlmsghdr header;
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct ifinfomsg))];
} request;
struct ifinfomsg *ifinfomsg;
zero(request);
request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
request.header.nlmsg_type = RTM_NEWLINK;
request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
request.header.nlmsg_seq = *requests + 1;
ifinfomsg = NLMSG_DATA(&request.header);
ifinfomsg->ifi_family = AF_UNSPEC;
ifinfomsg->ifi_index = if_loopback;
ifinfomsg->ifi_flags = IFF_UP;
ifinfomsg->ifi_change = IFF_UP;
zero(sa);
sa.nl.nl_family = AF_NETLINK;
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
(*requests)++;
return 0;
}
static int read_response(int fd, unsigned requests_max) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
} sa;
union {
struct nlmsghdr header;
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct nlmsgerr))];
} response;
ssize_t l;
socklen_t sa_len = sizeof(sa);
struct nlmsgerr *nlmsgerr;
if ((l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len)) < 0)
return -errno;
if (sa_len != sizeof(sa.nl) ||
sa.nl.nl_family != AF_NETLINK)
return -EIO;
if (sa.nl.nl_pid != 0)
return 0;
if ((size_t) l < sizeof(struct nlmsghdr))
return -EIO;
if (response.header.nlmsg_type != NLMSG_ERROR ||
(pid_t) response.header.nlmsg_pid != getpid() ||
response.header.nlmsg_seq >= requests_max)
return 0;
if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) ||
response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
return -EIO;
nlmsgerr = NLMSG_DATA(&response.header);
if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST) {
log_warning("Netlink failure for request %i: %s", response.header.nlmsg_seq, strerror(-nlmsgerr->error));
return nlmsgerr->error;
}
return response.header.nlmsg_seq;
}
int loopback_setup(void) {
int r, if_loopback;
union {
struct sockaddr sa;
struct sockaddr_nl nl;
struct sockaddr_storage storage;
} sa;
unsigned requests = 0, i;
int fd;
errno = 0;
if ((if_loopback = (int) if_nametoindex("lo")) <= 0)
return errno ? -errno : -ENODEV;
if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
return -errno;
zero(sa);
sa.nl.nl_family = AF_NETLINK;
if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
r = -errno;
goto finish;
}
if ((r = add_adresses(fd, if_loopback, &requests)) < 0)
goto finish;
if ((r = start_interface(fd, if_loopback, &requests)) < 0)
goto finish;
for (i = 0; i < requests; i++) {
if ((r = read_response(fd, requests)) < 0)
goto finish;
}
r = 0;
finish:
if (r < 0)
log_warning("Failed to configure loopback device: %s", strerror(-r));
if (fd >= 0)
close_nointr_nofail(fd);
return r;
}