]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/wait-online/manager.c
Merge pull request #18007 from fw-strlen/ipv6_masq_and_dnat
[thirdparty/systemd.git] / src / network / wait-online / manager.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/ether.h>
4 #include <linux/if.h>
5 #include <fnmatch.h>
6
7 #include "alloc-util.h"
8 #include "link.h"
9 #include "manager.h"
10 #include "netlink-util.h"
11 #include "strv.h"
12 #include "time-util.h"
13 #include "util.h"
14
15 static bool manager_ignore_link(Manager *m, Link *link) {
16 assert(m);
17 assert(link);
18
19 /* always ignore the loopback interface */
20 if (link->flags & IFF_LOOPBACK)
21 return true;
22
23 /* if interfaces are given on the command line, ignore all others */
24 if (m->interfaces && !hashmap_contains(m->interfaces, link->ifname))
25 return true;
26
27 if (!link->required_for_online)
28 return true;
29
30 /* ignore interfaces we explicitly are asked to ignore */
31 return strv_fnmatch(m->ignore, link->ifname);
32 }
33
34 static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
35 /* This returns the following:
36 * -EAGAIN: not processed by udev or networkd
37 * 0: operstate is not enough
38 * 1: online */
39
40 if (!l->state)
41 return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN),
42 "link has not yet been processed by udev");
43
44 if (STR_IN_SET(l->state, "configuring", "pending"))
45 return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN),
46 "link is being processed by networkd");
47
48 if (s.min < 0)
49 s.min = m->required_operstate.min >= 0 ? m->required_operstate.min
50 : l->required_operstate.min;
51
52 if (s.max < 0)
53 s.max = m->required_operstate.max >= 0 ? m->required_operstate.max
54 : l->required_operstate.max;
55
56 if (l->operational_state < s.min || l->operational_state > s.max) {
57 log_link_debug(l, "Operational state '%s' is not in range ['%s':'%s']",
58 link_operstate_to_string(l->operational_state),
59 link_operstate_to_string(s.min), link_operstate_to_string(s.max));
60 return 0;
61 }
62
63 return 1;
64 }
65
66 bool manager_configured(Manager *m) {
67 bool one_ready = false;
68 const char *ifname;
69 void *p;
70 Link *l;
71 int r;
72
73 if (!hashmap_isempty(m->interfaces)) {
74 /* wait for all the links given on the command line to appear */
75 HASHMAP_FOREACH_KEY(p, ifname, m->interfaces) {
76 LinkOperationalStateRange *range = p;
77
78 l = hashmap_get(m->links_by_name, ifname);
79 if (!l && range->min == LINK_OPERSTATE_MISSING) {
80 one_ready = true;
81 continue;
82 }
83
84 if (!l) {
85 log_debug("still waiting for %s", ifname);
86 if (!m->any)
87 return false;
88 continue;
89 }
90
91 if (manager_link_is_online(m, l, *range) <= 0) {
92 if (!m->any)
93 return false;
94 continue;
95 }
96
97 one_ready = true;
98 }
99
100 /* all interfaces given by the command line are online, or
101 * one of the specified interfaces is online. */
102 return one_ready;
103 }
104
105 /* wait for all links networkd manages to be in admin state 'configured'
106 * and at least one link to gain a carrier */
107 HASHMAP_FOREACH(l, m->links) {
108 if (manager_ignore_link(m, l)) {
109 log_link_debug(l, "link is ignored");
110 continue;
111 }
112
113 r = manager_link_is_online(m, l,
114 (LinkOperationalStateRange) { _LINK_OPERSTATE_INVALID,
115 _LINK_OPERSTATE_INVALID });
116 if (r < 0 && !m->any)
117 return false;
118 if (r > 0)
119 /* we wait for at least one link to be ready,
120 * regardless of who manages it */
121 one_ready = true;
122 }
123
124 return one_ready;
125 }
126
127 static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) {
128 Manager *m = userdata;
129 uint16_t type;
130 Link *l;
131 const char *ifname;
132 int ifindex, r;
133
134 assert(rtnl);
135 assert(m);
136 assert(mm);
137
138 r = sd_netlink_message_get_type(mm, &type);
139 if (r < 0) {
140 log_warning_errno(r, "rtnl: Could not get message type, ignoring: %m");
141 return 0;
142 }
143
144 r = sd_rtnl_message_link_get_ifindex(mm, &ifindex);
145 if (r < 0) {
146 log_warning_errno(r, "rtnl: Could not get ifindex from link, ignoring: %m");
147 return 0;
148 } else if (ifindex <= 0) {
149 log_warning("rtnl: received link message with invalid ifindex %d, ignoring", ifindex);
150 return 0;
151 }
152
153 r = sd_netlink_message_read_string(mm, IFLA_IFNAME, &ifname);
154 if (r < 0) {
155 log_warning_errno(r, "rtnl: Received link message without ifname, ignoring: %m");
156 return 0;
157 }
158
159 l = hashmap_get(m->links, INT_TO_PTR(ifindex));
160
161 switch (type) {
162
163 case RTM_NEWLINK:
164 if (!l) {
165 log_debug("Found link %i", ifindex);
166
167 r = link_new(m, &l, ifindex, ifname);
168 if (r < 0)
169 return log_error_errno(r, "Failed to create link object: %m");
170 }
171
172 r = link_update_rtnl(l, mm);
173 if (r < 0)
174 log_link_warning_errno(l, r, "Failed to process RTNL link message, ignoring: %m");
175
176 r = link_update_monitor(l);
177 if (r < 0 && r != -ENODATA)
178 log_link_warning_errno(l, r, "Failed to update link state, ignoring: %m");
179
180 break;
181
182 case RTM_DELLINK:
183 if (l) {
184 log_link_debug(l, "Removing link");
185 link_free(l);
186 }
187
188 break;
189 }
190
191 return 0;
192 }
193
194 static int on_rtnl_event(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) {
195 Manager *m = userdata;
196 int r;
197
198 r = manager_process_link(rtnl, mm, m);
199 if (r < 0)
200 return r;
201
202 if (manager_configured(m))
203 sd_event_exit(m->event, 0);
204
205 return 1;
206 }
207
208 static int manager_rtnl_listen(Manager *m) {
209 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
210 int r;
211
212 assert(m);
213
214 /* First, subscribe to interfaces coming and going */
215 r = sd_netlink_open(&m->rtnl);
216 if (r < 0)
217 return r;
218
219 r = sd_netlink_attach_event(m->rtnl, m->event, 0);
220 if (r < 0)
221 return r;
222
223 r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWLINK, on_rtnl_event, NULL, m, "wait-online-on-NEWLINK");
224 if (r < 0)
225 return r;
226
227 r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELLINK, on_rtnl_event, NULL, m, "wait-online-on-DELLINK");
228 if (r < 0)
229 return r;
230
231 /* Then, enumerate all links */
232 r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
233 if (r < 0)
234 return r;
235
236 r = sd_netlink_message_request_dump(req, true);
237 if (r < 0)
238 return r;
239
240 r = sd_netlink_call(m->rtnl, req, 0, &reply);
241 if (r < 0)
242 return r;
243
244 for (sd_netlink_message *i = reply; i; i = sd_netlink_message_next(i)) {
245 r = manager_process_link(m->rtnl, i, m);
246 if (r < 0)
247 return r;
248 }
249
250 return r;
251 }
252
253 static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
254 Manager *m = userdata;
255 Link *l;
256 int r;
257
258 assert(m);
259
260 sd_network_monitor_flush(m->network_monitor);
261
262 HASHMAP_FOREACH(l, m->links) {
263 r = link_update_monitor(l);
264 if (r < 0 && r != -ENODATA)
265 log_link_warning_errno(l, r, "Failed to update link state, ignoring: %m");
266 }
267
268 if (manager_configured(m))
269 sd_event_exit(m->event, 0);
270
271 return 0;
272 }
273
274 static int manager_network_monitor_listen(Manager *m) {
275 int r, fd, events;
276
277 assert(m);
278
279 r = sd_network_monitor_new(&m->network_monitor, NULL);
280 if (r < 0)
281 return r;
282
283 fd = sd_network_monitor_get_fd(m->network_monitor);
284 if (fd < 0)
285 return fd;
286
287 events = sd_network_monitor_get_events(m->network_monitor);
288 if (events < 0)
289 return events;
290
291 r = sd_event_add_io(m->event, &m->network_monitor_event_source,
292 fd, events, &on_network_event, m);
293 if (r < 0)
294 return r;
295
296 return 0;
297 }
298
299 int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
300 LinkOperationalStateRange required_operstate,
301 bool any, usec_t timeout) {
302 _cleanup_(manager_freep) Manager *m = NULL;
303 int r;
304
305 assert(ret);
306
307 m = new(Manager, 1);
308 if (!m)
309 return -ENOMEM;
310
311 *m = (Manager) {
312 .interfaces = interfaces,
313 .ignore = ignore,
314 .required_operstate = required_operstate,
315 .any = any,
316 };
317
318 r = sd_event_default(&m->event);
319 if (r < 0)
320 return r;
321
322 (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
323 (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
324
325 if (timeout > 0) {
326 usec_t usec;
327
328 usec = now(clock_boottime_or_monotonic()) + timeout;
329
330 r = sd_event_add_time(m->event, NULL, clock_boottime_or_monotonic(), usec, 0, NULL, INT_TO_PTR(-ETIMEDOUT));
331 if (r < 0)
332 return r;
333 }
334
335 sd_event_set_watchdog(m->event, true);
336
337 r = manager_network_monitor_listen(m);
338 if (r < 0)
339 return r;
340
341 r = manager_rtnl_listen(m);
342 if (r < 0)
343 return r;
344
345 *ret = TAKE_PTR(m);
346
347 return 0;
348 }
349
350 Manager* manager_free(Manager *m) {
351 if (!m)
352 return NULL;
353
354 hashmap_free_with_destructor(m->links, link_free);
355 hashmap_free(m->links_by_name);
356
357 sd_event_source_unref(m->network_monitor_event_source);
358 sd_network_monitor_unref(m->network_monitor);
359 sd_event_source_unref(m->rtnl_event_source);
360 sd_netlink_unref(m->rtnl);
361 sd_event_unref(m->event);
362
363 return mfree(m);
364 }