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