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