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