chiark / gitweb /
fix a couple of things found with the llvm static analyzer
[elogind.git] / src / core / loopback-setup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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
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.
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
16   Lesser General Public License for more details.
17
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/>.
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"
36 #include "socket-util.h"
37
38 #define NLMSG_TAIL(nmsg)                                                \
39         ((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
40
41 static 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
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) {
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
72 static 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
85 static int add_adresses(int fd, int if_loopback, unsigned *requests) {
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;
106         request.header.nlmsg_seq = *requests + 1;
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;
123         (*requests)++;
124
125         if (!socket_ipv6_is_supported())
126                 return 0;
127
128         request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
129         request.header.nlmsg_seq = *requests + 1;
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;
139         (*requests)++;
140
141         return 0;
142 }
143
144 static int start_interface(int fd, int if_loopback, unsigned *requests) {
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;
162         request.header.nlmsg_seq = *requests + 1;
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
176         (*requests)++;
177
178         return 0;
179 }
180
181 static int read_response(int fd, unsigned requests_max) {
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() ||
211             response.header.nlmsg_seq >= requests_max)
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
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;
223         }
224
225         return response.header.nlmsg_seq;
226 }
227
228 int 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;
235         unsigned requests = 0, i;
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
253         if ((r = add_adresses(fd, if_loopback, &requests)) < 0)
254                 goto finish;
255
256         if ((r = start_interface(fd, if_loopback, &requests)) < 0)
257                 goto finish;
258
259         for (i = 0; i < requests; i++) {
260                 if ((r = read_response(fd, requests)) < 0)
261                         goto finish;
262         }
263
264         r = 0;
265
266 finish:
267         if (r < 0)
268                 log_warning("Failed to configure loopback device: %s", strerror(-r));
269
270         if (fd >= 0)
271                 close_nointr_nofail(fd);
272
273         return r;
274 }