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