Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <linux/netdevice.h>
4 : : #include <netinet/ether.h>
5 : :
6 : : #include "sd-device.h"
7 : : #include "sd-netlink.h"
8 : :
9 : : #include "alloc-util.h"
10 : : #include "conf-files.h"
11 : : #include "conf-parser.h"
12 : : #include "def.h"
13 : : #include "device-util.h"
14 : : #include "ethtool-util.h"
15 : : #include "fd-util.h"
16 : : #include "link-config.h"
17 : : #include "log.h"
18 : : #include "memory-util.h"
19 : : #include "naming-scheme.h"
20 : : #include "netlink-util.h"
21 : : #include "network-internal.h"
22 : : #include "parse-util.h"
23 : : #include "path-util.h"
24 : : #include "proc-cmdline.h"
25 : : #include "random-util.h"
26 : : #include "stat-util.h"
27 : : #include "string-table.h"
28 : : #include "string-util.h"
29 : : #include "strv.h"
30 : :
31 : : struct link_config_ctx {
32 : : LIST_HEAD(link_config, links);
33 : :
34 : : int ethtool_fd;
35 : :
36 : : bool enable_name_policy;
37 : :
38 : : sd_netlink *rtnl;
39 : :
40 : : usec_t network_dirs_ts_usec;
41 : : };
42 : :
43 : 0 : static void link_config_free(link_config *link) {
44 [ # # ]: 0 : if (!link)
45 : 0 : return;
46 : :
47 : 0 : free(link->filename);
48 : :
49 : 0 : set_free_free(link->match_mac);
50 : 0 : strv_free(link->match_path);
51 : 0 : strv_free(link->match_driver);
52 : 0 : strv_free(link->match_type);
53 : 0 : strv_free(link->match_name);
54 : 0 : strv_free(link->match_property);
55 : 0 : condition_free_list(link->conditions);
56 : :
57 : 0 : free(link->description);
58 : 0 : free(link->mac);
59 : 0 : free(link->name_policy);
60 : 0 : free(link->name);
61 : 0 : free(link->alias);
62 : :
63 : 0 : free(link);
64 : : }
65 : :
66 [ # # ]: 0 : DEFINE_TRIVIAL_CLEANUP_FUNC(link_config*, link_config_free);
67 : :
68 : 0 : static void link_configs_free(link_config_ctx *ctx) {
69 : : link_config *link, *link_next;
70 : :
71 [ # # ]: 0 : if (!ctx)
72 : 0 : return;
73 : :
74 [ # # ]: 0 : LIST_FOREACH_SAFE(links, link, link_next, ctx->links)
75 : 0 : link_config_free(link);
76 : : }
77 : :
78 : 0 : void link_config_ctx_free(link_config_ctx *ctx) {
79 [ # # ]: 0 : if (!ctx)
80 : 0 : return;
81 : :
82 : 0 : safe_close(ctx->ethtool_fd);
83 : :
84 : 0 : sd_netlink_unref(ctx->rtnl);
85 : :
86 : 0 : link_configs_free(ctx);
87 : :
88 : 0 : free(ctx);
89 : :
90 : 0 : return;
91 : : }
92 : :
93 : 0 : int link_config_ctx_new(link_config_ctx **ret) {
94 : 0 : _cleanup_(link_config_ctx_freep) link_config_ctx *ctx = NULL;
95 : :
96 [ # # ]: 0 : if (!ret)
97 : 0 : return -EINVAL;
98 : :
99 : 0 : ctx = new0(link_config_ctx, 1);
100 [ # # ]: 0 : if (!ctx)
101 : 0 : return -ENOMEM;
102 : :
103 : 0 : LIST_HEAD_INIT(ctx->links);
104 : :
105 : 0 : ctx->ethtool_fd = -1;
106 : :
107 : 0 : ctx->enable_name_policy = true;
108 : :
109 : 0 : *ret = TAKE_PTR(ctx);
110 : :
111 : 0 : return 0;
112 : : }
113 : :
114 : 0 : int link_load_one(link_config_ctx *ctx, const char *filename) {
115 : 0 : _cleanup_(link_config_freep) link_config *link = NULL;
116 : 0 : _cleanup_fclose_ FILE *file = NULL;
117 : 0 : _cleanup_free_ char *name = NULL;
118 : : size_t i;
119 : : int r;
120 : :
121 [ # # ]: 0 : assert(ctx);
122 [ # # ]: 0 : assert(filename);
123 : :
124 : 0 : file = fopen(filename, "re");
125 [ # # ]: 0 : if (!file)
126 [ # # ]: 0 : return errno == ENOENT ? 0 : -errno;
127 : :
128 [ # # ]: 0 : if (null_or_empty_fd(fileno(file))) {
129 [ # # ]: 0 : log_debug("Skipping empty file: %s", filename);
130 : 0 : return 0;
131 : : }
132 : :
133 : 0 : name = strdup(filename);
134 [ # # ]: 0 : if (!name)
135 : 0 : return -ENOMEM;
136 : :
137 : 0 : link = new(link_config, 1);
138 [ # # ]: 0 : if (!link)
139 : 0 : return -ENOMEM;
140 : :
141 : 0 : *link = (link_config) {
142 : 0 : .filename = TAKE_PTR(name),
143 : : .mac_address_policy = _MAC_ADDRESS_POLICY_INVALID,
144 : : .wol = _WOL_INVALID,
145 : : .duplex = _DUP_INVALID,
146 : : .port = _NET_DEV_PORT_INVALID,
147 : : .autonegotiation = -1,
148 : : };
149 : :
150 [ # # ]: 0 : for (i = 0; i < ELEMENTSOF(link->features); i++)
151 : 0 : link->features[i] = -1;
152 : :
153 : 0 : r = config_parse(NULL, filename, file,
154 : : "Match\0Link\0",
155 : : config_item_perf_lookup, link_config_gperf_lookup,
156 : : CONFIG_PARSE_WARN, link);
157 [ # # ]: 0 : if (r < 0)
158 : 0 : return r;
159 : :
160 [ # # ]: 0 : if (link->speed > UINT_MAX)
161 : 0 : return -ERANGE;
162 : :
163 [ # # # # : 0 : if (set_isempty(link->match_mac) && strv_isempty(link->match_path) &&
# # ]
164 [ # # # # ]: 0 : strv_isempty(link->match_driver) && strv_isempty(link->match_type) &&
165 [ # # # # ]: 0 : strv_isempty(link->match_name) && strv_isempty(link->match_property) && !link->conditions)
166 [ # # ]: 0 : log_warning("%s: No valid settings found in the [Match] section. "
167 : : "The file will match all interfaces. "
168 : : "If that is intended, please add OriginalName=* in the [Match] section.",
169 : : filename);
170 : :
171 [ # # ]: 0 : if (!condition_test_list(link->conditions, NULL, NULL, NULL)) {
172 [ # # ]: 0 : log_debug("%s: Conditions do not match the system environment, skipping.", filename);
173 : 0 : return 0;
174 : : }
175 : :
176 [ # # ]: 0 : log_debug("Parsed configuration file %s", filename);
177 : :
178 [ # # # # ]: 0 : LIST_PREPEND(links, ctx->links, TAKE_PTR(link));
179 : 0 : return 0;
180 : : }
181 : :
182 : 0 : static bool enable_name_policy(void) {
183 : : bool b;
184 : :
185 [ # # # # ]: 0 : return proc_cmdline_get_bool("net.ifnames", &b) <= 0 || b;
186 : : }
187 : :
188 : 0 : static int link_unsigned_attribute(sd_device *device, const char *attr, unsigned *type) {
189 : : const char *s;
190 : : int r;
191 : :
192 : 0 : r = sd_device_get_sysattr_value(device, attr, &s);
193 [ # # ]: 0 : if (r < 0)
194 [ # # # # : 0 : return log_device_debug_errno(device, r, "Failed to query %s: %m", attr);
# # ]
195 : :
196 : 0 : r = safe_atou(s, type);
197 [ # # ]: 0 : if (r < 0)
198 [ # # # # : 0 : return log_device_warning_errno(device, r, "Failed to parse %s \"%s\": %m", attr, s);
# # ]
199 : :
200 [ # # # # : 0 : log_device_debug(device, "Device has %s=%u", attr, *type);
# # ]
201 : 0 : return 0;
202 : : }
203 : :
204 : 0 : int link_config_load(link_config_ctx *ctx) {
205 : 0 : _cleanup_strv_free_ char **files;
206 : : char **f;
207 : : int r;
208 : :
209 : 0 : link_configs_free(ctx);
210 : :
211 [ # # ]: 0 : if (!enable_name_policy()) {
212 : 0 : ctx->enable_name_policy = false;
213 [ # # ]: 0 : log_info("Network interface NamePolicy= disabled on kernel command line, ignoring.");
214 : : }
215 : :
216 : : /* update timestamp */
217 : 0 : paths_check_timestamp(NETWORK_DIRS, &ctx->network_dirs_ts_usec, true);
218 : :
219 : 0 : r = conf_files_list_strv(&files, ".link", NULL, 0, NETWORK_DIRS);
220 [ # # ]: 0 : if (r < 0)
221 [ # # ]: 0 : return log_error_errno(r, "failed to enumerate link files: %m");
222 : :
223 [ # # # # : 0 : STRV_FOREACH_BACKWARDS(f, files) {
# # ]
224 : 0 : r = link_load_one(ctx, *f);
225 [ # # ]: 0 : if (r < 0)
226 [ # # ]: 0 : log_error_errno(r, "Failed to load %s, ignoring: %m", *f);
227 : : }
228 : :
229 : 0 : return 0;
230 : : }
231 : :
232 : 0 : bool link_config_should_reload(link_config_ctx *ctx) {
233 : 0 : return paths_check_timestamp(NETWORK_DIRS, &ctx->network_dirs_ts_usec, false);
234 : : }
235 : :
236 : 0 : int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) {
237 : : link_config *link;
238 : :
239 [ # # ]: 0 : assert(ctx);
240 [ # # ]: 0 : assert(device);
241 [ # # ]: 0 : assert(ret);
242 : :
243 [ # # ]: 0 : LIST_FOREACH(links, link, ctx->links) {
244 [ # # ]: 0 : if (net_match_config(link->match_mac, link->match_path, link->match_driver,
245 : 0 : link->match_type, link->match_name, link->match_property,
246 : : device, NULL, NULL)) {
247 [ # # # # ]: 0 : if (link->match_name && !strv_contains(link->match_name, "*")) {
248 : 0 : unsigned name_assign_type = NET_NAME_UNKNOWN;
249 : :
250 : 0 : (void) link_unsigned_attribute(device, "name_assign_type", &name_assign_type);
251 : :
252 [ # # ]: 0 : if (name_assign_type == NET_NAME_ENUM) {
253 [ # # # # : 0 : log_device_warning(device, "Config file %s applies to device based on potentially unpredictable interface name",
# # ]
254 : : link->filename);
255 : 0 : *ret = link;
256 : :
257 : 0 : return 0;
258 [ # # ]: 0 : } else if (name_assign_type == NET_NAME_RENAMED) {
259 [ # # # # : 0 : log_device_warning(device, "Config file %s matches device based on renamed interface name, ignoring",
# # ]
260 : : link->filename);
261 : :
262 : 0 : continue;
263 : : }
264 : : }
265 : :
266 [ # # # # : 0 : log_device_debug(device, "Config file %s is applied", link->filename);
# # ]
267 : :
268 : 0 : *ret = link;
269 : 0 : return 0;
270 : : }
271 : : }
272 : :
273 : 0 : *ret = NULL;
274 : 0 : return -ENOENT;
275 : : }
276 : :
277 : 0 : static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr *mac) {
278 : : unsigned addr_type;
279 : 0 : bool want_random = policy == MAC_ADDRESS_POLICY_RANDOM;
280 : : int r;
281 : :
282 [ # # # # ]: 0 : assert(IN_SET(policy, MAC_ADDRESS_POLICY_RANDOM, MAC_ADDRESS_POLICY_PERSISTENT));
283 : :
284 : 0 : r = link_unsigned_attribute(device, "addr_assign_type", &addr_type);
285 [ # # ]: 0 : if (r < 0)
286 : 0 : return r;
287 [ # # # # ]: 0 : switch (addr_type) {
288 : 0 : case NET_ADDR_SET:
289 [ # # # # : 0 : return log_device_debug(device, "MAC on the device already set by userspace");
# # ]
290 : 0 : case NET_ADDR_STOLEN:
291 [ # # # # : 0 : return log_device_debug(device, "MAC on the device already set based on another device");
# # ]
292 : 0 : case NET_ADDR_RANDOM:
293 : : case NET_ADDR_PERM:
294 : 0 : break;
295 : 0 : default:
296 [ # # # # : 0 : return log_device_warning(device, "Unknown addr_assign_type %u, ignoring", addr_type);
# # ]
297 : : }
298 : :
299 [ # # ]: 0 : if (want_random == (addr_type == NET_ADDR_RANDOM))
300 [ # # # # : 0 : return log_device_debug(device, "MAC on the device already matches policy *%s*",
# # ]
301 : : mac_address_policy_to_string(policy));
302 : :
303 [ # # ]: 0 : if (want_random) {
304 [ # # # # : 0 : log_device_debug(device, "Using random bytes to generate MAC");
# # ]
305 : 0 : random_bytes(mac->ether_addr_octet, ETH_ALEN);
306 : : } else {
307 : : uint64_t result;
308 : :
309 : 0 : r = net_get_unique_predictable_data(device,
310 : 0 : naming_scheme_has(NAMING_STABLE_VIRTUAL_MACS),
311 : : &result);
312 [ # # ]: 0 : if (r < 0)
313 [ # # # # : 0 : return log_device_warning_errno(device, r, "Could not generate persistent MAC: %m");
# # ]
314 : :
315 [ # # # # : 0 : log_device_debug(device, "Using generated persistent MAC address");
# # ]
316 : : assert_cc(ETH_ALEN <= sizeof(result));
317 : 0 : memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
318 : : }
319 : :
320 : : /* see eth_random_addr in the kernel */
321 : 0 : mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
322 : 0 : mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
323 : 0 : return 1;
324 : : }
325 : :
326 : 0 : int link_config_apply(link_config_ctx *ctx, link_config *config,
327 : : sd_device *device, const char **name) {
328 : : struct ether_addr generated_mac;
329 : 0 : struct ether_addr *mac = NULL;
330 : 0 : const char *new_name = NULL;
331 : : const char *old_name;
332 : 0 : unsigned speed, name_type = NET_NAME_UNKNOWN;
333 : : NamePolicy policy;
334 : : int r, ifindex;
335 : :
336 [ # # ]: 0 : assert(ctx);
337 [ # # ]: 0 : assert(config);
338 [ # # ]: 0 : assert(device);
339 [ # # ]: 0 : assert(name);
340 : :
341 : 0 : r = sd_device_get_sysname(device, &old_name);
342 [ # # ]: 0 : if (r < 0)
343 : 0 : return r;
344 : :
345 : 0 : r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name,
346 : 0 : config->autonegotiation, config->advertise,
347 : : config->speed, config->duplex, config->port);
348 [ # # ]: 0 : if (r < 0) {
349 : :
350 [ # # ]: 0 : if (config->port != _NET_DEV_PORT_INVALID)
351 [ # # ]: 0 : log_warning_errno(r, "Could not set port (%s) of %s: %m", port_to_string(config->port), old_name);
352 : :
353 [ # # ]: 0 : if (!eqzero(config->advertise))
354 [ # # ]: 0 : log_warning_errno(r, "Could not set advertise mode: %m"); /* TODO: include modes in the log message. */
355 : :
356 [ # # ]: 0 : if (config->speed) {
357 : 0 : speed = DIV_ROUND_UP(config->speed, 1000000);
358 [ # # ]: 0 : if (r == -EOPNOTSUPP) {
359 : 0 : r = ethtool_set_speed(&ctx->ethtool_fd, old_name, speed, config->duplex);
360 [ # # ]: 0 : if (r < 0)
361 [ # # ]: 0 : log_warning_errno(r, "Could not set speed of %s to %u Mbps: %m", old_name, speed);
362 : : }
363 : : }
364 : :
365 [ # # ]: 0 : if (config->duplex !=_DUP_INVALID)
366 [ # # ]: 0 : log_warning_errno(r, "Could not set duplex of %s to (%s): %m", old_name, duplex_to_string(config->duplex));
367 : : }
368 : :
369 : 0 : r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol);
370 [ # # ]: 0 : if (r < 0)
371 [ # # ]: 0 : log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m",
372 : : old_name, wol_to_string(config->wol));
373 : :
374 : 0 : r = ethtool_set_features(&ctx->ethtool_fd, old_name, config->features);
375 [ # # ]: 0 : if (r < 0)
376 [ # # ]: 0 : log_warning_errno(r, "Could not set offload features of %s: %m", old_name);
377 : :
378 [ # # # # : 0 : if (config->channels.rx_count_set || config->channels.tx_count_set || config->channels.other_count_set || config->channels.combined_count_set) {
# # # # ]
379 : 0 : r = ethtool_set_channels(&ctx->ethtool_fd, old_name, &config->channels);
380 [ # # ]: 0 : if (r < 0)
381 [ # # ]: 0 : log_warning_errno(r, "Could not set channels of %s: %m", old_name);
382 : : }
383 : :
384 : 0 : r = sd_device_get_ifindex(device, &ifindex);
385 [ # # ]: 0 : if (r < 0)
386 [ # # # # : 0 : return log_device_warning_errno(device, r, "Could not find ifindex: %m");
# # ]
387 : :
388 : 0 : (void) link_unsigned_attribute(device, "name_assign_type", &name_type);
389 : :
390 [ # # # # ]: 0 : if (IN_SET(name_type, NET_NAME_USER, NET_NAME_RENAMED)
391 [ # # ]: 0 : && !naming_scheme_has(NAMING_ALLOW_RERENAMES)) {
392 [ # # # # : 0 : log_device_debug(device, "Device already has a name given by userspace, not renaming.");
# # ]
393 : 0 : goto no_rename;
394 : : }
395 : :
396 [ # # # # ]: 0 : if (ctx->enable_name_policy && config->name_policy)
397 [ # # # # ]: 0 : for (NamePolicy *p = config->name_policy; !new_name && *p != _NAMEPOLICY_INVALID; p++) {
398 : 0 : policy = *p;
399 : :
400 [ # # # # : 0 : switch (policy) {
# # # # ]
401 : 0 : case NAMEPOLICY_KERNEL:
402 [ # # ]: 0 : if (name_type != NET_NAME_PREDICTABLE)
403 : 0 : continue;
404 : :
405 : : /* The kernel claims to have given a predictable name, keep it. */
406 [ # # # # : 0 : log_device_debug(device, "Policy *%s*: keeping predictable kernel name",
# # ]
407 : : name_policy_to_string(policy));
408 : 0 : goto no_rename;
409 : 0 : case NAMEPOLICY_KEEP:
410 [ # # # # ]: 0 : if (!IN_SET(name_type, NET_NAME_USER, NET_NAME_RENAMED))
411 : 0 : continue;
412 : :
413 [ # # # # : 0 : log_device_debug(device, "Policy *%s*: keeping existing userspace name",
# # ]
414 : : name_policy_to_string(policy));
415 : 0 : goto no_rename;
416 : 0 : case NAMEPOLICY_DATABASE:
417 : 0 : (void) sd_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE", &new_name);
418 : 0 : break;
419 : 0 : case NAMEPOLICY_ONBOARD:
420 : 0 : (void) sd_device_get_property_value(device, "ID_NET_NAME_ONBOARD", &new_name);
421 : 0 : break;
422 : 0 : case NAMEPOLICY_SLOT:
423 : 0 : (void) sd_device_get_property_value(device, "ID_NET_NAME_SLOT", &new_name);
424 : 0 : break;
425 : 0 : case NAMEPOLICY_PATH:
426 : 0 : (void) sd_device_get_property_value(device, "ID_NET_NAME_PATH", &new_name);
427 : 0 : break;
428 : 0 : case NAMEPOLICY_MAC:
429 : 0 : (void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &new_name);
430 : 0 : break;
431 : 0 : default:
432 : 0 : assert_not_reached("invalid policy");
433 : : }
434 : : }
435 : :
436 [ # # ]: 0 : if (new_name)
437 [ # # # # : 0 : log_device_debug(device, "Policy *%s* yields \"%s\".", name_policy_to_string(policy), new_name);
# # ]
438 [ # # ]: 0 : else if (config->name) {
439 : 0 : new_name = config->name;
440 [ # # # # : 0 : log_device_debug(device, "Policies didn't yield a name, using specified Name=%s.", new_name);
# # ]
441 : : } else
442 [ # # # # : 0 : log_device_debug(device, "Policies didn't yield a name and Name= is not given, not renaming.");
# # ]
443 : 0 : no_rename:
444 : :
445 [ # # # # ]: 0 : if (IN_SET(config->mac_address_policy, MAC_ADDRESS_POLICY_PERSISTENT, MAC_ADDRESS_POLICY_RANDOM)) {
446 [ # # ]: 0 : if (get_mac(device, config->mac_address_policy, &generated_mac) > 0)
447 : 0 : mac = &generated_mac;
448 : : } else
449 : 0 : mac = config->mac;
450 : :
451 : 0 : r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu);
452 [ # # ]: 0 : if (r < 0)
453 [ # # ]: 0 : return log_warning_errno(r, "Could not set Alias=, MACAddress= or MTU= on %s: %m", old_name);
454 : :
455 : 0 : *name = new_name;
456 : :
457 : 0 : return 0;
458 : : }
459 : :
460 : 0 : int link_get_driver(link_config_ctx *ctx, sd_device *device, char **ret) {
461 : : const char *name;
462 : 0 : char *driver = NULL;
463 : : int r;
464 : :
465 : 0 : r = sd_device_get_sysname(device, &name);
466 [ # # ]: 0 : if (r < 0)
467 : 0 : return r;
468 : :
469 : 0 : r = ethtool_get_driver(&ctx->ethtool_fd, name, &driver);
470 [ # # ]: 0 : if (r < 0)
471 : 0 : return r;
472 : :
473 : 0 : *ret = driver;
474 : 0 : return 0;
475 : : }
476 : :
477 : : static const char* const mac_address_policy_table[_MAC_ADDRESS_POLICY_MAX] = {
478 : : [MAC_ADDRESS_POLICY_PERSISTENT] = "persistent",
479 : : [MAC_ADDRESS_POLICY_RANDOM] = "random",
480 : : [MAC_ADDRESS_POLICY_NONE] = "none",
481 : : };
482 : :
483 [ + + + + ]: 40 : DEFINE_STRING_TABLE_LOOKUP(mac_address_policy, MACAddressPolicy);
484 [ # # # # : 0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_address_policy, mac_address_policy, MACAddressPolicy,
# # # # #
# # # ]
485 : : "Failed to parse MAC address policy");
486 : :
487 : : static const char* const name_policy_table[_NAMEPOLICY_MAX] = {
488 : : [NAMEPOLICY_KERNEL] = "kernel",
489 : : [NAMEPOLICY_KEEP] = "keep",
490 : : [NAMEPOLICY_DATABASE] = "database",
491 : : [NAMEPOLICY_ONBOARD] = "onboard",
492 : : [NAMEPOLICY_SLOT] = "slot",
493 : : [NAMEPOLICY_PATH] = "path",
494 : : [NAMEPOLICY_MAC] = "mac",
495 : : };
496 : :
497 [ + + + + ]: 72 : DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
498 [ # # # # : 0 : DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy,
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
499 : : _NAMEPOLICY_INVALID,
500 : : "Failed to parse interface name policy");
|