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