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