]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/net/link-config.c
treewide: yet more log_*_errno + return simplifications
[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);
f647962d
MS
205 if (r < 0)
206 return log_error_errno(r, "failed to enumerate link files: %m");
af6f0d42
TG
207
208 STRV_FOREACH_BACKWARDS(f, files) {
209 r = load_link(ctx, *f);
210 if (r < 0)
211 return r;
212 }
213
214 return 0;
215}
216
217bool link_config_should_reload(link_config_ctx *ctx) {
2ad8416d 218 return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false);
af6f0d42
TG
219}
220
464cf22f
TG
221int link_config_get(link_config_ctx *ctx, struct udev_device *device,
222 link_config **ret) {
af6f0d42
TG
223 link_config *link;
224
225 LIST_FOREACH(links, link, ctx->links) {
eb7040ec 226 const char* attr_value = udev_device_get_sysattr_value(device, "address");
b3e01314 227
edbb03e9
TG
228 if (net_match_config(link->match_mac, link->match_path, link->match_driver,
229 link->match_type, NULL, link->match_host,
230 link->match_virt, link->match_kernel, link->match_arch,
eb7040ec 231 attr_value ? ether_aton(attr_value) : NULL,
b3e01314 232 udev_device_get_property_value(device, "ID_PATH"),
9b1c2626 233 udev_device_get_driver(udev_device_get_parent(device)),
bf175aaf 234 udev_device_get_property_value(device, "ID_NET_DRIVER"),
b3e01314
TG
235 udev_device_get_devtype(device),
236 NULL)) {
be32eb9b
TG
237 log_debug("Config file %s applies to device %s",
238 link->filename,
239 udev_device_get_sysname(device));
af6f0d42
TG
240 *ret = link;
241 return 0;
242 }
243 }
244
be32eb9b
TG
245 *ret = NULL;
246
af6f0d42
TG
247 return -ENOENT;
248}
249
16b9b87a
TG
250static bool mac_is_random(struct udev_device *device) {
251 const char *s;
f1ac7002
TG
252 unsigned type;
253 int r;
16b9b87a 254
3c9b8860 255 /* if we can't get the assign type, assume it is not random */
16b9b87a
TG
256 s = udev_device_get_sysattr_value(device, "addr_assign_type");
257 if (!s)
3c9b8860
TG
258 return false;
259
f1ac7002
TG
260 r = safe_atou(s, &type);
261 if (r < 0)
262 return false;
16b9b87a 263
04b67d49
TG
264 return type == NET_ADDR_RANDOM;
265}
266
267static bool should_rename(struct udev_device *device, bool respect_predictable) {
268 const char *s;
269 unsigned type;
270 int r;
271
3c9b8860 272 /* if we can't get the assgin type, assume we should rename */
04b67d49
TG
273 s = udev_device_get_sysattr_value(device, "name_assign_type");
274 if (!s)
3c9b8860
TG
275 return true;
276
04b67d49
TG
277 r = safe_atou(s, &type);
278 if (r < 0)
279 return true;
280
281 switch (type) {
282 case NET_NAME_USER:
283 case NET_NAME_RENAMED:
3c9b8860
TG
284 /* these were already named by userspace, do not touch again */
285 return false;
04b67d49 286 case NET_NAME_PREDICTABLE:
3c9b8860 287 /* the kernel claims to have given a predictable name */
04b67d49 288 if (respect_predictable)
3c9b8860 289 return false;
04b67d49
TG
290 /* fall through */
291 case NET_NAME_ENUM:
292 default:
3c9b8860
TG
293 /* the name is known to be bad, or of an unknown type */
294 return true;
04b67d49 295 }
16b9b87a
TG
296}
297
464cf22f
TG
298static int get_mac(struct udev_device *device, bool want_random,
299 struct ether_addr *mac) {
9bf3b535 300 int r;
16b9b87a 301
16b9b87a 302 if (want_random)
9bf3b535 303 random_bytes(mac->ether_addr_octet, ETH_ALEN);
16b9b87a 304 else {
9bf3b535 305 uint8_t result[8];
9bf3b535 306
b5db00e5 307 r = net_get_unique_predictable_data(device, result);
16b9b87a 308 if (r < 0)
55428d84 309 return r;
16b9b87a 310
9bf3b535
LP
311 assert_cc(ETH_ALEN <= sizeof(result));
312 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
16b9b87a
TG
313 }
314
315 /* see eth_random_addr in the kernel */
3c9b8860
TG
316 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
317 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
16b9b87a 318
16b9b87a
TG
319 return 0;
320}
321
464cf22f
TG
322int link_config_apply(link_config_ctx *ctx, link_config *config,
323 struct udev_device *device, const char **name) {
3e137a1b 324 const char *old_name;
5fde13d7
TG
325 const char *new_name = NULL;
326 struct ether_addr generated_mac;
16b9b87a 327 struct ether_addr *mac = NULL;
04b67d49 328 bool respect_predictable = false;
43b3a5ef 329 int r, ifindex;
af6f0d42 330
3e137a1b
TG
331 assert(ctx);
332 assert(config);
333 assert(device);
334 assert(name);
335
3e137a1b
TG
336 old_name = udev_device_get_sysname(device);
337 if (!old_name)
af6f0d42
TG
338 return -EINVAL;
339
aedca892 340 r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024,
464cf22f 341 config->duplex);
5fde13d7 342 if (r < 0)
755bde37
LP
343 log_warning_errno(r, "Could not set speed or duplex of %s to %u Mbps (%s): %m",
344 old_name, config->speed / 1024,
345 duplex_to_string(config->duplex));
a5010333 346
aedca892 347 r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol);
5fde13d7 348 if (r < 0)
755bde37
LP
349 log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m",
350 old_name, wol_to_string(config->wol));
af6f0d42 351
43b3a5ef
TG
352 ifindex = udev_device_get_ifindex(device);
353 if (ifindex <= 0) {
354 log_warning("Could not find ifindex");
355 return -ENODEV;
356 }
357
f6194225 358 if (ctx->enable_name_policy && config->name_policy) {
5fde13d7 359 NamePolicy *policy;
daeb71a3 360
68ba3877
TG
361 for (policy = config->name_policy;
362 !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
5fde13d7 363 switch (*policy) {
04b67d49
TG
364 case NAMEPOLICY_KERNEL:
365 respect_predictable = true;
366 break;
e51660ae
TG
367 case NAMEPOLICY_DATABASE:
368 new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE");
369 break;
5fde13d7
TG
370 case NAMEPOLICY_ONBOARD:
371 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
daeb71a3 372 break;
5fde13d7
TG
373 case NAMEPOLICY_SLOT:
374 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
daeb71a3 375 break;
5fde13d7
TG
376 case NAMEPOLICY_PATH:
377 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
daeb71a3 378 break;
5fde13d7
TG
379 case NAMEPOLICY_MAC:
380 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
daeb71a3 381 break;
5fde13d7
TG
382 default:
383 break;
384 }
daeb71a3
TG
385 }
386 }
387
04b67d49 388 if (should_rename(device, respect_predictable)) {
3c9b8860 389 /* if not set by policy, fall back manually set name */
04b67d49 390 if (!new_name)
04b67d49
TG
391 new_name = config->name;
392 } else
393 new_name = NULL;
394
5fde13d7
TG
395 switch (config->mac_policy) {
396 case MACPOLICY_PERSISTENT:
92d927f8 397 if (mac_is_random(device)) {
5fde13d7 398 r = get_mac(device, false, &generated_mac);
a669ea98
TG
399 if (r == -ENOENT)
400 break;
401 else if (r < 0)
16b9b87a 402 return r;
5fde13d7 403 mac = &generated_mac;
16b9b87a 404 }
5fde13d7
TG
405 break;
406 case MACPOLICY_RANDOM:
16b9b87a 407 if (!mac_is_random(device)) {
5fde13d7 408 r = get_mac(device, true, &generated_mac);
a669ea98
TG
409 if (r == -ENOENT)
410 break;
411 else if (r < 0)
16b9b87a 412 return r;
5fde13d7 413 mac = &generated_mac;
16b9b87a 414 }
5fde13d7
TG
415 break;
416 default:
417 mac = config->mac;
16b9b87a
TG
418 }
419
aedca892 420 r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac,
464cf22f 421 config->mtu);
f647962d
MS
422 if (r < 0)
423 return log_warning_errno(r, "Could not set Alias, MACAddress or MTU on %s: %m", old_name);
43b3a5ef 424
d95b83b8
TG
425 *name = new_name;
426
af6f0d42
TG
427 return 0;
428}
be32eb9b 429
847a8a5f
TG
430int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) {
431 const char *name;
432 char *driver;
433 int r;
434
847a8a5f
TG
435 name = udev_device_get_sysname(device);
436 if (!name)
437 return -EINVAL;
438
aedca892 439 r = ethtool_get_driver(&ctx->ethtool_fd, name, &driver);
847a8a5f
TG
440 if (r < 0)
441 return r;
442
443 *ret = driver;
444 return 0;
445}
446
2c5859af 447static const char* const mac_policy_table[_MACPOLICY_MAX] = {
be32eb9b
TG
448 [MACPOLICY_PERSISTENT] = "persistent",
449 [MACPOLICY_RANDOM] = "random"
450};
451
452DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy);
464cf22f
TG
453DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy,
454 "Failed to parse MAC address policy");
be32eb9b 455
2c5859af 456static const char* const name_policy_table[_NAMEPOLICY_MAX] = {
04b67d49 457 [NAMEPOLICY_KERNEL] = "kernel",
e51660ae 458 [NAMEPOLICY_DATABASE] = "database",
be32eb9b
TG
459 [NAMEPOLICY_ONBOARD] = "onboard",
460 [NAMEPOLICY_SLOT] = "slot",
461 [NAMEPOLICY_PATH] = "path",
462 [NAMEPOLICY_MAC] = "mac"
463};
464
465DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
464cf22f
TG
466DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy,
467 _NAMEPOLICY_INVALID,
468 "Failed to parse interface name policy");