]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/net/link-config.c
sd-ipv4ll/networkd: generate predictable addresses
[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
847a8a5f
TG
93 if (ctx->ethtool_fd == -1) {
94 r = ethtool_connect(&ctx->ethtool_fd);
95 if (r < 0)
96 return r;
97 }
97708579 98
847a8a5f
TG
99 if (!ctx->rtnl) {
100 r = sd_rtnl_open(&ctx->rtnl, 0);
101 if (r < 0)
102 return r;
103 }
97708579
TG
104
105 return 0;
106}
107
af6f0d42
TG
108static void link_configs_free(link_config_ctx *ctx) {
109 link_config *link, *link_next;
110
111 if (!ctx)
112 return;
113
114 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
115 free(link->filename);
116 free(link->match_path);
117 free(link->match_driver);
118 free(link->match_type);
119 free(link->description);
d2df0d0e 120 free(link->alias);
af6f0d42
TG
121
122 free(link);
123 }
124}
125
126void link_config_ctx_free(link_config_ctx *ctx) {
127 if (!ctx)
128 return;
129
03e334a1 130 safe_close(ctx->ethtool_fd);
43b3a5ef
TG
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
edbb03e9
TG
243 if (net_match_config(link->match_mac, link->match_path, link->match_driver,
244 link->match_type, NULL, link->match_host,
245 link->match_virt, link->match_kernel, link->match_arch,
b3e01314
TG
246 udev_device_get_sysattr_value(device, "address"),
247 udev_device_get_property_value(device, "ID_PATH"),
9b1c2626 248 udev_device_get_driver(udev_device_get_parent(device)),
bf175aaf 249 udev_device_get_property_value(device, "ID_NET_DRIVER"),
b3e01314
TG
250 udev_device_get_devtype(device),
251 NULL)) {
be32eb9b
TG
252 log_debug("Config file %s applies to device %s",
253 link->filename,
254 udev_device_get_sysname(device));
af6f0d42
TG
255 *ret = link;
256 return 0;
257 }
258 }
259
be32eb9b
TG
260 *ret = NULL;
261
af6f0d42
TG
262 return -ENOENT;
263}
264
16b9b87a
TG
265static bool mac_is_random(struct udev_device *device) {
266 const char *s;
f1ac7002
TG
267 unsigned type;
268 int r;
16b9b87a
TG
269
270 s = udev_device_get_sysattr_value(device, "addr_assign_type");
271 if (!s)
f1ac7002
TG
272 return false; /* if we don't know, assume it is not random */
273 r = safe_atou(s, &type);
274 if (r < 0)
275 return false;
16b9b87a
TG
276
277 /* check for NET_ADDR_RANDOM */
278 return type == 1;
279}
280
281static bool mac_is_permanent(struct udev_device *device) {
282 const char *s;
f1ac7002
TG
283 unsigned type;
284 int r;
16b9b87a
TG
285
286 s = udev_device_get_sysattr_value(device, "addr_assign_type");
287 if (!s)
f1ac7002
TG
288 return true; /* if we don't know, assume it is permanent */
289 r = safe_atou(s, &type);
290 if (r < 0)
291 return true;
16b9b87a
TG
292
293 /* check for NET_ADDR_PERM */
294 return type == 0;
295}
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 302 else {
9bf3b535 303 uint8_t result[8];
9bf3b535 304
b5db00e5 305 r = net_get_unique_predictable_data(device, result);
16b9b87a 306 if (r < 0)
55428d84 307 return r;
16b9b87a 308
9bf3b535
LP
309 assert_cc(ETH_ALEN <= sizeof(result));
310 memcpy(mac->ether_addr_octet, result, ETH_ALEN);
16b9b87a
TG
311 }
312
313 /* see eth_random_addr in the kernel */
314 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
315 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
316
16b9b87a
TG
317 return 0;
318}
319
3e137a1b
TG
320int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device, const char **name) {
321 const char *old_name;
5fde13d7
TG
322 const char *new_name = NULL;
323 struct ether_addr generated_mac;
16b9b87a 324 struct ether_addr *mac = NULL;
43b3a5ef 325 int r, ifindex;
af6f0d42 326
3e137a1b
TG
327 assert(ctx);
328 assert(config);
329 assert(device);
330 assert(name);
331
97708579
TG
332 r = link_config_ctx_connect(ctx);
333 if (r < 0)
334 return r;
335
3e137a1b
TG
336 old_name = udev_device_get_sysname(device);
337 if (!old_name)
af6f0d42
TG
338 return -EINVAL;
339
733f7a2c 340 r = ethtool_set_speed(ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex);
5fde13d7 341 if (r < 0)
733f7a2c
TG
342 log_warning("Could not set speed or duplex of %s to %u Mbps (%s): %s",
343 old_name, config->speed / 1024, duplex_to_string(config->duplex),
344 strerror(-r));
a5010333 345
3e137a1b 346 r = ethtool_set_wol(ctx->ethtool_fd, old_name, config->wol);
5fde13d7
TG
347 if (r < 0)
348 log_warning("Could not set WakeOnLan of %s to %s: %s",
3e137a1b 349 old_name, wol_to_string(config->wol), strerror(-r));
af6f0d42 350
43b3a5ef
TG
351 ifindex = udev_device_get_ifindex(device);
352 if (ifindex <= 0) {
353 log_warning("Could not find ifindex");
354 return -ENODEV;
355 }
356
f6194225 357 if (ctx->enable_name_policy && config->name_policy) {
5fde13d7 358 NamePolicy *policy;
daeb71a3 359
5fde13d7
TG
360 for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
361 switch (*policy) {
e51660ae
TG
362 case NAMEPOLICY_DATABASE:
363 new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE");
364 break;
5fde13d7
TG
365 case NAMEPOLICY_ONBOARD:
366 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
daeb71a3 367 break;
5fde13d7
TG
368 case NAMEPOLICY_SLOT:
369 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
daeb71a3 370 break;
5fde13d7
TG
371 case NAMEPOLICY_PATH:
372 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
daeb71a3 373 break;
5fde13d7
TG
374 case NAMEPOLICY_MAC:
375 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
daeb71a3 376 break;
5fde13d7
TG
377 default:
378 break;
379 }
daeb71a3
TG
380 }
381 }
382
3e137a1b
TG
383 if (new_name)
384 *name = new_name; /* a name was set by a policy */
385 else if (config->name)
386 *name = config->name; /* a name was set manually in the config */
387 else
388 *name = NULL;
daeb71a3 389
5fde13d7
TG
390 switch (config->mac_policy) {
391 case MACPOLICY_PERSISTENT:
16b9b87a 392 if (!mac_is_permanent(device)) {
5fde13d7 393 r = get_mac(device, false, &generated_mac);
16b9b87a
TG
394 if (r < 0)
395 return r;
5fde13d7 396 mac = &generated_mac;
16b9b87a 397 }
5fde13d7
TG
398 break;
399 case MACPOLICY_RANDOM:
16b9b87a 400 if (!mac_is_random(device)) {
5fde13d7 401 r = get_mac(device, true, &generated_mac);
16b9b87a
TG
402 if (r < 0)
403 return r;
5fde13d7 404 mac = &generated_mac;
16b9b87a 405 }
5fde13d7
TG
406 break;
407 default:
408 mac = config->mac;
16b9b87a
TG
409 }
410
d2df0d0e 411 r = rtnl_set_link_properties(ctx->rtnl, ifindex, config->alias, mac, config->mtu);
43b3a5ef 412 if (r < 0) {
d2df0d0e 413 log_warning("Could not set Alias, MACAddress or MTU on %s: %s", old_name, strerror(-r));
5fde13d7 414 return r;
43b3a5ef
TG
415 }
416
af6f0d42
TG
417 return 0;
418}
be32eb9b 419
847a8a5f
TG
420int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) {
421 const char *name;
422 char *driver;
423 int r;
424
425 r = link_config_ctx_connect(ctx);
426 if (r < 0)
427 return r;
428
429 name = udev_device_get_sysname(device);
430 if (!name)
431 return -EINVAL;
432
433 r = ethtool_get_driver(ctx->ethtool_fd, name, &driver);
434 if (r < 0)
435 return r;
436
437 *ret = driver;
438 return 0;
439}
440
2c5859af 441static const char* const mac_policy_table[_MACPOLICY_MAX] = {
be32eb9b
TG
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
2c5859af 449static const char* const name_policy_table[_NAMEPOLICY_MAX] = {
e51660ae 450 [NAMEPOLICY_DATABASE] = "database",
be32eb9b
TG
451 [NAMEPOLICY_ONBOARD] = "onboard",
452 [NAMEPOLICY_SLOT] = "slot",
453 [NAMEPOLICY_PATH] = "path",
454 [NAMEPOLICY_MAC] = "mac"
455};
456
457DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy);
458DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, _NAMEPOLICY_INVALID, "Failed to parse interface name policy");