]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/net/link-config.c
Improve messages about user mode a bit
[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>
2a73e0d3 23#include <net/if.h>
43b3a5ef 24
16b9b87a 25#include "sd-id128.h"
af6f0d42 26
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"
be32eb9b 41#include "net-util.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
af6f0d42 53 char **link_dirs;
97f2d76d 54 usec_t link_dirs_ts_usec;
af6f0d42
TG
55};
56
5b9d4dc0
TG
57DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free);
58#define _cleanup_link_config_ctx_free_ _cleanup_(link_config_ctx_freep)
59
af6f0d42 60int link_config_ctx_new(link_config_ctx **ret) {
5b9d4dc0 61 _cleanup_link_config_ctx_free_ link_config_ctx *ctx = NULL;
af6f0d42
TG
62
63 if (!ret)
64 return -EINVAL;
65
66 ctx = new0(link_config_ctx, 1);
67 if (!ctx)
68 return -ENOMEM;
69
70 LIST_HEAD_INIT(ctx->links);
71
97708579
TG
72 ctx->ethtool_fd = -1;
73
f6194225
TG
74 ctx->enable_name_policy = true;
75
9dc670ea
TG
76 ctx->link_dirs = strv_new("/etc/systemd/network",
77 "/run/systemd/network",
78 "/usr/lib/systemd/network",
c50e28a3
DR
79#ifdef HAVE_SPLIT_USR
80 "/lib/systemd/network",
81#endif
af6f0d42
TG
82 NULL);
83 if (!ctx->link_dirs) {
84 log_error("failed to build link config directory array");
af6f0d42
TG
85 return -ENOMEM;
86 }
5b9d4dc0 87
af6f0d42 88 if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
9f6445e3 89 log_error("failed to canonicalize link config directories");
af6f0d42
TG
90 return -ENOMEM;
91 }
92
af6f0d42 93 *ret = ctx;
5b9d4dc0
TG
94 ctx = NULL;
95
af6f0d42
TG
96 return 0;
97}
98
97708579
TG
99static int link_config_ctx_connect(link_config_ctx *ctx) {
100 int r;
101
102 if (ctx->ethtool_fd >= 0 && ctx->rtnl)
103 return 0;
104
105 r = ethtool_connect(&ctx->ethtool_fd);
106 if (r < 0)
107 return r;
108
109 r = sd_rtnl_open(0, &ctx->rtnl);
110 if (r < 0)
111 return r;
112
113 return 0;
114}
115
af6f0d42
TG
116static void link_configs_free(link_config_ctx *ctx) {
117 link_config *link, *link_next;
118
119 if (!ctx)
120 return;
121
122 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
123 free(link->filename);
124 free(link->match_path);
125 free(link->match_driver);
126 free(link->match_type);
127 free(link->description);
d2df0d0e 128 free(link->alias);
af6f0d42
TG
129
130 free(link);
131 }
132}
133
134void link_config_ctx_free(link_config_ctx *ctx) {
135 if (!ctx)
136 return;
137
43b3a5ef
TG
138 if (ctx->ethtool_fd >= 0)
139 close_nointr_nofail(ctx->ethtool_fd);
140
141 sd_rtnl_unref(ctx->rtnl);
142
af6f0d42 143 strv_free(ctx->link_dirs);
af6f0d42
TG
144 link_configs_free(ctx);
145
146 free(ctx);
147
148 return;
149}
150
151static int load_link(link_config_ctx *ctx, const char *filename) {
152 link_config *link;
2fd069b1 153 _cleanup_fclose_ FILE *file;
af6f0d42
TG
154 int r;
155
187dc6e5
TA
156 assert(ctx);
157 assert(filename);
158
af6f0d42
TG
159 file = fopen(filename, "re");
160 if (!file) {
161 if (errno == ENOENT)
162 return 0;
163 else
164 return errno;
165 }
166
167 link = new0(link_config, 1);
168 if (!link) {
169 r = log_oom();
170 goto failure;
171 }
172
5fde13d7
TG
173 link->mac_policy = _MACPOLICY_INVALID;
174 link->wol = _WOL_INVALID;
175 link->duplex = _DUP_INVALID;
176
af6f0d42
TG
177 r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
178 (void*) link_config_gperf_lookup, false, false, link);
179 if (r < 0) {
48912436 180 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
af6f0d42
TG
181 goto failure;
182 } else
98a375f6 183 log_debug("Parsed configuration file %s", filename);
af6f0d42
TG
184
185 link->filename = strdup(filename);
186
187 LIST_PREPEND(links, ctx->links, link);
188
189 return 0;
190
191failure:
192 free(link);
193 return r;
194}
195
f6194225
TG
196static bool enable_name_policy(void) {
197 _cleanup_free_ char *line;
198 char *w, *state;
199 int r;
200 size_t l;
201
74df0fca
LP
202 r = proc_cmdline(&line);
203 if (r < 0)
f6194225 204 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
74df0fca
LP
205 if (r <= 0)
206 return true;
f6194225
TG
207
208 FOREACH_WORD_QUOTED(w, l, line, state)
ff83aac3 209 if (strneq(w, "net.ifnames=0", l))
f6194225
TG
210 return false;
211
212 return true;
213}
214
af6f0d42
TG
215int link_config_load(link_config_ctx *ctx) {
216 int r;
217 char **files, **f;
218
219 link_configs_free(ctx);
220
f6194225
TG
221 if (!enable_name_policy()) {
222 ctx->enable_name_policy = false;
223 log_info("Network interface NamePolicy= disabled on kernel commandline, ignoring.");
224 }
225
97f2d76d
TG
226 /* update timestamp */
227 paths_check_timestamp(ctx->link_dirs, &ctx->link_dirs_ts_usec, true);
af6f0d42
TG
228
229 r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
230 if (r < 0) {
231 log_error("failed to enumerate link files: %s", strerror(-r));
232 return r;
233 }
234
235 STRV_FOREACH_BACKWARDS(f, files) {
236 r = load_link(ctx, *f);
237 if (r < 0)
238 return r;
239 }
240
241 return 0;
242}
243
244bool link_config_should_reload(link_config_ctx *ctx) {
97f2d76d 245 return paths_check_timestamp(ctx->link_dirs, &ctx->link_dirs_ts_usec, false);
af6f0d42
TG
246}
247
af6f0d42
TG
248int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
249 link_config *link;
250
251 LIST_FOREACH(links, link, ctx->links) {
b3e01314 252
be32eb9b 253 if (net_match_config(link->match_mac, link->match_path,
b3e01314
TG
254 link->match_driver, link->match_type, NULL,
255 udev_device_get_sysattr_value(device, "address"),
256 udev_device_get_property_value(device, "ID_PATH"),
257 udev_device_get_driver(device),
258 udev_device_get_devtype(device),
259 NULL)) {
be32eb9b
TG
260 log_debug("Config file %s applies to device %s",
261 link->filename,
262 udev_device_get_sysname(device));
af6f0d42
TG
263 *ret = link;
264 return 0;
265 }
266 }
267
be32eb9b
TG
268 *ret = NULL;
269
af6f0d42
TG
270 return -ENOENT;
271}
272
16b9b87a
TG
273static bool mac_is_random(struct udev_device *device) {
274 const char *s;
f1ac7002
TG
275 unsigned type;
276 int r;
16b9b87a
TG
277
278 s = udev_device_get_sysattr_value(device, "addr_assign_type");
279 if (!s)
f1ac7002
TG
280 return false; /* if we don't know, assume it is not random */
281 r = safe_atou(s, &type);
282 if (r < 0)
283 return false;
16b9b87a
TG
284
285 /* check for NET_ADDR_RANDOM */
286 return type == 1;
287}
288
289static bool mac_is_permanent(struct udev_device *device) {
290 const char *s;
f1ac7002
TG
291 unsigned type;
292 int r;
16b9b87a
TG
293
294 s = udev_device_get_sysattr_value(device, "addr_assign_type");
295 if (!s)
f1ac7002
TG
296 return true; /* if we don't know, assume it is permanent */
297 r = safe_atou(s, &type);
298 if (r < 0)
299 return true;
16b9b87a
TG
300
301 /* check for NET_ADDR_PERM */
302 return type == 0;
303}
304
9bf3b535
LP
305#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
306
5fde13d7 307static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
9bf3b535 308 int r;
16b9b87a 309
16b9b87a 310 if (want_random)
9bf3b535 311 random_bytes(mac->ether_addr_octet, ETH_ALEN);
16b9b87a
TG
312 else {
313 const char *name;
9bf3b535
LP
314 uint8_t result[8];
315 size_t l, sz;
316 uint8_t *v;
16b9b87a
TG
317
318 /* fetch some persistent data unique (on this machine) to this device */
319 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
320 if (!name) {
321 name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
322 if (!name) {
323 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
324 if (!name)
55428d84 325 return -ENOENT;
16b9b87a
TG
326 }
327 }
9bf3b535
LP
328
329 l = strlen(name);
330 sz = sizeof(sd_id128_t) + l;
331 v = alloca(sz);
332
16b9b87a 333 /* fetch some persistent data unique to this machine */
9bf3b535 334 r = sd_id128_get_machine((sd_id128_t*) v);
16b9b87a 335 if (r < 0)
55428d84 336 return r;
9bf3b535 337 memcpy(v + sizeof(sd_id128_t), name, l);
16b9b87a 338
9bf3b535
LP
339 /* Let's hash the machine ID plus the device name. We
340 * use a fixed, but originally randomly created hash
341 * key here. */
342 siphash24(result, v, sz, HASH_KEY.bytes);
16b9b87a 343
9bf3b535
LP
344 assert_cc(ETH_ALEN <= sizeof(result));
345 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
16b9b87a
TG
346 }
347
348 /* see eth_random_addr in the kernel */
349 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
350 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
351
16b9b87a
TG
352 return 0;
353}
354
3e137a1b
TG
355int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device, const char **name) {
356 const char *old_name;
5fde13d7
TG
357 const char *new_name = NULL;
358 struct ether_addr generated_mac;
16b9b87a 359 struct ether_addr *mac = NULL;
43b3a5ef 360 int r, ifindex;
af6f0d42 361
3e137a1b
TG
362 assert(ctx);
363 assert(config);
364 assert(device);
365 assert(name);
366
97708579
TG
367 r = link_config_ctx_connect(ctx);
368 if (r < 0)
369 return r;
370
3e137a1b
TG
371 old_name = udev_device_get_sysname(device);
372 if (!old_name)
af6f0d42
TG
373 return -EINVAL;
374
3e137a1b 375 r = ethtool_set_speed(ctx->ethtool_fd, old_name, config->speed, config->duplex);
5fde13d7
TG
376 if (r < 0)
377 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
3e137a1b 378 old_name, config->speed, duplex_to_string(config->duplex), strerror(-r));
a5010333 379
3e137a1b 380 r = ethtool_set_wol(ctx->ethtool_fd, old_name, config->wol);
5fde13d7
TG
381 if (r < 0)
382 log_warning("Could not set WakeOnLan of %s to %s: %s",
3e137a1b 383 old_name, wol_to_string(config->wol), strerror(-r));
af6f0d42 384
43b3a5ef
TG
385 ifindex = udev_device_get_ifindex(device);
386 if (ifindex <= 0) {
387 log_warning("Could not find ifindex");
388 return -ENODEV;
389 }
390
f6194225 391 if (ctx->enable_name_policy && config->name_policy) {
5fde13d7 392 NamePolicy *policy;
daeb71a3 393
5fde13d7
TG
394 for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
395 switch (*policy) {
396 case NAMEPOLICY_ONBOARD:
397 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
daeb71a3 398 break;
5fde13d7
TG
399 case NAMEPOLICY_SLOT:
400 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
daeb71a3 401 break;
5fde13d7
TG
402 case NAMEPOLICY_PATH:
403 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
daeb71a3 404 break;
5fde13d7
TG
405 case NAMEPOLICY_MAC:
406 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
daeb71a3 407 break;
5fde13d7
TG
408 default:
409 break;
410 }
daeb71a3
TG
411 }
412 }
413
3e137a1b
TG
414 if (new_name)
415 *name = new_name; /* a name was set by a policy */
416 else if (config->name)
417 *name = config->name; /* a name was set manually in the config */
418 else
419 *name = NULL;
daeb71a3 420
5fde13d7
TG
421 switch (config->mac_policy) {
422 case MACPOLICY_PERSISTENT:
16b9b87a 423 if (!mac_is_permanent(device)) {
5fde13d7 424 r = get_mac(device, false, &generated_mac);
16b9b87a
TG
425 if (r < 0)
426 return r;
5fde13d7 427 mac = &generated_mac;
16b9b87a 428 }
5fde13d7
TG
429 break;
430 case MACPOLICY_RANDOM:
16b9b87a 431 if (!mac_is_random(device)) {
5fde13d7 432 r = get_mac(device, true, &generated_mac);
16b9b87a
TG
433 if (r < 0)
434 return r;
5fde13d7 435 mac = &generated_mac;
16b9b87a 436 }
5fde13d7
TG
437 break;
438 default:
439 mac = config->mac;
16b9b87a
TG
440 }
441
d2df0d0e 442 r = rtnl_set_link_properties(ctx->rtnl, ifindex, config->alias, mac, config->mtu);
43b3a5ef 443 if (r < 0) {
d2df0d0e 444 log_warning("Could not set Alias, MACAddress or MTU on %s: %s", old_name, strerror(-r));
5fde13d7 445 return r;
43b3a5ef
TG
446 }
447
af6f0d42
TG
448 return 0;
449}
be32eb9b
TG
450
451static const char* const mac_policy_table[] = {
452 [MACPOLICY_PERSISTENT] = "persistent",
453 [MACPOLICY_RANDOM] = "random"
454};
455
456DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy);
457DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy, "Failed to parse MAC address policy");
458
459static const char* const name_policy_table[] = {
460 [NAMEPOLICY_ONBOARD] = "onboard",
461 [NAMEPOLICY_SLOT] = "slot",
462 [NAMEPOLICY_PATH] = "path",
463 [NAMEPOLICY_MAC] = "mac"
464};
465
466DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
467DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, _NAMEPOLICY_INVALID, "Failed to parse interface name policy");