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