]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-setlink.c
network: use request queue to set link flags
[thirdparty/systemd.git] / src / network / networkd-setlink.c
CommitLineData
0fa8ee6c
YW
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
93fabc10
YW
3#include <netinet/in.h>
4#include <linux/if.h>
5
0fa8ee6c
YW
6#include "missing_network.h"
7#include "netlink-util.h"
8#include "networkd-link.h"
9#include "networkd-manager.h"
10#include "networkd-queue.h"
11#include "string-table.h"
12
13static const char *const set_link_operation_table[_SET_LINK_OPERATION_MAX] = {
93fabc10 14 [SET_LINK_FLAGS] = "link flags",
0fa8ee6c
YW
15 [SET_LINK_MTU] = "MTU",
16};
17
18DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(set_link_operation, SetLinkOperation);
19
20static int set_link_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, SetLinkOperation op, bool ignore) {
21 int r;
22
23 assert(m);
24 assert(link);
25 assert(link->set_link_messages > 0);
26 assert(op >= 0 && op < _SET_LINK_OPERATION_MAX);
27
28 link->set_link_messages--;
29
30 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
31 return 0;
32
33 r = sd_netlink_message_get_errno(m);
34 if (r < 0) {
35 const char *error_msg;
36
37 error_msg = strjoina("Failed to set ", set_link_operation_to_string(op), ignore ? ", ignoring" : "");
38 log_link_message_warning_errno(link, m, r, error_msg);
39
40 if (!ignore)
41 link_enter_failed(link);
42 return 0;
43 }
44
45 log_link_debug(link, "%s set.", set_link_operation_to_string(op));
46 return 1;
47}
48
93fabc10
YW
49static int link_set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
50 return set_link_handler_internal(rtnl, m, link, SET_LINK_FLAGS, true);
51}
52
0fa8ee6c
YW
53static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
54 int r;
55
56 r = set_link_handler_internal(rtnl, m, link, SET_LINK_MTU, true);
57 if (r <= 0)
58 return r;
59
60 /* The kernel resets ipv6 mtu after changing device mtu;
61 * we must set this here, after we've set device mtu */
62 r = link_set_ipv6_mtu(link);
63 if (r < 0)
64 log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m");
65
66 if (link->entering_to_join_netdev) {
67 r = link_enter_join_netdev(link);
68 if (r < 0)
69 link_enter_failed(link);
70 }
71
72 return 0;
73}
74
75static int link_configure(
76 Link *link,
77 SetLinkOperation op,
78 void *userdata,
79 link_netlink_message_handler_t callback) {
80
81 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
82 int r;
83
84 assert(link);
85 assert(link->manager);
86 assert(link->manager->rtnl);
87 assert(link->network);
88 assert(op >= 0 && op < _SET_LINK_OPERATION_MAX);
89 assert(callback);
90
91 log_link_debug(link, "Setting %s", set_link_operation_to_string(op));
92
93 r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
94 if (r < 0)
95 return log_link_debug_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
96
97 switch (op) {
93fabc10
YW
98 case SET_LINK_FLAGS: {
99 unsigned ifi_change = 0, ifi_flags = 0;
100
101 if (link->network->arp >= 0) {
102 ifi_change |= IFF_NOARP;
103 SET_FLAG(ifi_flags, IFF_NOARP, link->network->arp == 0);
104 }
105
106 if (link->network->multicast >= 0) {
107 ifi_change |= IFF_MULTICAST;
108 SET_FLAG(ifi_flags, IFF_MULTICAST, link->network->multicast);
109 }
110
111 if (link->network->allmulticast >= 0) {
112 ifi_change |= IFF_ALLMULTI;
113 SET_FLAG(ifi_flags, IFF_ALLMULTI, link->network->allmulticast);
114 }
115
116 if (link->network->promiscuous >= 0) {
117 ifi_change |= IFF_PROMISC;
118 SET_FLAG(ifi_flags, IFF_PROMISC, link->network->promiscuous);
119 }
120
121 r = sd_rtnl_message_link_set_flags(req, ifi_flags, ifi_change);
122 if (r < 0)
123 return log_link_debug_errno(link, r, "Could not set link flags: %m");
124
125 break;
126 }
0fa8ee6c
YW
127 case SET_LINK_MTU:
128 r = sd_netlink_message_append_u32(req, IFLA_MTU, PTR_TO_UINT32(userdata));
129 if (r < 0)
130 return log_link_debug_errno(link, r, "Could not append IFLA_MTU attribute: %m");
131 break;
132 default:
133 assert_not_reached("Invalid set link operation");
134 }
135
136 r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
137 link_netlink_destroy_callback, link);
138 if (r < 0)
139 return log_link_debug_errno(link, r, "Could not send RTM_SETLINK message: %m");
140
141 link_ref(link);
142 return 0;
143}
144
145static bool link_is_ready_to_call_set_link(Request *req) {
146 Link *link;
147
148 assert(req);
149
150 link = req->link;
151
152 if (!IN_SET(link->state, LINK_STATE_INITIALIZED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
153 return false;
154
155 return true;
156}
157
158int request_process_set_link(Request *req) {
159 int r;
160
161 assert(req);
162 assert(req->link);
163 assert(req->type == REQUEST_TYPE_SET_LINK);
164 assert(req->set_link_operation >= 0 && req->set_link_operation < _SET_LINK_OPERATION_MAX);
165 assert(req->netlink_handler);
166
167 if (!link_is_ready_to_call_set_link(req))
168 return 0;
169
170 r = link_configure(req->link, req->set_link_operation, req->userdata, req->netlink_handler);
171 if (r < 0)
172 return log_link_error_errno(req->link, r, "Failed to set %s: %m",
173 set_link_operation_to_string(req->set_link_operation));
174
175 return 1;
176}
177
178static int link_request_set_link(
179 Link *link,
180 SetLinkOperation op,
181 link_netlink_message_handler_t netlink_handler,
182 Request **ret) {
183
184 Request *req;
185 int r;
186
187 assert(link);
188 assert(op >= 0 && op < _SET_LINK_OPERATION_MAX);
189 assert(netlink_handler);
190
191 r = link_queue_request(link, REQUEST_TYPE_SET_LINK, INT_TO_PTR(op), false,
192 &link->set_link_messages, netlink_handler, &req);
193 if (r < 0)
194 return log_link_error_errno(link, r, "Failed to request to set %s: %m",
195 set_link_operation_to_string(op));
196
197 log_link_debug(link, "Requested to set %s", set_link_operation_to_string(op));
198
199 if (ret)
200 *ret = req;
201 return 0;
202}
203
93fabc10
YW
204int link_request_to_set_flags(Link *link) {
205 assert(link);
206 assert(link->network);
207
208 if (link->network->arp < 0 &&
209 link->network->multicast < 0 &&
210 link->network->allmulticast < 0 &&
211 link->network->promiscuous < 0)
212 return 0;
213
214 return link_request_set_link(link, SET_LINK_FLAGS, link_set_flags_handler, NULL);
215}
216
0fa8ee6c
YW
217int link_request_to_set_mtu(Link *link, uint32_t mtu) {
218 Request *req = NULL; /* avoid false maybe-uninitialized warning */
219 int r;
220
221 assert(link);
222
223 /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes on the interface. Bump up
224 * MTU bytes to IPV6_MTU_MIN. */
225 if (mtu < IPV6_MIN_MTU && link_ipv6_enabled(link)) {
226 log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as IPv6 is enabled "
227 "and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes");
228 mtu = IPV6_MIN_MTU;
229 }
230
231 if (link->mtu == mtu)
232 return 0;
233
234 r = link_request_set_link(link, SET_LINK_MTU, link_set_mtu_handler, &req);
235 if (r < 0)
236 return r;
237
238 req->userdata = UINT32_TO_PTR(mtu);
239 return 0;
240}
241
242static bool link_reduces_vlan_mtu(Link *link) {
243 /* See netif_reduces_vlan_mtu() in kernel. */
244 return streq_ptr(link->kind, "macsec");
245}
246
247static uint32_t link_get_requested_mtu_by_stacked_netdevs(Link *link) {
248 uint32_t mtu = 0;
249 NetDev *dev;
250
251 HASHMAP_FOREACH(dev, link->network->stacked_netdevs)
252 if (dev->kind == NETDEV_KIND_VLAN && dev->mtu > 0)
253 /* See vlan_dev_change_mtu() in kernel. */
254 mtu = MAX(mtu, link_reduces_vlan_mtu(link) ? dev->mtu + 4 : dev->mtu);
255
256 else if (dev->kind == NETDEV_KIND_MACVLAN && dev->mtu > mtu)
257 /* See macvlan_change_mtu() in kernel. */
258 mtu = dev->mtu;
259
260 return mtu;
261}
262
263int link_configure_mtu(Link *link) {
264 uint32_t mtu;
265
266 assert(link);
267 assert(link->network);
268
269 if (link->network->mtu > 0)
270 return link_request_to_set_mtu(link, link->network->mtu);
271
272 mtu = link_get_requested_mtu_by_stacked_netdevs(link);
273 if (link->mtu >= mtu)
274 return 0;
275
276 log_link_notice(link, "Bumping MTU bytes from %"PRIu32" to %"PRIu32" because of stacked device. "
277 "If it is not desired, then please explicitly specify MTUBytes= setting.",
278 link->mtu, mtu);
279
280 return link_request_to_set_mtu(link, mtu);
281}