]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/loopback-setup.c
Merge pull request #7388 from keszybz/doc-tweak
[thirdparty/systemd.git] / src / core / loopback-setup.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <net/if.h>
21 #include <stdlib.h>
22
23 #include "sd-netlink.h"
24
25 #include "loopback-setup.h"
26 #include "missing.h"
27 #include "netlink-util.h"
28
29 #define LOOPBACK_SETUP_TIMEOUT_USEC (5 * USEC_PER_SEC)
30
31 struct state {
32 unsigned n_messages;
33 int rcode;
34 const char *title;
35 };
36
37 static int generic_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
38 struct state *s = userdata;
39
40 assert(s);
41 assert(s->n_messages > 0);
42 s->n_messages--;
43
44 errno = 0;
45 log_debug_errno(sd_netlink_message_get_errno(m), "Failed to %s: %m", s->title);
46
47 s->rcode = sd_netlink_message_get_errno(m);
48
49 return 0;
50 }
51
52 static int start_loopback(sd_netlink *rtnl, struct state *s) {
53 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
54 int r;
55
56 assert(rtnl);
57 assert(s);
58
59 r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX);
60 if (r < 0)
61 return r;
62
63 r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
64 if (r < 0)
65 return r;
66
67 r = sd_netlink_call_async(rtnl, req, generic_handler, s, LOOPBACK_SETUP_TIMEOUT_USEC, NULL);
68 if (r < 0)
69 return r;
70
71 s->n_messages ++;
72 return 0;
73 }
74
75 static int add_ipv4_address(sd_netlink *rtnl, struct state *s) {
76 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
77 int r;
78
79 assert(rtnl);
80 assert(s);
81
82 r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET);
83 if (r < 0)
84 return r;
85
86 r = sd_rtnl_message_addr_set_prefixlen(req, 8);
87 if (r < 0)
88 return r;
89
90 r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
91 if (r < 0)
92 return r;
93
94 r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST);
95 if (r < 0)
96 return r;
97
98 r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &(struct in_addr) { .s_addr = htobe32(INADDR_LOOPBACK) } );
99 if (r < 0)
100 return r;
101
102 r = sd_netlink_call_async(rtnl, req, generic_handler, s, USEC_INFINITY, NULL);
103 if (r < 0)
104 return r;
105
106 s->n_messages ++;
107 return 0;
108 }
109
110 static int add_ipv6_address(sd_netlink *rtnl, struct state *s) {
111 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
112 int r;
113
114 assert(rtnl);
115 assert(s);
116
117 r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET6);
118 if (r < 0)
119 return r;
120
121 r = sd_rtnl_message_addr_set_prefixlen(req, 128);
122 if (r < 0)
123 return r;
124
125 r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
126 if (r < 0)
127 return r;
128
129 r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST);
130 if (r < 0)
131 return r;
132
133 r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &in6addr_loopback);
134 if (r < 0)
135 return r;
136
137 r = sd_netlink_call_async(rtnl, req, generic_handler, s, USEC_INFINITY, NULL);
138 if (r < 0)
139 return r;
140
141 s->n_messages ++;
142 return 0;
143 }
144
145 static bool check_loopback(sd_netlink *rtnl) {
146 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
147 unsigned flags;
148 int r;
149
150 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, LOOPBACK_IFINDEX);
151 if (r < 0)
152 return false;
153
154 r = sd_netlink_call(rtnl, req, USEC_INFINITY, &reply);
155 if (r < 0)
156 return false;
157
158 r = sd_rtnl_message_link_get_flags(reply, &flags);
159 if (r < 0)
160 return false;
161
162 return flags & IFF_UP;
163 }
164
165 int loopback_setup(void) {
166 _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
167 struct state state_4 = { .title = "add address 127.0.0.1 to loopback interface" },
168 state_6 = { .title = "add address ::1 to loopback interface"},
169 state_up = { .title = "bring loopback interface up" };
170 int r;
171
172 r = sd_netlink_open(&rtnl);
173 if (r < 0)
174 return log_error_errno(r, "Failed to open netlink: %m");
175
176 /* Note that we add the IP addresses here explicitly even though the kernel does that too implicitly when
177 * setting up the loopback device. The reason we do this here a second time (and possibly race against the
178 * kernel) is that we want to synchronously wait until the IP addresses are set up correctly, see
179 *
180 * https://github.com/systemd/systemd/issues/5641 */
181
182 r = add_ipv4_address(rtnl, &state_4);
183 if (r < 0)
184 return log_error_errno(r, "Failed to enqueue IPv4 loopback address add request: %m");
185
186 r = add_ipv6_address(rtnl, &state_6);
187 if (r < 0)
188 return log_error_errno(r, "Failed to enqueue IPv6 loopback address add request: %m");
189
190 r = start_loopback(rtnl, &state_up);
191 if (r < 0)
192 return log_error_errno(r, "Failed to enqueue loopback interface start request: %m");
193
194 while (state_4.n_messages + state_6.n_messages + state_up.n_messages > 0) {
195 r = sd_netlink_wait(rtnl, LOOPBACK_SETUP_TIMEOUT_USEC);
196 if (r < 0)
197 return log_error_errno(r, "Failed to wait for netlink event: %m");
198
199 r = sd_netlink_process(rtnl, NULL);
200 if (r < 0)
201 return log_warning_errno(r, "Failed to process netlink event: %m");
202 }
203
204 /* Note that we don't really care whether the addresses could be added or not */
205 if (state_up.rcode != 0) {
206 /* If we lack the permissions to configure the loopback device,
207 * but we find it to be already configured, let's exit cleanly,
208 * in order to supported unprivileged containers. */
209 if (state_up.rcode == -EPERM && check_loopback(rtnl))
210 return 0;
211
212 return log_warning_errno(state_up.rcode, "Failed to configure loopback device: %m");
213 }
214
215 return 0;
216 }