]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/net/link-config.c
sd-bus: make sure %m resolves to the specified error in bus_error_set_errnofv()
[thirdparty/systemd.git] / src / udev / net / link-config.c
CommitLineData
af6f0d42
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
43b3a5ef
TG
22#include <netinet/ether.h>
23
16b9b87a 24#include "sd-id128.h"
af6f0d42 25
04b67d49 26#include "missing.h"
16b9b87a 27#include "link-config.h"
a5010333
TG
28#include "ethtool-util.h"
29
43b3a5ef
TG
30#include "libudev-private.h"
31#include "sd-rtnl.h"
af6f0d42
TG
32#include "util.h"
33#include "log.h"
34#include "strv.h"
35#include "path-util.h"
36#include "conf-parser.h"
37#include "conf-files.h"
daeb71a3 38#include "fileio.h"
16b9b87a 39#include "hashmap.h"
3aeb37bc 40#include "rtnl-util.h"
c6f7c917 41#include "network-internal.h"
9bf3b535 42#include "siphash24.h"
af6f0d42
TG
43
44struct link_config_ctx {
45 LIST_HEAD(link_config, links);
46
a5010333
TG
47 int ethtool_fd;
48
f6194225
TG
49 bool enable_name_policy;
50
43b3a5ef
TG
51 sd_rtnl *rtnl;
52
97f2d76d 53 usec_t link_dirs_ts_usec;
af6f0d42
TG
54};
55
2ad8416d
ZJS
56static const char* const link_dirs[] = {
57 "/etc/systemd/network",
58 "/run/systemd/network",
59 "/usr/lib/systemd/network",
60#ifdef HAVE_SPLIT_USR
61 "/lib/systemd/network",
62#endif
63 NULL};
64
5b9d4dc0
TG
65DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free);
66#define _cleanup_link_config_ctx_free_ _cleanup_(link_config_ctx_freep)
67
af6f0d42 68int link_config_ctx_new(link_config_ctx **ret) {
5b9d4dc0 69 _cleanup_link_config_ctx_free_ link_config_ctx *ctx = NULL;
af6f0d42
TG
70
71 if (!ret)
72 return -EINVAL;
73
74 ctx = new0(link_config_ctx, 1);
75 if (!ctx)
76 return -ENOMEM;
77
78 LIST_HEAD_INIT(ctx->links);
79
97708579
TG
80 ctx->ethtool_fd = -1;
81
f6194225
TG
82 ctx->enable_name_policy = true;
83
af6f0d42 84 *ret = ctx;
5b9d4dc0
TG
85 ctx = NULL;
86
af6f0d42
TG
87 return 0;
88}
89
90static void link_configs_free(link_config_ctx *ctx) {
91 link_config *link, *link_next;
92
93 if (!ctx)
94 return;
95
96 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
97 free(link->filename);
98 free(link->match_path);
99 free(link->match_driver);
100 free(link->match_type);
101 free(link->description);
d2df0d0e 102 free(link->alias);
edf029b7 103 free(link->name_policy);
af6f0d42
TG
104
105 free(link);
106 }
107}
108
109void link_config_ctx_free(link_config_ctx *ctx) {
110 if (!ctx)
111 return;
112
03e334a1 113 safe_close(ctx->ethtool_fd);
43b3a5ef
TG
114
115 sd_rtnl_unref(ctx->rtnl);
116
af6f0d42
TG
117 link_configs_free(ctx);
118
119 free(ctx);
120
121 return;
122}
123
124static int load_link(link_config_ctx *ctx, const char *filename) {
ecb08ec6 125 _cleanup_free_ link_config *link = NULL;
6e37cd2f 126 _cleanup_fclose_ FILE *file = NULL;
af6f0d42
TG
127 int r;
128
187dc6e5
TA
129 assert(ctx);
130 assert(filename);
131
af6f0d42
TG
132 file = fopen(filename, "re");
133 if (!file) {
134 if (errno == ENOENT)
135 return 0;
136 else
ecb08ec6 137 return -errno;
af6f0d42
TG
138 }
139
ed88bcfb
ZJS
140 if (null_or_empty_fd(fileno(file))) {
141 log_debug("Skipping empty file: %s", filename);
142 return 0;
143 }
144
af6f0d42 145 link = new0(link_config, 1);
ecb08ec6
ZJS
146 if (!link)
147 return log_oom();
af6f0d42 148
5fde13d7
TG
149 link->mac_policy = _MACPOLICY_INVALID;
150 link->wol = _WOL_INVALID;
151 link->duplex = _DUP_INVALID;
152
e9f3d2d5
ZJS
153 r = config_parse(NULL, filename, file,
154 "Match\0Link\0Ethernet\0",
155 config_item_perf_lookup, link_config_gperf_lookup,
36f822c4
ZJS
156 false, false, true, link);
157 if (r < 0)
ecb08ec6 158 return r;
36f822c4 159 else
98a375f6 160 log_debug("Parsed configuration file %s", filename);
af6f0d42
TG
161
162 link->filename = strdup(filename);
163
164 LIST_PREPEND(links, ctx->links, link);
ecb08ec6 165 link = NULL;
af6f0d42
TG
166
167 return 0;
af6f0d42
TG
168}
169
f6194225 170static bool enable_name_policy(void) {
f8a0bb52 171 _cleanup_free_ char *line = NULL;
a2a5291b 172 const char *word, *state;
f6194225
TG
173 int r;
174 size_t l;
175
74df0fca 176 r = proc_cmdline(&line);
b5884878 177 if (r < 0) {
da927ba9 178 log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m");
74df0fca 179 return true;
b5884878 180 }
f6194225 181
a2a5291b
ZJS
182 FOREACH_WORD_QUOTED(word, l, line, state)
183 if (strneq(word, "net.ifnames=0", l))
f6194225
TG
184 return false;
185
186 return true;
187}
188
af6f0d42
TG
189int link_config_load(link_config_ctx *ctx) {
190 int r;
edf029b7
TG
191 _cleanup_strv_free_ char **files;
192 char **f;
af6f0d42
TG
193
194 link_configs_free(ctx);
195
f6194225
TG
196 if (!enable_name_policy()) {
197 ctx->enable_name_policy = false;
3f85ef0f 198 log_info("Network interface NamePolicy= disabled on kernel command line, ignoring.");
f6194225
TG
199 }
200
97f2d76d 201 /* update timestamp */
2ad8416d 202 paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true);
af6f0d42 203
2ad8416d 204 r = conf_files_list_strv(&files, ".link", NULL, link_dirs);
af6f0d42 205 if (r < 0) {
da927ba9 206 log_error_errno(r, "failed to enumerate link files: %m");
af6f0d42
TG
207 return r;
208 }
209
210 STRV_FOREACH_BACKWARDS(f, files) {
211 r = load_link(ctx, *f);
212 if (r < 0)
213 return r;
214 }
215
216 return 0;
217}
218
219bool link_config_should_reload(link_config_ctx *ctx) {
2ad8416d 220 return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false);
af6f0d42
TG
221}
222
464cf22f
TG
223int link_config_get(link_config_ctx *ctx, struct udev_device *device,
224 link_config **ret) {
af6f0d42
TG
225 link_config *link;
226
227 LIST_FOREACH(links, link, ctx->links) {
eb7040ec 228 const char* attr_value = udev_device_get_sysattr_value(device, "address");
b3e01314 229
edbb03e9
TG
230 if (net_match_config(link->match_mac, link->match_path, link->match_driver,
231 link->match_type, NULL, link->match_host,
232 link->match_virt, link->match_kernel, link->match_arch,
eb7040ec 233 attr_value ? ether_aton(attr_value) : NULL,
b3e01314 234 udev_device_get_property_value(device, "ID_PATH"),
9b1c2626 235 udev_device_get_driver(udev_device_get_parent(device)),
bf175aaf 236 udev_device_get_property_value(device, "ID_NET_DRIVER"),
b3e01314
TG
237 udev_device_get_devtype(device),
238 NULL)) {
be32eb9b
TG
239 log_debug("Config file %s applies to device %s",
240 link->filename,
241 udev_device_get_sysname(device));
af6f0d42
TG
242 *ret = link;
243 return 0;
244 }
245 }
246
be32eb9b
TG
247 *ret = NULL;
248
af6f0d42
TG
249 return -ENOENT;
250}
251
16b9b87a
TG
252static bool mac_is_random(struct udev_device *device) {
253 const char *s;
f1ac7002
TG
254 unsigned type;
255 int r;
16b9b87a 256
3c9b8860 257 /* if we can't get the assign type, assume it is not random */
16b9b87a
TG
258 s = udev_device_get_sysattr_value(device, "addr_assign_type");
259 if (!s)
3c9b8860
TG
260 return false;
261
f1ac7002
TG
262 r = safe_atou(s, &type);
263 if (r < 0)
264 return false;
16b9b87a 265
04b67d49
TG
266 return type == NET_ADDR_RANDOM;
267}
268
269static bool should_rename(struct udev_device *device, bool respect_predictable) {
270 const char *s;
271 unsigned type;
272 int r;
273
3c9b8860 274 /* if we can't get the assgin type, assume we should rename */
04b67d49
TG
275 s = udev_device_get_sysattr_value(device, "name_assign_type");
276 if (!s)
3c9b8860
TG
277 return true;
278
04b67d49
TG
279 r = safe_atou(s, &type);
280 if (r < 0)
281 return true;
282
283 switch (type) {
284 case NET_NAME_USER:
285 case NET_NAME_RENAMED:
3c9b8860
TG
286 /* these were already named by userspace, do not touch again */
287 return false;
04b67d49 288 case NET_NAME_PREDICTABLE:
3c9b8860 289 /* the kernel claims to have given a predictable name */
04b67d49 290 if (respect_predictable)
3c9b8860 291 return false;
04b67d49
TG
292 /* fall through */
293 case NET_NAME_ENUM:
294 default:
3c9b8860
TG
295 /* the name is known to be bad, or of an unknown type */
296 return true;
04b67d49 297 }
16b9b87a
TG
298}
299
464cf22f
TG
300static int get_mac(struct udev_device *device, bool want_random,
301 struct ether_addr *mac) {
9bf3b535 302 int r;
16b9b87a 303
16b9b87a 304 if (want_random)
9bf3b535 305 random_bytes(mac->ether_addr_octet, ETH_ALEN);
16b9b87a 306 else {
9bf3b535 307 uint8_t result[8];
9bf3b535 308
b5db00e5 309 r = net_get_unique_predictable_data(device, result);
16b9b87a 310 if (r < 0)
55428d84 311 return r;
16b9b87a 312
9bf3b535
LP
313 assert_cc(ETH_ALEN <= sizeof(result));
314 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
16b9b87a
TG
315 }
316
317 /* see eth_random_addr in the kernel */
3c9b8860
TG
318 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
319 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
16b9b87a 320
16b9b87a
TG
321 return 0;
322}
323
464cf22f
TG
324int link_config_apply(link_config_ctx *ctx, link_config *config,
325 struct udev_device *device, const char **name) {
3e137a1b 326 const char *old_name;
5fde13d7
TG
327 const char *new_name = NULL;
328 struct ether_addr generated_mac;
16b9b87a 329 struct ether_addr *mac = NULL;
04b67d49 330 bool respect_predictable = false;
43b3a5ef 331 int r, ifindex;
af6f0d42 332
3e137a1b
TG
333 assert(ctx);
334 assert(config);
335 assert(device);
336 assert(name);
337
3e137a1b
TG
338 old_name = udev_device_get_sysname(device);
339 if (!old_name)
af6f0d42
TG
340 return -EINVAL;
341
aedca892 342 r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024,
464cf22f 343 config->duplex);
5fde13d7 344 if (r < 0)
733f7a2c 345 log_warning("Could not set speed or duplex of %s to %u Mbps (%s): %s",
464cf22f
TG
346 old_name, config->speed / 1024,
347 duplex_to_string(config->duplex), strerror(-r));
a5010333 348
aedca892 349 r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol);
5fde13d7
TG
350 if (r < 0)
351 log_warning("Could not set WakeOnLan of %s to %s: %s",
3e137a1b 352 old_name, wol_to_string(config->wol), strerror(-r));
af6f0d42 353
43b3a5ef
TG
354 ifindex = udev_device_get_ifindex(device);
355 if (ifindex <= 0) {
356 log_warning("Could not find ifindex");
357 return -ENODEV;
358 }
359
f6194225 360 if (ctx->enable_name_policy && config->name_policy) {
5fde13d7 361 NamePolicy *policy;
daeb71a3 362
68ba3877
TG
363 for (policy = config->name_policy;
364 !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
5fde13d7 365 switch (*policy) {
04b67d49
TG
366 case NAMEPOLICY_KERNEL:
367 respect_predictable = true;
368 break;
e51660ae
TG
369 case NAMEPOLICY_DATABASE:
370 new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE");
371 break;
5fde13d7
TG
372 case NAMEPOLICY_ONBOARD:
373 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
daeb71a3 374 break;
5fde13d7
TG
375 case NAMEPOLICY_SLOT:
376 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
daeb71a3 377 break;
5fde13d7
TG
378 case NAMEPOLICY_PATH:
379 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
daeb71a3 380 break;
5fde13d7
TG
381 case NAMEPOLICY_MAC:
382 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
daeb71a3 383 break;
5fde13d7
TG
384 default:
385 break;
386 }
daeb71a3
TG
387 }
388 }
389
04b67d49 390 if (should_rename(device, respect_predictable)) {
3c9b8860 391 /* if not set by policy, fall back manually set name */
04b67d49 392 if (!new_name)
04b67d49
TG
393 new_name = config->name;
394 } else
395 new_name = NULL;
396
5fde13d7
TG
397 switch (config->mac_policy) {
398 case MACPOLICY_PERSISTENT:
92d927f8 399 if (mac_is_random(device)) {
5fde13d7 400 r = get_mac(device, false, &generated_mac);
a669ea98
TG
401 if (r == -ENOENT)
402 break;
403 else if (r < 0)
16b9b87a 404 return r;
5fde13d7 405 mac = &generated_mac;
16b9b87a 406 }
5fde13d7
TG
407 break;
408 case MACPOLICY_RANDOM:
16b9b87a 409 if (!mac_is_random(device)) {
5fde13d7 410 r = get_mac(device, true, &generated_mac);
a669ea98
TG
411 if (r == -ENOENT)
412 break;
413 else if (r < 0)
16b9b87a 414 return r;
5fde13d7 415 mac = &generated_mac;
16b9b87a 416 }
5fde13d7
TG
417 break;
418 default:
419 mac = config->mac;
16b9b87a
TG
420 }
421
aedca892 422 r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac,
464cf22f 423 config->mtu);
43b3a5ef 424 if (r < 0) {
464cf22f
TG
425 log_warning("Could not set Alias, MACAddress or MTU on %s: %s",
426 old_name, strerror(-r));
5fde13d7 427 return r;
43b3a5ef
TG
428 }
429
d95b83b8
TG
430 *name = new_name;
431
af6f0d42
TG
432 return 0;
433}
be32eb9b 434
847a8a5f
TG
435int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) {
436 const char *name;
437 char *driver;
438 int r;
439
847a8a5f
TG
440 name = udev_device_get_sysname(device);
441 if (!name)
442 return -EINVAL;
443
aedca892 444 r = ethtool_get_driver(&ctx->ethtool_fd, name, &driver);
847a8a5f
TG
445 if (r < 0)
446 return r;
447
448 *ret = driver;
449 return 0;
450}
451
2c5859af 452static const char* const mac_policy_table[_MACPOLICY_MAX] = {
be32eb9b
TG
453 [MACPOLICY_PERSISTENT] = "persistent",
454 [MACPOLICY_RANDOM] = "random"
455};
456
457DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy);
464cf22f
TG
458DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy,
459 "Failed to parse MAC address policy");
be32eb9b 460
2c5859af 461static const char* const name_policy_table[_NAMEPOLICY_MAX] = {
04b67d49 462 [NAMEPOLICY_KERNEL] = "kernel",
e51660ae 463 [NAMEPOLICY_DATABASE] = "database",
be32eb9b
TG
464 [NAMEPOLICY_ONBOARD] = "onboard",
465 [NAMEPOLICY_SLOT] = "slot",
466 [NAMEPOLICY_PATH] = "path",
467 [NAMEPOLICY_MAC] = "mac"
468};
469
470DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
464cf22f
TG
471DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy,
472 _NAMEPOLICY_INVALID,
473 "Failed to parse interface name policy");