/src/systemd/src/shared/loopback-setup.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | | |
3 | | #include <net/if.h> |
4 | | |
5 | | #include "sd-netlink.h" |
6 | | |
7 | | #include "errno-util.h" |
8 | | #include "log.h" |
9 | | #include "loopback-setup.h" |
10 | | #include "missing-network.h" |
11 | | #include "time-util.h" |
12 | | |
13 | 0 | #define LOOPBACK_SETUP_TIMEOUT_USEC (5 * USEC_PER_SEC) |
14 | | |
15 | | struct state { |
16 | | unsigned n_messages; |
17 | | int rcode; |
18 | | const char *error_message; |
19 | | const char *success_message; |
20 | | const char *eexist_message; |
21 | | }; |
22 | | |
23 | 0 | static int generic_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { |
24 | 0 | struct state *s = ASSERT_PTR(userdata); |
25 | 0 | int r; |
26 | |
|
27 | 0 | assert(s->n_messages > 0); |
28 | 0 | s->n_messages--; |
29 | |
|
30 | 0 | errno = 0; |
31 | |
|
32 | 0 | r = sd_netlink_message_get_errno(m); |
33 | 0 | if (r == -EEXIST && s->eexist_message) |
34 | 0 | log_debug_errno(r, "%s", s->eexist_message); |
35 | 0 | else if (r < 0) |
36 | 0 | log_debug_errno(r, "%s: %m", s->error_message); |
37 | 0 | else |
38 | 0 | log_debug("%s", s->success_message); |
39 | |
|
40 | 0 | s->rcode = r; |
41 | 0 | return 0; |
42 | 0 | } |
43 | | |
44 | 0 | static int start_loopback(sd_netlink *rtnl, struct state *s) { |
45 | 0 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; |
46 | 0 | int r; |
47 | |
|
48 | 0 | assert(rtnl); |
49 | 0 | assert(s); |
50 | |
|
51 | 0 | r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX); |
52 | 0 | if (r < 0) |
53 | 0 | return r; |
54 | | |
55 | 0 | r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); |
56 | 0 | if (r < 0) |
57 | 0 | return r; |
58 | | |
59 | 0 | r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, LOOPBACK_SETUP_TIMEOUT_USEC, "systemd-start-loopback"); |
60 | 0 | if (r < 0) |
61 | 0 | return r; |
62 | | |
63 | 0 | s->n_messages++; |
64 | 0 | return 0; |
65 | 0 | } |
66 | | |
67 | 0 | static int add_ipv4_address(sd_netlink *rtnl, struct state *s) { |
68 | 0 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; |
69 | 0 | int r; |
70 | |
|
71 | 0 | assert(rtnl); |
72 | 0 | assert(s); |
73 | |
|
74 | 0 | r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET); |
75 | 0 | if (r < 0) |
76 | 0 | return r; |
77 | | |
78 | 0 | r = sd_rtnl_message_addr_set_prefixlen(req, 8); |
79 | 0 | if (r < 0) |
80 | 0 | return r; |
81 | | |
82 | 0 | r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST); |
83 | 0 | if (r < 0) |
84 | 0 | return r; |
85 | | |
86 | 0 | r = sd_netlink_message_append_u32(req, IFA_FLAGS, IFA_F_PERMANENT); |
87 | 0 | if (r < 0) |
88 | 0 | return r; |
89 | | |
90 | 0 | r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &(struct in_addr) { .s_addr = htobe32(INADDR_LOOPBACK) } ); |
91 | 0 | if (r < 0) |
92 | 0 | return r; |
93 | | |
94 | 0 | r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, USEC_INFINITY, "systemd-loopback-ipv4"); |
95 | 0 | if (r < 0) |
96 | 0 | return r; |
97 | | |
98 | 0 | s->n_messages++; |
99 | 0 | return 0; |
100 | 0 | } |
101 | | |
102 | 0 | static int add_ipv6_address(sd_netlink *rtnl, struct state *s) { |
103 | 0 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; |
104 | 0 | int r; |
105 | |
|
106 | 0 | assert(rtnl); |
107 | 0 | assert(s); |
108 | |
|
109 | 0 | r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET6); |
110 | 0 | if (r < 0) |
111 | 0 | return r; |
112 | | |
113 | 0 | r = sd_rtnl_message_addr_set_prefixlen(req, 128); |
114 | 0 | if (r < 0) |
115 | 0 | return r; |
116 | | |
117 | 0 | uint32_t flags = IFA_F_PERMANENT|IFA_F_NOPREFIXROUTE; |
118 | 0 | r = sd_netlink_message_append_u32(req, IFA_FLAGS, flags); |
119 | 0 | if (r < 0) |
120 | 0 | return r; |
121 | | |
122 | 0 | r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST); |
123 | 0 | if (r < 0) |
124 | 0 | return r; |
125 | | |
126 | 0 | r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &in6addr_loopback); |
127 | 0 | if (r < 0) |
128 | 0 | return r; |
129 | | |
130 | 0 | r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, USEC_INFINITY, "systemd-loopback-ipv6"); |
131 | 0 | if (r < 0) |
132 | 0 | return r; |
133 | | |
134 | 0 | s->n_messages++; |
135 | 0 | return 0; |
136 | 0 | } |
137 | | |
138 | 0 | static int check_loopback(sd_netlink *rtnl) { |
139 | 0 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; |
140 | 0 | unsigned flags; |
141 | 0 | int r; |
142 | |
|
143 | 0 | r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, LOOPBACK_IFINDEX); |
144 | 0 | if (r < 0) |
145 | 0 | return r; |
146 | | |
147 | 0 | r = sd_netlink_call(rtnl, req, USEC_INFINITY, &reply); |
148 | 0 | if (r < 0) |
149 | 0 | return r; |
150 | | |
151 | 0 | r = sd_rtnl_message_link_get_flags(reply, &flags); |
152 | 0 | if (r < 0) |
153 | 0 | return r; |
154 | | |
155 | 0 | return flags & IFF_UP; |
156 | 0 | } |
157 | | |
158 | 0 | int loopback_setup(void) { |
159 | 0 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; |
160 | 0 | struct state state_4 = { |
161 | 0 | .error_message = "Failed to add address 127.0.0.1 to loopback interface", |
162 | 0 | .success_message = "Successfully added address 127.0.0.1 to loopback interface", |
163 | 0 | .eexist_message = "127.0.0.1 has already been added to loopback interface", |
164 | 0 | }, state_6 = { |
165 | 0 | .error_message = "Failed to add address ::1 to loopback interface", |
166 | 0 | .success_message = "Successfully added address ::1 to loopback interface", |
167 | 0 | .eexist_message = "::1 has already been added to loopback interface", |
168 | 0 | }, state_up = { |
169 | 0 | .error_message = "Failed to bring loopback interface up", |
170 | 0 | .success_message = "Successfully brought loopback interface up", |
171 | 0 | }; |
172 | 0 | int r; |
173 | | |
174 | | /* Note, we, generally assume callers ignore the return code here (except test cases), hence only log add LOG_WARN level. */ |
175 | |
|
176 | 0 | r = sd_netlink_open(&rtnl); |
177 | 0 | if (r < 0) |
178 | 0 | return log_warning_errno(r, "Failed to open netlink, ignoring: %m"); |
179 | | |
180 | | /* Note that we add the IP addresses here explicitly even though the kernel does that too implicitly when |
181 | | * setting up the loopback device. The reason we do this here a second time (and possibly race against the |
182 | | * kernel) is that we want to synchronously wait until the IP addresses are set up correctly, see |
183 | | * |
184 | | * https://github.com/systemd/systemd/issues/5641 */ |
185 | | |
186 | 0 | r = add_ipv4_address(rtnl, &state_4); |
187 | 0 | if (r < 0) |
188 | 0 | return log_warning_errno(r, "Failed to enqueue IPv4 loopback address add request, ignoring: %m"); |
189 | | |
190 | 0 | r = add_ipv6_address(rtnl, &state_6); |
191 | 0 | if (r < 0) |
192 | 0 | return log_warning_errno(r, "Failed to enqueue IPv6 loopback address add request, ignoring: %m"); |
193 | | |
194 | 0 | r = start_loopback(rtnl, &state_up); |
195 | 0 | if (r < 0) |
196 | 0 | return log_warning_errno(r, "Failed to enqueue loopback interface start request, ignoring: %m"); |
197 | | |
198 | 0 | while (state_4.n_messages + state_6.n_messages + state_up.n_messages > 0) { |
199 | 0 | r = sd_netlink_wait(rtnl, LOOPBACK_SETUP_TIMEOUT_USEC); |
200 | 0 | if (r < 0) |
201 | 0 | return log_warning_errno(r, "Failed to wait for netlink event, ignoring: %m"); |
202 | | |
203 | 0 | r = sd_netlink_process(rtnl, NULL); |
204 | 0 | if (r < 0) |
205 | 0 | return log_warning_errno(r, "Failed to process netlink event, ignoring: %m"); |
206 | 0 | } |
207 | | |
208 | | /* Note that we don't really care whether the addresses could be added or not */ |
209 | 0 | if (state_up.rcode != 0) { |
210 | | |
211 | | /* If we lack the permissions to configure the loopback device, but we find it to be already |
212 | | * configured, let's exit cleanly, in order to supported unprivileged containers. */ |
213 | 0 | if (ERRNO_IS_PRIVILEGE(state_up.rcode)) { |
214 | 0 | r = check_loopback(rtnl); |
215 | 0 | if (r < 0) |
216 | 0 | log_debug_errno(r, "Failed to check if loopback device might already be up, ignoring: %m"); |
217 | 0 | else if (r > 0) { |
218 | 0 | log_debug("Configuring loopback failed, but device is already up, suppressing failure."); |
219 | 0 | return 0; |
220 | 0 | } |
221 | 0 | } |
222 | | |
223 | 0 | return log_warning_errno(state_up.rcode, "Failed to configure loopback network device, ignoring: %m"); |
224 | 0 | } |
225 | | |
226 | 0 | return 0; |
227 | 0 | } |