chiark / gitweb /
Make sure that keys are properly removed from hashmap
[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
31 #include "util.h"
32 #include "macro.h"
33 #include "loopback-setup.h"
34 #include "socket-util.h"
35 #include "sd-rtnl.h"
36 #include "rtnl-util.h"
37
38 static int pipe_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
39         int *counter = userdata;
40         int r;
41
42         (*counter) --;
43
44         r = sd_rtnl_message_get_errno(m);
45
46         return r == -EEXIST ? 0 : r;
47 }
48
49 static int add_addresses(sd_rtnl *rtnl, int if_loopback, struct in_addr *ipv4_address, int *counter) {
50         _cleanup_rtnl_message_unref_ sd_rtnl_message *ipv4 = NULL, *ipv6 = NULL;
51         int r;
52
53         r = sd_rtnl_message_new_addr(rtnl, &ipv4, RTM_NEWADDR, if_loopback, AF_INET);
54         if (r < 0)
55                 return r;
56
57         r = sd_rtnl_message_addr_set_prefixlen(ipv4, 8);
58         if (r < 0)
59                 return r;
60
61         r = sd_rtnl_message_addr_set_flags(ipv4, IFA_F_PERMANENT);
62         if (r < 0)
63                 return r;
64
65         r = sd_rtnl_message_addr_set_scope(ipv4, RT_SCOPE_HOST);
66         if (r < 0)
67                 return r;
68
69         r = sd_rtnl_message_append_in_addr(ipv4, IFA_LOCAL, ipv4_address);
70         if (r < 0)
71                 return r;
72
73         r = sd_rtnl_call_async(rtnl, ipv4, &pipe_handler, counter, 0, NULL);
74         if (r < 0)
75                 return r;
76
77         (*counter) ++;
78
79         if (!socket_ipv6_is_supported())
80                 return 0;
81
82         r = sd_rtnl_message_new_addr(rtnl, &ipv6, RTM_NEWADDR, if_loopback, AF_INET6);
83         if (r < 0)
84                 return r;
85
86         r = sd_rtnl_message_addr_set_prefixlen(ipv6, 128);
87         if (r < 0)
88                 return r;
89
90         r = sd_rtnl_message_addr_set_flags(ipv6, IFA_F_PERMANENT);
91         if (r < 0)
92                 return r;
93
94         r = sd_rtnl_message_addr_set_scope(ipv6, RT_SCOPE_HOST);
95         if (r < 0)
96                 return r;
97
98         r = sd_rtnl_message_append_in6_addr(ipv6, IFA_LOCAL, &in6addr_loopback);
99         if (r < 0)
100                 return r;
101
102         r = sd_rtnl_call_async(rtnl, ipv6, &pipe_handler, counter, 0, NULL);
103         if (r < 0)
104                 return r;
105
106         (*counter) ++;
107
108         return 0;
109 }
110
111 static int start_interface(sd_rtnl *rtnl, int if_loopback, struct in_addr *ipv4_address, int *counter) {
112         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
113         int r;
114
115         r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, if_loopback);
116         if (r < 0)
117                 return r;
118
119         r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
120         if (r < 0)
121                 return r;
122
123         r = sd_rtnl_call_async(rtnl, req, &pipe_handler, counter, 0, NULL);
124         if (r < 0)
125                 return r;
126
127         (*counter) ++;
128
129         return 0;
130 }
131
132 static int check_loopback(void) {
133         int r;
134         _cleanup_close_ int fd = -1;
135         union {
136                 struct sockaddr sa;
137                 struct sockaddr_in in;
138         } sa = {
139                 .in.sin_family = AF_INET,
140                 .in.sin_addr.s_addr = INADDR_LOOPBACK,
141         };
142
143         /* If we failed to set up the loop back device, check whether
144          * it might already be set up */
145
146         fd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
147         if (fd < 0)
148                 return -errno;
149
150         if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0)
151                 r = 1;
152         else
153                 r = errno == EADDRNOTAVAIL ? 0 : -errno;
154
155         return r;
156 }
157
158 int loopback_setup(void) {
159         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
160         int r, if_loopback, counter = 0;
161         bool eperm = false;
162         struct in_addr ipv4_address;
163
164         errno = 0;
165         if_loopback = (int) if_nametoindex("lo");
166         if (if_loopback <= 0)
167                 return errno ? -errno : -ENODEV;
168
169         ipv4_address.s_addr = htonl(INADDR_LOOPBACK);
170
171         r = sd_rtnl_open(&rtnl, 0);
172         if (r < 0)
173                 return r;
174
175         r = add_addresses(rtnl, if_loopback, &ipv4_address, &counter);
176         if (r < 0)
177                 return r;
178
179         r = start_interface(rtnl, if_loopback, &ipv4_address, &counter);
180         if (r < 0)
181                 return r;
182
183         while (counter > 0) {
184                 r = sd_rtnl_wait(rtnl, 0);
185                 if (r < 0)
186                         return r;
187
188                 r = sd_rtnl_process(rtnl, 0);
189                 if (r < 0) {
190                         if (r == -EPERM)
191                                 eperm = true;
192                         else {
193                                 log_warning("Failed to configure loopback device: %s", strerror(-r));
194                                 return r;
195                         }
196                 }
197         }
198
199         if (eperm && check_loopback() < 0) {
200                 log_warning("Failed to configure loopback device: %s", strerror(EPERM));
201                 return -EPERM;
202         }
203
204         return 0;
205 }