1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
22 #include <netinet/ether.h>
27 #include "link-config.h"
28 #include "ethtool-util.h"
30 #include "libudev-private.h"
35 #include "path-util.h"
36 #include "conf-parser.h"
37 #include "conf-files.h"
40 #include "rtnl-util.h"
42 struct link_config_ctx {
43 LIST_HEAD(link_config, links);
47 bool enable_name_policy;
52 usec_t link_dirs_ts_usec;
55 DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free);
56 #define _cleanup_link_config_ctx_free_ _cleanup_(link_config_ctx_freep)
58 int link_config_ctx_new(link_config_ctx **ret) {
59 _cleanup_link_config_ctx_free_ link_config_ctx *ctx = NULL;
64 ctx = new0(link_config_ctx, 1);
68 LIST_HEAD_INIT(ctx->links);
72 ctx->enable_name_policy = true;
74 ctx->link_dirs = strv_new("/etc/systemd/network",
75 "/run/systemd/network",
76 "/usr/lib/systemd/network",
78 "/lib/systemd/network",
81 if (!ctx->link_dirs) {
82 log_error("failed to build link config directory array");
86 if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
87 log_error("failed to canonicalize link config directories\n");
97 static int link_config_ctx_connect(link_config_ctx *ctx) {
100 if (ctx->ethtool_fd >= 0 && ctx->rtnl)
103 r = ethtool_connect(&ctx->ethtool_fd);
107 r = sd_rtnl_open(0, &ctx->rtnl);
114 static void link_configs_free(link_config_ctx *ctx) {
115 link_config *link, *link_next;
120 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
121 free(link->filename);
122 free(link->match_path);
123 free(link->match_driver);
124 free(link->match_type);
125 free(link->description);
131 void link_config_ctx_free(link_config_ctx *ctx) {
135 if (ctx->ethtool_fd >= 0)
136 close_nointr_nofail(ctx->ethtool_fd);
138 sd_rtnl_unref(ctx->rtnl);
140 strv_free(ctx->link_dirs);
141 link_configs_free(ctx);
148 static int load_link(link_config_ctx *ctx, const char *filename) {
153 file = fopen(filename, "re");
161 link = new0(link_config, 1);
167 link->mac_policy = _MACPOLICY_INVALID;
168 link->wol = _WOL_INVALID;
169 link->duplex = _DUP_INVALID;
172 r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
173 (void*) link_config_gperf_lookup, false, false, link);
175 log_warning("Colud not parse config file %s: %s", filename, strerror(-r));
178 log_debug("Parsed configuration file %s", filename);
180 link->filename = strdup(filename);
182 LIST_PREPEND(links, ctx->links, link);
191 static bool enable_name_policy(void) {
192 _cleanup_free_ char *line;
197 r = read_one_line_file("/proc/cmdline", &line);
199 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
200 return true; /* something is very wrong, let's not make it worse */
203 FOREACH_WORD_QUOTED(w, l, line, state)
204 if (strneq(w, "net.ifnames=0", l))
210 int link_config_load(link_config_ctx *ctx) {
214 link_configs_free(ctx);
217 if (!enable_name_policy()) {
218 ctx->enable_name_policy = false;
219 log_info("Network interface NamePolicy= disabled on kernel commandline, ignoring.");
222 /* update timestamp */
223 paths_check_timestamp(ctx->link_dirs, &ctx->link_dirs_ts_usec, true);
225 r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
227 log_error("failed to enumerate link files: %s", strerror(-r));
231 STRV_FOREACH_BACKWARDS(f, files) {
232 r = load_link(ctx, *f);
240 bool link_config_should_reload(link_config_ctx *ctx) {
241 return paths_check_timestamp(ctx->link_dirs, &ctx->link_dirs_ts_usec, false);
244 static bool match_config(link_config *match, struct udev_device *device) {
245 const char *property;
247 if (match->match_mac) {
248 property = udev_device_get_sysattr_value(device, "address");
249 if (!property || memcmp(match->match_mac, ether_aton(property), ETH_ALEN)) {
250 log_debug("Device MAC address (%s) did not match MACAddress=%s",
251 property, ether_ntoa(match->match_mac));
256 if (match->match_path) {
257 property = udev_device_get_property_value(device, "ID_PATH");
258 if (!streq_ptr(match->match_path, property)) {
259 log_debug("Device's persistent path (%s) did not match Path=%s",
260 property, match->match_path);
265 if (match->match_driver) {
266 property = udev_device_get_driver(device);
267 if (!streq_ptr(match->match_driver, property)) {
268 log_debug("Device driver (%s) did not match Driver=%s",
269 property, match->match_driver);
274 if (match->match_type) {
275 property = udev_device_get_devtype(device);
276 if (!streq_ptr(match->match_type, property)) {
277 log_debug("Device type (%s) did not match Type=%s",
278 property, match->match_type);
286 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
289 LIST_FOREACH(links, link, ctx->links) {
290 if (match_config(link, device)) {
291 log_debug("Config file %s applies to device %s", link->filename, udev_device_get_sysname(device));
300 static bool mac_is_random(struct udev_device *device) {
305 s = udev_device_get_sysattr_value(device, "addr_assign_type");
307 return false; /* if we don't know, assume it is not random */
308 r = safe_atou(s, &type);
312 /* check for NET_ADDR_RANDOM */
316 static bool mac_is_permanent(struct udev_device *device) {
321 s = udev_device_get_sysattr_value(device, "addr_assign_type");
323 return true; /* if we don't know, assume it is permanent */
324 r = safe_atou(s, &type);
328 /* check for NET_ADDR_PERM */
332 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
341 char machineid_buf[33];
342 const char *seed_str;
344 /* fetch some persistent data unique (on this machine) to this device */
345 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
347 name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
349 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
354 /* fetch some persistent data unique to this machine */
355 r = sd_id128_get_machine(&machine);
359 /* combine the data */
360 seed_str = strappenda(name, sd_id128_to_string(machine, machineid_buf));
362 /* hash to get seed */
363 seed = string_hash_func(seed_str);
368 for(i = 0; i < ETH_ALEN; i++) {
369 mac->ether_addr_octet[i] = random();
372 /* see eth_random_addr in the kernel */
373 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
374 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
379 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device, const char **name) {
380 const char *old_name;
381 const char *new_name = NULL;
382 struct ether_addr generated_mac;
383 struct ether_addr *mac = NULL;
391 r = link_config_ctx_connect(ctx);
395 old_name = udev_device_get_sysname(device);
399 if (config->description) {
400 r = udev_device_set_sysattr_value(device, "ifalias",
401 config->description);
403 log_warning("Could not set description of %s to '%s': %s",
404 old_name, config->description, strerror(-r));
407 r = ethtool_set_speed(ctx->ethtool_fd, old_name, config->speed, config->duplex);
409 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
410 old_name, config->speed, duplex_to_string(config->duplex), strerror(-r));
412 r = ethtool_set_wol(ctx->ethtool_fd, old_name, config->wol);
414 log_warning("Could not set WakeOnLan of %s to %s: %s",
415 old_name, wol_to_string(config->wol), strerror(-r));
417 ifindex = udev_device_get_ifindex(device);
419 log_warning("Could not find ifindex");
423 if (ctx->enable_name_policy && config->name_policy) {
426 for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
428 case NAMEPOLICY_ONBOARD:
429 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
431 case NAMEPOLICY_SLOT:
432 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
434 case NAMEPOLICY_PATH:
435 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
438 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
447 *name = new_name; /* a name was set by a policy */
448 else if (config->name)
449 *name = config->name; /* a name was set manually in the config */
453 switch (config->mac_policy) {
454 case MACPOLICY_PERSISTENT:
455 if (!mac_is_permanent(device)) {
456 r = get_mac(device, false, &generated_mac);
459 mac = &generated_mac;
462 case MACPOLICY_RANDOM:
463 if (!mac_is_random(device)) {
464 r = get_mac(device, true, &generated_mac);
467 mac = &generated_mac;
474 r = rtnl_set_link_properties(ctx->rtnl, ifindex, mac, config->mtu);
476 log_warning("Could not set MACAddress or MTU on %s: %s", old_name, strerror(-r));