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