]>
Commit | Line | Data |
---|---|---|
0fa8ee6c YW |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
93fabc10 YW |
3 | #include <netinet/in.h> |
4 | #include <linux/if.h> | |
7558f9e7 | 5 | #include <linux/if_arp.h> |
ad851cae | 6 | #include <linux/if_bridge.h> |
93fabc10 | 7 | |
0fa8ee6c | 8 | #include "missing_network.h" |
a8840714 | 9 | #include "netif-util.h" |
0fa8ee6c | 10 | #include "netlink-util.h" |
3b6a3bde | 11 | #include "networkd-address.h" |
7558f9e7 | 12 | #include "networkd-can.h" |
0fa8ee6c YW |
13 | #include "networkd-link.h" |
14 | #include "networkd-manager.h" | |
15 | #include "networkd-queue.h" | |
9b682672 | 16 | #include "networkd-setlink.h" |
e33232d4 | 17 | #include "networkd-sriov.h" |
a9d2d037 | 18 | #include "networkd-wiphy.h" |
0fa8ee6c | 19 | |
1362bd6c YW |
20 | static int get_link_default_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
21 | return link_getlink_handler_internal(rtnl, m, link, "Failed to sync link information"); | |
22 | } | |
23 | ||
440d40dc YW |
24 | static int get_link_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
25 | if (get_link_default_handler(rtnl, m, link) > 0) | |
26 | link->master_set = true; | |
27 | return 0; | |
28 | } | |
29 | ||
baa95d22 YW |
30 | static int get_link_update_flag_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
31 | assert(link); | |
32 | assert(link->set_flags_messages > 0); | |
33 | ||
34 | link->set_flags_messages--; | |
35 | ||
36 | return get_link_default_handler(rtnl, m, link); | |
37 | } | |
38 | ||
1362bd6c YW |
39 | static int set_link_handler_internal( |
40 | sd_netlink *rtnl, | |
41 | sd_netlink_message *m, | |
80d62d4f | 42 | Request *req, |
1362bd6c | 43 | Link *link, |
1362bd6c YW |
44 | bool ignore, |
45 | link_netlink_message_handler_t get_link_handler) { | |
46 | ||
0fa8ee6c YW |
47 | int r; |
48 | ||
49 | assert(m); | |
80d62d4f | 50 | assert(req); |
0fa8ee6c | 51 | assert(link); |
0fa8ee6c | 52 | |
0fa8ee6c YW |
53 | r = sd_netlink_message_get_errno(m); |
54 | if (r < 0) { | |
55 | const char *error_msg; | |
56 | ||
eb93dc9b | 57 | error_msg = strjoina("Failed to set ", request_type_to_string(req->type), ignore ? ", ignoring" : ""); |
0fa8ee6c YW |
58 | log_link_message_warning_errno(link, m, r, error_msg); |
59 | ||
60 | if (!ignore) | |
61 | link_enter_failed(link); | |
80d62d4f | 62 | return 0; |
0fa8ee6c YW |
63 | } |
64 | ||
eb93dc9b | 65 | log_link_debug(link, "%s set.", request_type_to_string(req->type)); |
1362bd6c YW |
66 | |
67 | if (get_link_handler) { | |
68 | r = link_call_getlink(link, get_link_handler); | |
69 | if (r < 0) { | |
70 | link_enter_failed(link); | |
80d62d4f | 71 | return 0; |
1362bd6c YW |
72 | } |
73 | } | |
74 | ||
75 | if (link->set_link_messages == 0) | |
76 | link_check_ready(link); | |
77 | ||
0fa8ee6c YW |
78 | return 1; |
79 | } | |
80 | ||
80d62d4f | 81 | static int link_set_addrgen_mode_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
8e00e24c YW |
82 | int r; |
83 | ||
80d62d4f | 84 | r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, NULL); |
8e00e24c YW |
85 | if (r <= 0) |
86 | return r; | |
87 | ||
88 | r = link_drop_ipv6ll_addresses(link); | |
89 | if (r < 0) { | |
90 | log_link_warning_errno(link, r, "Failed to drop IPv6LL addresses: %m"); | |
91 | link_enter_failed(link); | |
92 | } | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
80d62d4f YW |
97 | static int link_set_bond_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
98 | return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); | |
5062b859 YW |
99 | } |
100 | ||
80d62d4f YW |
101 | static int link_set_bridge_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
102 | return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, NULL); | |
7d5b232f YW |
103 | } |
104 | ||
80d62d4f | 105 | static int link_set_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
228693af YW |
106 | int r; |
107 | ||
108 | assert(link); | |
109 | ||
110 | r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); | |
111 | if (r <= 0) | |
112 | return r; | |
113 | ||
114 | link->bridge_vlan_set = true; | |
115 | return 0; | |
116 | } | |
117 | ||
118 | static int link_del_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { | |
80d62d4f | 119 | return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); |
8252fb44 YW |
120 | } |
121 | ||
80d62d4f YW |
122 | static int link_set_can_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
123 | return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); | |
7558f9e7 YW |
124 | } |
125 | ||
80d62d4f YW |
126 | static int link_set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
127 | return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, get_link_default_handler); | |
93fabc10 YW |
128 | } |
129 | ||
80d62d4f YW |
130 | static int link_set_group_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
131 | return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); | |
cc4c8fb1 YW |
132 | } |
133 | ||
80d62d4f YW |
134 | static int link_set_ipoib_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
135 | return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, NULL); | |
72e65e6f YW |
136 | } |
137 | ||
80d62d4f YW |
138 | static int link_set_mac_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
139 | return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler); | |
a8e5e27c YW |
140 | } |
141 | ||
80d62d4f | 142 | static int link_set_mac_allow_retry_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
d05c332c YW |
143 | int r; |
144 | ||
145 | assert(m); | |
146 | assert(link); | |
d05c332c YW |
147 | |
148 | r = sd_netlink_message_get_errno(m); | |
149 | if (r == -EBUSY) { | |
150 | /* Most real network devices refuse to set its hardware address with -EBUSY when its | |
151 | * operstate is not down. See, eth_prepare_mac_addr_change() in net/ethernet/eth.c | |
152 | * of kernel. */ | |
153 | ||
154 | log_link_message_debug_errno(link, m, r, "Failed to set MAC address, retrying again: %m"); | |
155 | ||
156 | r = link_request_to_set_mac(link, /* allow_retry = */ false); | |
157 | if (r < 0) | |
158 | link_enter_failed(link); | |
159 | ||
160 | return 0; | |
161 | } | |
162 | ||
80d62d4f | 163 | return link_set_mac_handler(rtnl, m, req, link, userdata); |
d05c332c YW |
164 | } |
165 | ||
80d62d4f YW |
166 | static int link_set_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
167 | return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, get_link_master_handler); | |
d24bf1b5 YW |
168 | } |
169 | ||
80d62d4f | 170 | static int link_unset_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
c347a982 | 171 | /* Some devices do not support setting master ifindex. Let's ignore error on unsetting master ifindex. */ |
80d62d4f | 172 | return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_master_handler); |
c347a982 YW |
173 | } |
174 | ||
80d62d4f | 175 | static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
0fa8ee6c YW |
176 | int r; |
177 | ||
80d62d4f | 178 | r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler); |
0fa8ee6c YW |
179 | if (r <= 0) |
180 | return r; | |
181 | ||
182 | /* The kernel resets ipv6 mtu after changing device mtu; | |
183 | * we must set this here, after we've set device mtu */ | |
184 | r = link_set_ipv6_mtu(link); | |
185 | if (r < 0) | |
186 | log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m"); | |
187 | ||
0fa8ee6c YW |
188 | return 0; |
189 | } | |
190 | ||
bb2f88ff | 191 | static int link_configure_fill_message( |
0fa8ee6c | 192 | Link *link, |
bb2f88ff | 193 | sd_netlink_message *req, |
eb93dc9b | 194 | RequestType type, |
bb2f88ff | 195 | void *userdata) { |
0fa8ee6c YW |
196 | int r; |
197 | ||
eb93dc9b YW |
198 | switch (type) { |
199 | case REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE: | |
80f2647d | 200 | r = ipv6ll_addrgen_mode_fill_message(req, PTR_TO_UINT8(userdata)); |
8e00e24c | 201 | if (r < 0) |
bb2f88ff | 202 | return r; |
8e00e24c | 203 | break; |
eb93dc9b | 204 | case REQUEST_TYPE_SET_LINK_BOND: |
5062b859 YW |
205 | r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK); |
206 | if (r < 0) | |
bb2f88ff | 207 | return r; |
5062b859 YW |
208 | |
209 | r = sd_netlink_message_open_container(req, IFLA_LINKINFO); | |
210 | if (r < 0) | |
bb2f88ff | 211 | return r; |
5062b859 YW |
212 | |
213 | r = sd_netlink_message_open_container_union(req, IFLA_INFO_DATA, "bond"); | |
214 | if (r < 0) | |
bb2f88ff | 215 | return r; |
5062b859 YW |
216 | |
217 | if (link->network->active_slave) { | |
218 | r = sd_netlink_message_append_u32(req, IFLA_BOND_ACTIVE_SLAVE, link->ifindex); | |
219 | if (r < 0) | |
bb2f88ff | 220 | return r; |
5062b859 YW |
221 | } |
222 | ||
223 | if (link->network->primary_slave) { | |
224 | r = sd_netlink_message_append_u32(req, IFLA_BOND_PRIMARY, link->ifindex); | |
225 | if (r < 0) | |
bb2f88ff | 226 | return r; |
5062b859 YW |
227 | } |
228 | ||
229 | r = sd_netlink_message_close_container(req); | |
230 | if (r < 0) | |
bb2f88ff | 231 | return r; |
5062b859 YW |
232 | |
233 | r = sd_netlink_message_close_container(req); | |
234 | if (r < 0) | |
bb2f88ff | 235 | return r; |
5062b859 YW |
236 | |
237 | break; | |
eb93dc9b | 238 | case REQUEST_TYPE_SET_LINK_BRIDGE: |
7d5b232f YW |
239 | r = sd_rtnl_message_link_set_family(req, AF_BRIDGE); |
240 | if (r < 0) | |
bb2f88ff | 241 | return r; |
7d5b232f YW |
242 | |
243 | r = sd_netlink_message_open_container(req, IFLA_PROTINFO); | |
244 | if (r < 0) | |
bb2f88ff | 245 | return r; |
7d5b232f YW |
246 | |
247 | if (link->network->use_bpdu >= 0) { | |
3f504b89 | 248 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, !link->network->use_bpdu); |
7d5b232f | 249 | if (r < 0) |
bb2f88ff | 250 | return r; |
7d5b232f YW |
251 | } |
252 | ||
253 | if (link->network->hairpin >= 0) { | |
254 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MODE, link->network->hairpin); | |
255 | if (r < 0) | |
bb2f88ff | 256 | return r; |
7d5b232f YW |
257 | } |
258 | ||
97f27f8a SW |
259 | if (link->network->isolated >= 0) { |
260 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_ISOLATED, link->network->isolated); | |
261 | if (r < 0) | |
262 | return r; | |
263 | } | |
264 | ||
7d5b232f YW |
265 | if (link->network->fast_leave >= 0) { |
266 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_FAST_LEAVE, link->network->fast_leave); | |
267 | if (r < 0) | |
bb2f88ff | 268 | return r; |
7d5b232f YW |
269 | } |
270 | ||
271 | if (link->network->allow_port_to_be_root >= 0) { | |
3f504b89 | 272 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, !link->network->allow_port_to_be_root); |
7d5b232f | 273 | if (r < 0) |
bb2f88ff | 274 | return r; |
7d5b232f YW |
275 | } |
276 | ||
277 | if (link->network->unicast_flood >= 0) { | |
278 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_UNICAST_FLOOD, link->network->unicast_flood); | |
279 | if (r < 0) | |
bb2f88ff | 280 | return r; |
7d5b232f YW |
281 | } |
282 | ||
283 | if (link->network->multicast_flood >= 0) { | |
284 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_FLOOD, link->network->multicast_flood); | |
285 | if (r < 0) | |
bb2f88ff | 286 | return r; |
7d5b232f YW |
287 | } |
288 | ||
289 | if (link->network->multicast_to_unicast >= 0) { | |
290 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_TO_UCAST, link->network->multicast_to_unicast); | |
291 | if (r < 0) | |
bb2f88ff | 292 | return r; |
7d5b232f YW |
293 | } |
294 | ||
295 | if (link->network->neighbor_suppression >= 0) { | |
296 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_NEIGH_SUPPRESS, link->network->neighbor_suppression); | |
297 | if (r < 0) | |
bb2f88ff | 298 | return r; |
7d5b232f YW |
299 | } |
300 | ||
301 | if (link->network->learning >= 0) { | |
302 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_LEARNING, link->network->learning); | |
303 | if (r < 0) | |
bb2f88ff | 304 | return r; |
7d5b232f YW |
305 | } |
306 | ||
307 | if (link->network->bridge_proxy_arp >= 0) { | |
308 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROXYARP, link->network->bridge_proxy_arp); | |
309 | if (r < 0) | |
bb2f88ff | 310 | return r; |
7d5b232f YW |
311 | } |
312 | ||
313 | if (link->network->bridge_proxy_arp_wifi >= 0) { | |
314 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROXYARP_WIFI, link->network->bridge_proxy_arp_wifi); | |
315 | if (r < 0) | |
bb2f88ff | 316 | return r; |
7d5b232f YW |
317 | } |
318 | ||
319 | if (link->network->cost != 0) { | |
320 | r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost); | |
321 | if (r < 0) | |
bb2f88ff | 322 | return r; |
7d5b232f YW |
323 | } |
324 | ||
325 | if (link->network->priority != LINK_BRIDGE_PORT_PRIORITY_INVALID) { | |
326 | r = sd_netlink_message_append_u16(req, IFLA_BRPORT_PRIORITY, link->network->priority); | |
327 | if (r < 0) | |
bb2f88ff | 328 | return r; |
7d5b232f YW |
329 | } |
330 | ||
331 | if (link->network->multicast_router != _MULTICAST_ROUTER_INVALID) { | |
332 | r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MULTICAST_ROUTER, link->network->multicast_router); | |
333 | if (r < 0) | |
bb2f88ff | 334 | return r; |
7d5b232f YW |
335 | } |
336 | ||
337 | r = sd_netlink_message_close_container(req); | |
338 | if (r < 0) | |
bb2f88ff | 339 | return r; |
7d5b232f | 340 | break; |
eb93dc9b | 341 | case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN: |
228693af YW |
342 | r = bridge_vlan_set_message(link, req, /* is_set = */ true); |
343 | if (r < 0) | |
344 | return r; | |
345 | break; | |
346 | case REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN: | |
347 | r = bridge_vlan_set_message(link, req, /* is_set = */ false); | |
8252fb44 | 348 | if (r < 0) |
bb2f88ff | 349 | return r; |
8252fb44 | 350 | break; |
eb93dc9b | 351 | case REQUEST_TYPE_SET_LINK_CAN: |
7558f9e7 YW |
352 | r = can_set_netlink_message(link, req); |
353 | if (r < 0) | |
bb2f88ff | 354 | return r; |
7558f9e7 | 355 | break; |
eb93dc9b | 356 | case REQUEST_TYPE_SET_LINK_FLAGS: { |
93fabc10 YW |
357 | unsigned ifi_change = 0, ifi_flags = 0; |
358 | ||
359 | if (link->network->arp >= 0) { | |
360 | ifi_change |= IFF_NOARP; | |
361 | SET_FLAG(ifi_flags, IFF_NOARP, link->network->arp == 0); | |
362 | } | |
363 | ||
364 | if (link->network->multicast >= 0) { | |
365 | ifi_change |= IFF_MULTICAST; | |
366 | SET_FLAG(ifi_flags, IFF_MULTICAST, link->network->multicast); | |
367 | } | |
368 | ||
369 | if (link->network->allmulticast >= 0) { | |
370 | ifi_change |= IFF_ALLMULTI; | |
371 | SET_FLAG(ifi_flags, IFF_ALLMULTI, link->network->allmulticast); | |
372 | } | |
373 | ||
374 | if (link->network->promiscuous >= 0) { | |
375 | ifi_change |= IFF_PROMISC; | |
376 | SET_FLAG(ifi_flags, IFF_PROMISC, link->network->promiscuous); | |
377 | } | |
378 | ||
379 | r = sd_rtnl_message_link_set_flags(req, ifi_flags, ifi_change); | |
380 | if (r < 0) | |
bb2f88ff | 381 | return r; |
93fabc10 YW |
382 | |
383 | break; | |
384 | } | |
eb93dc9b | 385 | case REQUEST_TYPE_SET_LINK_GROUP: |
10af8bb2 | 386 | r = sd_netlink_message_append_u32(req, IFLA_GROUP, (uint32_t) link->network->group); |
cc4c8fb1 | 387 | if (r < 0) |
bb2f88ff | 388 | return r; |
cc4c8fb1 | 389 | break; |
eb93dc9b | 390 | case REQUEST_TYPE_SET_LINK_MAC: |
a8840714 | 391 | r = netlink_message_append_hw_addr(req, IFLA_ADDRESS, &link->requested_hw_addr); |
a8e5e27c | 392 | if (r < 0) |
bb2f88ff | 393 | return r; |
a8e5e27c | 394 | break; |
eb93dc9b | 395 | case REQUEST_TYPE_SET_LINK_IPOIB: |
72e65e6f YW |
396 | r = ipoib_set_netlink_message(link, req); |
397 | if (r < 0) | |
bb2f88ff | 398 | return r; |
72e65e6f | 399 | break; |
eb93dc9b | 400 | case REQUEST_TYPE_SET_LINK_MASTER: |
d24bf1b5 YW |
401 | r = sd_netlink_message_append_u32(req, IFLA_MASTER, PTR_TO_UINT32(userdata)); |
402 | if (r < 0) | |
bb2f88ff | 403 | return r; |
d24bf1b5 | 404 | break; |
eb93dc9b | 405 | case REQUEST_TYPE_SET_LINK_MTU: |
0fa8ee6c YW |
406 | r = sd_netlink_message_append_u32(req, IFLA_MTU, PTR_TO_UINT32(userdata)); |
407 | if (r < 0) | |
bb2f88ff | 408 | return r; |
0fa8ee6c YW |
409 | break; |
410 | default: | |
04499a70 | 411 | assert_not_reached(); |
0fa8ee6c YW |
412 | } |
413 | ||
bb2f88ff ZJS |
414 | return 0; |
415 | } | |
416 | ||
80d62d4f YW |
417 | static int link_configure(Link *link, Request *req) { |
418 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
bb2f88ff ZJS |
419 | int r; |
420 | ||
421 | assert(link); | |
422 | assert(link->manager); | |
80d62d4f YW |
423 | assert(req); |
424 | ||
eb93dc9b | 425 | log_link_debug(link, "Setting %s", request_type_to_string(req->type)); |
bb2f88ff | 426 | |
eb93dc9b | 427 | if (req->type == REQUEST_TYPE_SET_LINK_BOND) |
80d62d4f | 428 | r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->master_ifindex); |
eb93dc9b | 429 | else if (IN_SET(req->type, REQUEST_TYPE_SET_LINK_CAN, REQUEST_TYPE_SET_LINK_IPOIB)) |
80d62d4f | 430 | r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex); |
228693af YW |
431 | else if (req->type == REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN) |
432 | r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_DELLINK, link->ifindex); | |
bb2f88ff | 433 | else |
80d62d4f | 434 | r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex); |
bb2f88ff | 435 | if (r < 0) |
80d62d4f | 436 | return r; |
bb2f88ff | 437 | |
eb93dc9b | 438 | r = link_configure_fill_message(link, m, req->type, req->userdata); |
0fa8ee6c | 439 | if (r < 0) |
80d62d4f | 440 | return r; |
0fa8ee6c | 441 | |
80d62d4f | 442 | return request_call_netlink_async(link->manager->rtnl, m, req); |
0fa8ee6c YW |
443 | } |
444 | ||
d24bf1b5 YW |
445 | static bool netdev_is_ready(NetDev *netdev) { |
446 | assert(netdev); | |
447 | ||
448 | if (netdev->state != NETDEV_STATE_READY) | |
449 | return false; | |
450 | if (netdev->ifindex == 0) | |
451 | return false; | |
452 | ||
453 | return true; | |
454 | } | |
455 | ||
e5192864 | 456 | static int link_is_ready_to_set_link(Link *link, Request *req) { |
d24bf1b5 | 457 | int r; |
0fa8ee6c | 458 | |
e5192864 YW |
459 | assert(link); |
460 | assert(link->manager); | |
461 | assert(link->network); | |
0fa8ee6c YW |
462 | assert(req); |
463 | ||
a48ffe47 | 464 | if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) |
0fa8ee6c YW |
465 | return false; |
466 | ||
eb93dc9b YW |
467 | switch (req->type) { |
468 | case REQUEST_TYPE_SET_LINK_BOND: | |
469 | case REQUEST_TYPE_SET_LINK_BRIDGE: | |
57aef9d7 YW |
470 | if (!link->master_set) |
471 | return false; | |
eb93dc9b | 472 | |
57aef9d7 YW |
473 | if (link->network->keep_master && link->master_ifindex <= 0) |
474 | return false; | |
475 | break; | |
eb93dc9b YW |
476 | |
477 | case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN: | |
440d40dc YW |
478 | if (!link->master_set) |
479 | return false; | |
eb93dc9b | 480 | |
57aef9d7 YW |
481 | if (link->network->keep_master && link->master_ifindex <= 0 && !streq_ptr(link->kind, "bridge")) |
482 | return false; | |
440d40dc | 483 | break; |
eb93dc9b | 484 | |
228693af YW |
485 | case REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN: |
486 | return link->bridge_vlan_set; | |
487 | ||
eb93dc9b | 488 | case REQUEST_TYPE_SET_LINK_CAN: |
baa95d22 YW |
489 | /* Do not check link->set_flgas_messages here, as it is ok even if link->flags |
490 | * is outdated, and checking the counter causes a deadlock. */ | |
7558f9e7 YW |
491 | if (FLAGS_SET(link->flags, IFF_UP)) { |
492 | /* The CAN interface must be down to configure bitrate, etc... */ | |
cae162a7 | 493 | r = link_down_now(link); |
e5192864 YW |
494 | if (r < 0) |
495 | return r; | |
7558f9e7 YW |
496 | } |
497 | break; | |
eb93dc9b YW |
498 | |
499 | case REQUEST_TYPE_SET_LINK_MAC: | |
d05c332c | 500 | if (req->netlink_handler == link_set_mac_handler) { |
36edc2c9 | 501 | /* This is the second attempt to set hardware address. On the first attempt |
d05c332c | 502 | * req->netlink_handler points to link_set_mac_allow_retry_handler(). |
36edc2c9 | 503 | * The first attempt failed as the interface was up. */ |
cae162a7 | 504 | r = link_down_now(link); |
e5192864 YW |
505 | if (r < 0) |
506 | return r; | |
f1a69d5a JZ |
507 | |
508 | /* If the kind of the link is "bond", we need | |
509 | * set the slave link down as well. */ | |
510 | if (streq_ptr(link->kind, "bond")) { | |
511 | r = link_down_slave_links(link); | |
512 | if (r < 0) | |
513 | return r; | |
514 | } | |
d05c332c YW |
515 | } |
516 | break; | |
eb93dc9b YW |
517 | |
518 | case REQUEST_TYPE_SET_LINK_MASTER: { | |
d24bf1b5 | 519 | uint32_t m = 0; |
5329a379 YW |
520 | Request req_mac = { |
521 | .link = link, | |
eb93dc9b | 522 | .type = REQUEST_TYPE_SET_LINK_MAC, |
5329a379 | 523 | }; |
d24bf1b5 | 524 | |
d24bf1b5 YW |
525 | if (link->network->batadv) { |
526 | if (!netdev_is_ready(link->network->batadv)) | |
527 | return false; | |
528 | m = link->network->batadv->ifindex; | |
529 | } else if (link->network->bond) { | |
5329a379 YW |
530 | if (ordered_set_contains(link->manager->request_queue, &req_mac)) |
531 | return false; | |
d24bf1b5 YW |
532 | if (!netdev_is_ready(link->network->bond)) |
533 | return false; | |
534 | m = link->network->bond->ifindex; | |
535 | ||
baa95d22 YW |
536 | /* Do not check link->set_flgas_messages here, as it is ok even if link->flags |
537 | * is outdated, and checking the counter causes a deadlock. */ | |
d24bf1b5 YW |
538 | if (FLAGS_SET(link->flags, IFF_UP)) { |
539 | /* link must be down when joining to bond master. */ | |
cae162a7 | 540 | r = link_down_now(link); |
e5192864 YW |
541 | if (r < 0) |
542 | return r; | |
d24bf1b5 YW |
543 | } |
544 | } else if (link->network->bridge) { | |
5329a379 YW |
545 | if (ordered_set_contains(link->manager->request_queue, &req_mac)) |
546 | return false; | |
d24bf1b5 YW |
547 | if (!netdev_is_ready(link->network->bridge)) |
548 | return false; | |
549 | m = link->network->bridge->ifindex; | |
550 | } else if (link->network->vrf) { | |
551 | if (!netdev_is_ready(link->network->vrf)) | |
552 | return false; | |
553 | m = link->network->vrf->ifindex; | |
554 | } | |
555 | ||
9f913d37 YW |
556 | if (m == (uint32_t) link->master_ifindex) { |
557 | /* The requested master is already set. */ | |
558 | link->master_set = true; | |
559 | return -EALREADY; /* indicate to cancel the request. */ | |
560 | } | |
561 | ||
d24bf1b5 YW |
562 | req->userdata = UINT32_TO_PTR(m); |
563 | break; | |
564 | } | |
eb93dc9b | 565 | case REQUEST_TYPE_SET_LINK_MTU: { |
a60cc587 YW |
566 | if (ordered_set_contains(link->manager->request_queue, |
567 | &(const Request) { | |
568 | .link = link, | |
569 | .type = REQUEST_TYPE_SET_LINK_IPOIB, | |
570 | })) | |
571 | return false; | |
c3747f90 | 572 | |
a60cc587 YW |
573 | /* Changing FD mode may affect MTU. */ |
574 | if (ordered_set_contains(link->manager->request_queue, | |
575 | &(const Request) { | |
576 | .link = link, | |
577 | .type = REQUEST_TYPE_SET_LINK_CAN, | |
578 | })) | |
579 | return false; | |
c3747f90 | 580 | } |
d24bf1b5 YW |
581 | default: |
582 | break; | |
583 | } | |
584 | ||
0fa8ee6c YW |
585 | return true; |
586 | } | |
587 | ||
09d09207 | 588 | static int link_process_set_link(Request *req, Link *link, void *userdata) { |
0fa8ee6c YW |
589 | int r; |
590 | ||
591 | assert(req); | |
eb93dc9b | 592 | assert(link); |
9b682672 | 593 | |
e5192864 | 594 | r = link_is_ready_to_set_link(link, req); |
9f913d37 YW |
595 | if (r == -EALREADY) |
596 | return 1; /* Cancel the request. */ | |
e5192864 YW |
597 | if (r <= 0) |
598 | return r; | |
0fa8ee6c | 599 | |
e5192864 | 600 | r = link_configure(link, req); |
0fa8ee6c | 601 | if (r < 0) |
eb93dc9b | 602 | return log_link_warning_errno(link, r, "Failed to set %s", request_type_to_string(req->type)); |
0fa8ee6c YW |
603 | |
604 | return 1; | |
605 | } | |
606 | ||
607 | static int link_request_set_link( | |
608 | Link *link, | |
eb93dc9b | 609 | RequestType type, |
80d62d4f | 610 | request_netlink_handler_t netlink_handler, |
0fa8ee6c YW |
611 | Request **ret) { |
612 | ||
613 | Request *req; | |
614 | int r; | |
615 | ||
616 | assert(link); | |
0fa8ee6c | 617 | |
09d09207 YW |
618 | r = link_queue_request_full(link, type, NULL, NULL, NULL, NULL, |
619 | link_process_set_link, | |
620 | &link->set_link_messages, | |
621 | netlink_handler, | |
622 | &req); | |
0fa8ee6c | 623 | if (r < 0) |
eb93dc9b YW |
624 | return log_link_warning_errno(link, r, "Failed to request to set %s: %m", |
625 | request_type_to_string(type)); | |
0fa8ee6c | 626 | |
eb93dc9b | 627 | log_link_debug(link, "Requested to set %s", request_type_to_string(type)); |
0fa8ee6c YW |
628 | |
629 | if (ret) | |
630 | *ret = req; | |
631 | return 0; | |
632 | } | |
633 | ||
8e00e24c | 634 | int link_request_to_set_addrgen_mode(Link *link) { |
80f2647d | 635 | IPv6LinkLocalAddressGenMode mode; |
8e00e24c | 636 | Request *req; |
8e00e24c YW |
637 | int r; |
638 | ||
639 | assert(link); | |
640 | assert(link->network); | |
641 | ||
642 | if (!socket_ipv6_is_supported()) | |
643 | return 0; | |
644 | ||
80f2647d | 645 | mode = link_get_ipv6ll_addrgen_mode(link); |
8e00e24c | 646 | |
05b8fc49 YW |
647 | if (mode == link->ipv6ll_address_gen_mode) |
648 | return 0; | |
649 | ||
77d65e56 JG |
650 | /* If the link is already up, then changing the mode by netlink does not take effect until the |
651 | * link goes down. Hence, we need to reset the interface. However, setting the mode by sysctl | |
652 | * does not need that. Let's use the sysctl interface when the link is already up. | |
653 | * See also issue #22424. */ | |
654 | if (mode != IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE && | |
655 | FLAGS_SET(link->flags, IFF_UP)) { | |
656 | r = link_set_ipv6ll_addrgen_mode(link, mode); | |
657 | if (r < 0) | |
658 | log_link_warning_errno(link, r, "Cannot set IPv6 address generation mode, ignoring: %m"); | |
659 | ||
660 | return 0; | |
661 | } | |
8e00e24c | 662 | |
eb93dc9b YW |
663 | r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE, |
664 | link_set_addrgen_mode_handler, | |
665 | &req); | |
8e00e24c YW |
666 | if (r < 0) |
667 | return r; | |
668 | ||
669 | req->userdata = UINT8_TO_PTR(mode); | |
670 | return 0; | |
671 | } | |
672 | ||
5062b859 YW |
673 | int link_request_to_set_bond(Link *link) { |
674 | assert(link); | |
675 | assert(link->network); | |
676 | ||
57aef9d7 YW |
677 | if (!link->network->bond) { |
678 | Link *master; | |
679 | ||
680 | if (!link->network->keep_master) | |
681 | return 0; | |
682 | ||
683 | if (link_get_master(link, &master) < 0) | |
684 | return 0; | |
685 | ||
686 | if (!streq_ptr(master->kind, "bond")) | |
687 | return 0; | |
688 | } | |
5062b859 | 689 | |
eb93dc9b YW |
690 | return link_request_set_link(link, REQUEST_TYPE_SET_LINK_BOND, |
691 | link_set_bond_handler, NULL); | |
5062b859 YW |
692 | } |
693 | ||
7d5b232f YW |
694 | int link_request_to_set_bridge(Link *link) { |
695 | assert(link); | |
696 | assert(link->network); | |
697 | ||
57aef9d7 YW |
698 | if (!link->network->bridge) { |
699 | Link *master; | |
700 | ||
701 | if (!link->network->keep_master) | |
702 | return 0; | |
703 | ||
704 | if (link_get_master(link, &master) < 0) | |
705 | return 0; | |
706 | ||
707 | if (!streq_ptr(master->kind, "bridge")) | |
708 | return 0; | |
709 | } | |
7d5b232f | 710 | |
eb93dc9b YW |
711 | return link_request_set_link(link, REQUEST_TYPE_SET_LINK_BRIDGE, |
712 | link_set_bridge_handler, | |
713 | NULL); | |
7d5b232f YW |
714 | } |
715 | ||
8252fb44 | 716 | int link_request_to_set_bridge_vlan(Link *link) { |
228693af YW |
717 | int r; |
718 | ||
8252fb44 YW |
719 | assert(link); |
720 | assert(link->network); | |
721 | ||
f269016c | 722 | /* If nothing configured, use the default vlan ID. */ |
228693af YW |
723 | if (memeqzero(link->network->bridge_vlan_bitmap, BRIDGE_VLAN_BITMAP_LEN * sizeof(uint32_t)) && |
724 | link->network->bridge_vlan_pvid == BRIDGE_VLAN_KEEP_PVID) | |
8252fb44 YW |
725 | return 0; |
726 | ||
57aef9d7 YW |
727 | if (!link->network->bridge && !streq_ptr(link->kind, "bridge")) { |
728 | Link *master; | |
729 | ||
730 | if (!link->network->keep_master) | |
731 | return 0; | |
732 | ||
733 | if (link_get_master(link, &master) < 0) | |
734 | return 0; | |
735 | ||
736 | if (!streq_ptr(master->kind, "bridge")) | |
737 | return 0; | |
738 | } | |
8252fb44 | 739 | |
228693af YW |
740 | link->bridge_vlan_set = false; |
741 | ||
742 | r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_BRIDGE_VLAN, | |
743 | link_set_bridge_vlan_handler, | |
744 | NULL); | |
745 | if (r < 0) | |
746 | return r; | |
747 | ||
748 | r = link_request_set_link(link, REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN, | |
749 | link_del_bridge_vlan_handler, | |
750 | NULL); | |
751 | if (r < 0) | |
752 | return r; | |
753 | ||
754 | return 0; | |
8252fb44 YW |
755 | } |
756 | ||
7558f9e7 YW |
757 | int link_request_to_set_can(Link *link) { |
758 | assert(link); | |
759 | assert(link->network); | |
760 | ||
761 | if (link->iftype != ARPHRD_CAN) | |
762 | return 0; | |
763 | ||
764 | if (!streq_ptr(link->kind, "can")) | |
765 | return 0; | |
766 | ||
eb93dc9b YW |
767 | return link_request_set_link(link, REQUEST_TYPE_SET_LINK_CAN, |
768 | link_set_can_handler, | |
769 | NULL); | |
7558f9e7 YW |
770 | } |
771 | ||
93fabc10 YW |
772 | int link_request_to_set_flags(Link *link) { |
773 | assert(link); | |
774 | assert(link->network); | |
775 | ||
776 | if (link->network->arp < 0 && | |
777 | link->network->multicast < 0 && | |
778 | link->network->allmulticast < 0 && | |
779 | link->network->promiscuous < 0) | |
780 | return 0; | |
781 | ||
eb93dc9b YW |
782 | return link_request_set_link(link, REQUEST_TYPE_SET_LINK_FLAGS, |
783 | link_set_flags_handler, | |
784 | NULL); | |
93fabc10 YW |
785 | } |
786 | ||
cc4c8fb1 YW |
787 | int link_request_to_set_group(Link *link) { |
788 | assert(link); | |
789 | assert(link->network); | |
790 | ||
10af8bb2 | 791 | if (link->network->group < 0) |
cc4c8fb1 YW |
792 | return 0; |
793 | ||
eb93dc9b YW |
794 | return link_request_set_link(link, REQUEST_TYPE_SET_LINK_GROUP, |
795 | link_set_group_handler, | |
796 | NULL); | |
cc4c8fb1 YW |
797 | } |
798 | ||
d05c332c | 799 | int link_request_to_set_mac(Link *link, bool allow_retry) { |
a8840714 YW |
800 | int r; |
801 | ||
a8e5e27c YW |
802 | assert(link); |
803 | assert(link->network); | |
804 | ||
a8840714 | 805 | if (link->network->hw_addr.length == 0) |
a8e5e27c YW |
806 | return 0; |
807 | ||
a8840714 | 808 | link->requested_hw_addr = link->network->hw_addr; |
45aa0e84 | 809 | r = net_verify_hardware_address(link->ifname, /* is_static = */ true, |
a8840714 YW |
810 | link->iftype, &link->hw_addr, &link->requested_hw_addr); |
811 | if (r < 0) | |
812 | return r; | |
5388e103 | 813 | |
a8840714 | 814 | if (hw_addr_equal(&link->hw_addr, &link->requested_hw_addr)) |
5388e103 YW |
815 | return 0; |
816 | ||
eb93dc9b | 817 | return link_request_set_link(link, REQUEST_TYPE_SET_LINK_MAC, |
d05c332c YW |
818 | allow_retry ? link_set_mac_allow_retry_handler : link_set_mac_handler, |
819 | NULL); | |
a8e5e27c YW |
820 | } |
821 | ||
72e65e6f YW |
822 | int link_request_to_set_ipoib(Link *link) { |
823 | assert(link); | |
824 | assert(link->network); | |
825 | ||
826 | if (link->iftype != ARPHRD_INFINIBAND) | |
827 | return 0; | |
828 | ||
829 | if (link->network->ipoib_mode < 0 && | |
830 | link->network->ipoib_umcast < 0) | |
831 | return 0; | |
832 | ||
eb93dc9b YW |
833 | return link_request_set_link(link, REQUEST_TYPE_SET_LINK_IPOIB, |
834 | link_set_ipoib_handler, | |
835 | NULL); | |
72e65e6f YW |
836 | } |
837 | ||
d24bf1b5 YW |
838 | int link_request_to_set_master(Link *link) { |
839 | assert(link); | |
c347a982 | 840 | assert(link->network); |
d24bf1b5 | 841 | |
57aef9d7 | 842 | if (link->network->keep_master) { |
53c2e3b9 | 843 | /* When KeepMaster=yes, BatmanAdvanced=, Bond=, Bridge=, and VRF= are ignored. */ |
57aef9d7 YW |
844 | link->master_set = true; |
845 | return 0; | |
440d40dc | 846 | |
53c2e3b9 YW |
847 | } else if (link->network->batadv || link->network->bond || link->network->bridge || link->network->vrf) { |
848 | link->master_set = false; | |
eb93dc9b YW |
849 | return link_request_set_link(link, REQUEST_TYPE_SET_LINK_MASTER, |
850 | link_set_master_handler, | |
851 | NULL); | |
53c2e3b9 YW |
852 | |
853 | } else if (link->master_ifindex != 0) { | |
854 | /* Unset master only when it is set. */ | |
855 | link->master_set = false; | |
eb93dc9b YW |
856 | return link_request_set_link(link, REQUEST_TYPE_SET_LINK_MASTER, |
857 | link_unset_master_handler, | |
858 | NULL); | |
53c2e3b9 YW |
859 | |
860 | } else { | |
861 | /* Nothing we need to do. */ | |
862 | link->master_set = true; | |
863 | return 0; | |
864 | } | |
d24bf1b5 YW |
865 | } |
866 | ||
0fa8ee6c | 867 | int link_request_to_set_mtu(Link *link, uint32_t mtu) { |
717ba5fc | 868 | const char *origin; |
15be8042 | 869 | uint32_t min_mtu, max_mtu; |
eb93dc9b | 870 | Request *req; |
0fa8ee6c YW |
871 | int r; |
872 | ||
873 | assert(link); | |
717ba5fc YW |
874 | assert(link->network); |
875 | ||
876 | min_mtu = link->min_mtu; | |
877 | origin = "the minimum MTU of the interface"; | |
878 | if (link_ipv6_enabled(link)) { | |
879 | /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes on the interface. Bump up | |
880 | * MTU bytes to IPV6_MTU_MIN. */ | |
881 | if (min_mtu < IPV6_MIN_MTU) { | |
882 | min_mtu = IPV6_MIN_MTU; | |
883 | origin = "the minimum IPv6 MTU"; | |
884 | } | |
885 | if (min_mtu < link->network->ipv6_mtu) { | |
886 | min_mtu = link->network->ipv6_mtu; | |
887 | origin = "the requested IPv6 MTU in IPv6MTUBytes="; | |
888 | } | |
889 | } | |
890 | ||
891 | if (mtu < min_mtu) { | |
892 | log_link_warning(link, "Bumping the requested MTU %"PRIu32" to %s (%"PRIu32")", | |
893 | mtu, origin, min_mtu); | |
894 | mtu = min_mtu; | |
895 | } | |
0fa8ee6c | 896 | |
15be8042 YW |
897 | max_mtu = link->max_mtu; |
898 | if (link->iftype == ARPHRD_CAN) | |
899 | /* The maximum MTU may be changed when FD mode is changed. | |
900 | * See https://docs.kernel.org/networking/can.html#can-fd-flexible-data-rate-driver-support | |
901 | * MTU = 16 (CAN_MTU) => Classical CAN device | |
902 | * MTU = 72 (CANFD_MTU) => CAN FD capable device | |
903 | * So, even if the current maximum is 16, we should not reduce the requested value now. */ | |
904 | max_mtu = MAX(max_mtu, 72u); | |
905 | ||
906 | if (mtu > max_mtu) { | |
717ba5fc | 907 | log_link_warning(link, "Reducing the requested MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".", |
15be8042 YW |
908 | mtu, max_mtu); |
909 | mtu = max_mtu; | |
0fa8ee6c YW |
910 | } |
911 | ||
912 | if (link->mtu == mtu) | |
913 | return 0; | |
914 | ||
eb93dc9b YW |
915 | r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_MTU, |
916 | link_set_mtu_handler, | |
917 | &req); | |
0fa8ee6c YW |
918 | if (r < 0) |
919 | return r; | |
920 | ||
921 | req->userdata = UINT32_TO_PTR(mtu); | |
922 | return 0; | |
923 | } | |
924 | ||
925 | static bool link_reduces_vlan_mtu(Link *link) { | |
926 | /* See netif_reduces_vlan_mtu() in kernel. */ | |
927 | return streq_ptr(link->kind, "macsec"); | |
928 | } | |
929 | ||
930 | static uint32_t link_get_requested_mtu_by_stacked_netdevs(Link *link) { | |
931 | uint32_t mtu = 0; | |
932 | NetDev *dev; | |
933 | ||
934 | HASHMAP_FOREACH(dev, link->network->stacked_netdevs) | |
935 | if (dev->kind == NETDEV_KIND_VLAN && dev->mtu > 0) | |
936 | /* See vlan_dev_change_mtu() in kernel. */ | |
937 | mtu = MAX(mtu, link_reduces_vlan_mtu(link) ? dev->mtu + 4 : dev->mtu); | |
938 | ||
939 | else if (dev->kind == NETDEV_KIND_MACVLAN && dev->mtu > mtu) | |
940 | /* See macvlan_change_mtu() in kernel. */ | |
941 | mtu = dev->mtu; | |
942 | ||
943 | return mtu; | |
944 | } | |
945 | ||
946 | int link_configure_mtu(Link *link) { | |
947 | uint32_t mtu; | |
948 | ||
949 | assert(link); | |
950 | assert(link->network); | |
951 | ||
952 | if (link->network->mtu > 0) | |
953 | return link_request_to_set_mtu(link, link->network->mtu); | |
954 | ||
955 | mtu = link_get_requested_mtu_by_stacked_netdevs(link); | |
956 | if (link->mtu >= mtu) | |
957 | return 0; | |
958 | ||
959 | log_link_notice(link, "Bumping MTU bytes from %"PRIu32" to %"PRIu32" because of stacked device. " | |
960 | "If it is not desired, then please explicitly specify MTUBytes= setting.", | |
961 | link->mtu, mtu); | |
962 | ||
963 | return link_request_to_set_mtu(link, mtu); | |
964 | } | |
112a0972 | 965 | |
b1d9c504 YW |
966 | static int link_up_dsa_slave(Link *link) { |
967 | Link *master; | |
968 | int r; | |
969 | ||
970 | assert(link); | |
971 | ||
972 | /* For older kernels (specifically, older than 9d5ef190e5615a7b63af89f88c4106a5bc127974, kernel-5.12), | |
973 | * it is necessary to bring up a DSA slave that its master interface is already up. And bringing up | |
974 | * the slave fails with -ENETDOWN. So, let's bring up the master even if it is not managed by us, | |
975 | * and try to bring up the slave after the master becomes up. */ | |
976 | ||
977 | if (link->dsa_master_ifindex <= 0) | |
978 | return 0; | |
979 | ||
980 | if (!streq_ptr(link->driver, "dsa")) | |
981 | return 0; | |
982 | ||
983 | if (link_get_by_index(link->manager, link->dsa_master_ifindex, &master) < 0) | |
984 | return 0; | |
985 | ||
986 | if (master->state == LINK_STATE_UNMANAGED) { | |
987 | /* If the DSA master interface is unmanaged, then it will never become up. | |
988 | * Let's request to bring up the master. */ | |
989 | r = link_request_to_bring_up_or_down(master, /* up = */ true); | |
990 | if (r < 0) | |
991 | return r; | |
992 | } | |
993 | ||
994 | r = link_request_to_bring_up_or_down(link, /* up = */ true); | |
995 | if (r < 0) | |
996 | return r; | |
997 | ||
998 | return 1; | |
999 | } | |
1000 | ||
80d62d4f YW |
1001 | static int link_up_or_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
1002 | bool on_activate, up; | |
112a0972 YW |
1003 | int r; |
1004 | ||
1005 | assert(m); | |
80d62d4f | 1006 | assert(req); |
112a0972 YW |
1007 | assert(link); |
1008 | ||
80d62d4f YW |
1009 | on_activate = req->type == REQUEST_TYPE_ACTIVATE_LINK; |
1010 | up = PTR_TO_INT(req->userdata); | |
112a0972 YW |
1011 | |
1012 | r = sd_netlink_message_get_errno(m); | |
b1d9c504 YW |
1013 | if (r == -ENETDOWN && up && link_up_dsa_slave(link) > 0) |
1014 | log_link_message_debug_errno(link, m, r, "Could not bring up dsa slave, retrying again after dsa master becomes up"); | |
b05e5200 YW |
1015 | else if (r < 0) |
1016 | log_link_message_warning_errno(link, m, r, up ? | |
1017 | "Could not bring up interface, ignoring" : | |
1018 | "Could not bring down interface, ignoring"); | |
112a0972 | 1019 | |
baa95d22 YW |
1020 | r = link_call_getlink(link, get_link_update_flag_handler); |
1021 | if (r < 0) { | |
1022 | link_enter_failed(link); | |
80d62d4f | 1023 | return 0; |
baa95d22 YW |
1024 | } |
1025 | ||
80d62d4f YW |
1026 | link->set_flags_messages++; |
1027 | ||
54cd4bb7 | 1028 | if (on_activate) { |
112a0972 YW |
1029 | link->activated = true; |
1030 | link_check_ready(link); | |
1031 | } | |
1032 | ||
1033 | return 0; | |
1034 | } | |
1035 | ||
68f52063 YW |
1036 | static const char *up_or_down(bool up) { |
1037 | return up ? "up" : "down"; | |
1038 | } | |
1039 | ||
54ff39f7 YW |
1040 | static int link_up_or_down(Link *link, bool up, Request *req) { |
1041 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
112a0972 YW |
1042 | int r; |
1043 | ||
1044 | assert(link); | |
1045 | assert(link->manager); | |
1046 | assert(link->manager->rtnl); | |
54ff39f7 | 1047 | assert(req); |
112a0972 | 1048 | |
68f52063 | 1049 | log_link_debug(link, "Bringing link %s", up_or_down(up)); |
112a0972 | 1050 | |
54ff39f7 | 1051 | r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex); |
112a0972 | 1052 | if (r < 0) |
a79a8d16 | 1053 | return r; |
112a0972 | 1054 | |
54ff39f7 | 1055 | r = sd_rtnl_message_link_set_flags(m, up ? IFF_UP : 0, IFF_UP); |
112a0972 | 1056 | if (r < 0) |
a79a8d16 | 1057 | return r; |
112a0972 | 1058 | |
80d62d4f | 1059 | return request_call_netlink_async(link->manager->rtnl, m, req); |
112a0972 YW |
1060 | } |
1061 | ||
e33232d4 | 1062 | static bool link_is_ready_to_activate_one(Link *link, bool allow_unmanaged) { |
112a0972 YW |
1063 | assert(link); |
1064 | ||
e33232d4 | 1065 | if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED)) |
112a0972 YW |
1066 | return false; |
1067 | ||
e33232d4 YW |
1068 | if (!link->network) |
1069 | return allow_unmanaged; | |
1070 | ||
112a0972 YW |
1071 | if (link->set_link_messages > 0) |
1072 | return false; | |
1073 | ||
e33232d4 YW |
1074 | return true; |
1075 | } | |
1076 | ||
1077 | static bool link_is_ready_to_activate(Link *link, bool up) { | |
1078 | assert(link); | |
1079 | ||
1080 | if (!check_ready_for_all_sr_iov_ports(link, /* allow_unmanaged = */ false, | |
1081 | link_is_ready_to_activate_one)) | |
1082 | return false; | |
1083 | ||
a9d2d037 YW |
1084 | if (up && link_rfkilled(link) > 0) |
1085 | return false; | |
1086 | ||
112a0972 YW |
1087 | return true; |
1088 | } | |
1089 | ||
09d09207 | 1090 | static int link_process_activation(Request *req, Link *link, void *userdata) { |
ff51134c | 1091 | bool up = PTR_TO_INT(userdata); |
112a0972 YW |
1092 | int r; |
1093 | ||
1094 | assert(req); | |
ff51134c | 1095 | assert(link); |
112a0972 | 1096 | |
a9d2d037 | 1097 | if (!link_is_ready_to_activate(link, up)) |
112a0972 YW |
1098 | return 0; |
1099 | ||
54ff39f7 | 1100 | r = link_up_or_down(link, up, req); |
112a0972 | 1101 | if (r < 0) |
a79a8d16 | 1102 | return log_link_warning_errno(link, r, "Failed to activate link: %m"); |
112a0972 YW |
1103 | |
1104 | return 1; | |
1105 | } | |
1106 | ||
1107 | int link_request_to_activate(Link *link) { | |
112a0972 YW |
1108 | bool up; |
1109 | int r; | |
1110 | ||
1111 | assert(link); | |
1112 | assert(link->network); | |
1113 | ||
1114 | switch (link->network->activation_policy) { | |
1115 | case ACTIVATION_POLICY_BOUND: | |
112a0972 YW |
1116 | r = link_handle_bound_to_list(link); |
1117 | if (r < 0) | |
1118 | return r; | |
1119 | _fallthrough_; | |
1120 | case ACTIVATION_POLICY_MANUAL: | |
1121 | link->activated = true; | |
1122 | link_check_ready(link); | |
1123 | return 0; | |
1124 | case ACTIVATION_POLICY_UP: | |
1125 | case ACTIVATION_POLICY_ALWAYS_UP: | |
1126 | up = true; | |
1127 | break; | |
1128 | case ACTIVATION_POLICY_DOWN: | |
1129 | case ACTIVATION_POLICY_ALWAYS_DOWN: | |
1130 | up = false; | |
1131 | break; | |
1132 | default: | |
04499a70 | 1133 | assert_not_reached(); |
112a0972 YW |
1134 | } |
1135 | ||
1136 | link->activated = false; | |
1137 | ||
09d09207 YW |
1138 | r = link_queue_request_full(link, REQUEST_TYPE_ACTIVATE_LINK, |
1139 | INT_TO_PTR(up), NULL, NULL, NULL, | |
1140 | link_process_activation, | |
1141 | &link->set_flags_messages, | |
1142 | link_up_or_down_handler, NULL); | |
112a0972 YW |
1143 | if (r < 0) |
1144 | return log_link_error_errno(link, r, "Failed to request to activate link: %m"); | |
1145 | ||
112a0972 YW |
1146 | log_link_debug(link, "Requested to activate link"); |
1147 | return 0; | |
1148 | } | |
68f52063 | 1149 | |
b1d9c504 | 1150 | static bool link_is_ready_to_bring_up_or_down(Link *link, bool up) { |
68f52063 YW |
1151 | assert(link); |
1152 | ||
b1d9c504 YW |
1153 | if (up && link->dsa_master_ifindex > 0) { |
1154 | Link *master; | |
1155 | ||
3e7bf853 | 1156 | /* The master interface must be up. See comments in link_up_dsa_slave(). */ |
b1d9c504 YW |
1157 | |
1158 | if (link_get_by_index(link->manager, link->dsa_master_ifindex, &master) < 0) | |
1159 | return false; | |
1160 | ||
1161 | if (!FLAGS_SET(master->flags, IFF_UP)) | |
1162 | return false; | |
1163 | } | |
1164 | ||
68f52063 YW |
1165 | if (link->state == LINK_STATE_UNMANAGED) |
1166 | return true; | |
1167 | ||
1168 | if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) | |
1169 | return false; | |
1170 | ||
1171 | if (link->set_link_messages > 0) | |
1172 | return false; | |
1173 | ||
1174 | if (!link->activated) | |
1175 | return false; | |
1176 | ||
a9d2d037 YW |
1177 | if (up && link_rfkilled(link) > 0) |
1178 | return false; | |
1179 | ||
68f52063 YW |
1180 | return true; |
1181 | } | |
1182 | ||
09d09207 | 1183 | static int link_process_up_or_down(Request *req, Link *link, void *userdata) { |
ff51134c | 1184 | bool up = PTR_TO_INT(userdata); |
68f52063 YW |
1185 | int r; |
1186 | ||
1187 | assert(req); | |
ff51134c | 1188 | assert(link); |
68f52063 | 1189 | |
b1d9c504 | 1190 | if (!link_is_ready_to_bring_up_or_down(link, up)) |
68f52063 YW |
1191 | return 0; |
1192 | ||
54ff39f7 | 1193 | r = link_up_or_down(link, up, req); |
68f52063 | 1194 | if (r < 0) |
a79a8d16 | 1195 | return log_link_warning_errno(link, r, "Failed to bring link %s: %m", up_or_down(up)); |
68f52063 YW |
1196 | |
1197 | return 1; | |
1198 | } | |
1199 | ||
1200 | int link_request_to_bring_up_or_down(Link *link, bool up) { | |
68f52063 YW |
1201 | int r; |
1202 | ||
1203 | assert(link); | |
1204 | ||
09d09207 YW |
1205 | r = link_queue_request_full(link, REQUEST_TYPE_UP_DOWN, |
1206 | INT_TO_PTR(up), NULL, NULL, NULL, | |
1207 | link_process_up_or_down, | |
1208 | &link->set_flags_messages, | |
1209 | link_up_or_down_handler, NULL); | |
68f52063 | 1210 | if (r < 0) |
a79a8d16 YW |
1211 | return log_link_warning_errno(link, r, "Failed to request to bring link %s: %m", |
1212 | up_or_down(up)); | |
68f52063 | 1213 | |
68f52063 YW |
1214 | log_link_debug(link, "Requested to bring link %s", up_or_down(up)); |
1215 | return 0; | |
1216 | } | |
63dc6025 | 1217 | |
cae162a7 YW |
1218 | static int link_down_now_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
1219 | int r; | |
1220 | ||
1221 | assert(m); | |
1222 | assert(link); | |
1223 | assert(link->set_flags_messages > 0); | |
1224 | ||
1225 | link->set_flags_messages--; | |
1226 | ||
1227 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
1228 | return 0; | |
1229 | ||
1230 | r = sd_netlink_message_get_errno(m); | |
1231 | if (r < 0) | |
1232 | log_link_message_warning_errno(link, m, r, "Could not bring down interface, ignoring"); | |
1233 | ||
1234 | r = link_call_getlink(link, get_link_update_flag_handler); | |
1235 | if (r < 0) { | |
1236 | link_enter_failed(link); | |
1237 | return 0; | |
1238 | } | |
1239 | ||
1240 | link->set_flags_messages++; | |
1241 | return 0; | |
1242 | } | |
1243 | ||
1244 | int link_down_now(Link *link) { | |
1245 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; | |
1246 | int r; | |
1247 | ||
1248 | assert(link); | |
1249 | assert(link->manager); | |
1250 | assert(link->manager->rtnl); | |
1251 | ||
1252 | log_link_debug(link, "Bringing link down"); | |
1253 | ||
1254 | r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); | |
1255 | if (r < 0) | |
1256 | return log_link_warning_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); | |
1257 | ||
1258 | r = sd_rtnl_message_link_set_flags(req, 0, IFF_UP); | |
1259 | if (r < 0) | |
1260 | return log_link_warning_errno(link, r, "Could not set link flags: %m"); | |
1261 | ||
1262 | r = netlink_call_async(link->manager->rtnl, NULL, req, link_down_now_handler, | |
1263 | link_netlink_destroy_callback, link); | |
1264 | if (r < 0) | |
1265 | return log_link_warning_errno(link, r, "Could not send rtnetlink message: %m"); | |
1266 | ||
1267 | link->set_flags_messages++; | |
1268 | link_ref(link); | |
1269 | return 0; | |
1270 | } | |
1271 | ||
f1a69d5a JZ |
1272 | int link_down_slave_links(Link *link) { |
1273 | Link *slave; | |
1274 | int r; | |
1275 | ||
1276 | assert(link); | |
1277 | ||
1278 | SET_FOREACH(slave, link->slaves) { | |
1279 | r = link_down_now(slave); | |
1280 | if (r < 0) | |
1281 | return r; | |
1282 | } | |
1283 | ||
1284 | return 0; | |
1285 | } | |
1286 | ||
63dc6025 YW |
1287 | static int link_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
1288 | int r; | |
1289 | ||
1290 | assert(m); | |
1291 | assert(link); | |
1292 | ||
1293 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
1294 | return 0; | |
1295 | ||
1296 | r = sd_netlink_message_get_errno(m); | |
1297 | if (r < 0) | |
1298 | log_link_message_warning_errno(link, m, r, "Could not remove interface, ignoring"); | |
1299 | ||
1300 | return 0; | |
1301 | } | |
1302 | ||
1303 | int link_remove(Link *link) { | |
1304 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; | |
1305 | int r; | |
1306 | ||
1307 | assert(link); | |
1308 | assert(link->manager); | |
1309 | assert(link->manager->rtnl); | |
1310 | ||
1311 | log_link_debug(link, "Removing link."); | |
1312 | ||
1313 | r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_DELLINK, link->ifindex); | |
1314 | if (r < 0) | |
1315 | return log_link_debug_errno(link, r, "Could not allocate RTM_DELLINK message: %m"); | |
1316 | ||
1317 | r = netlink_call_async(link->manager->rtnl, NULL, req, link_remove_handler, | |
1318 | link_netlink_destroy_callback, link); | |
1319 | if (r < 0) | |
1320 | return log_link_debug_errno(link, r, "Could not send rtnetlink message: %m"); | |
1321 | ||
1322 | link_ref(link); | |
1323 | ||
1324 | return 0; | |
1325 | } |