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"
41 struct link_config_ctx {
42 LIST_HEAD(link_config, links);
49 usec_t *link_dirs_ts_usec;
52 int link_config_ctx_new(link_config_ctx **ret) {
59 ctx = new0(link_config_ctx, 1);
63 r = ethtool_connect(&ctx->ethtool_fd);
65 link_config_ctx_free(ctx);
69 r = sd_rtnl_open(0, &ctx->rtnl);
71 link_config_ctx_free(ctx);
75 LIST_HEAD_INIT(ctx->links);
77 ctx->link_dirs = strv_new("/etc/systemd/network",
78 "/run/systemd/network",
79 "/usr/lib/systemd/network",
81 if (!ctx->link_dirs) {
82 log_error("failed to build link config directory array");
83 link_config_ctx_free(ctx);
86 if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
87 log_error("failed to canonicalize link config directories\n");
88 link_config_ctx_free(ctx);
92 ctx->link_dirs_ts_usec = calloc(strv_length(ctx->link_dirs), sizeof(usec_t));
93 if(!ctx->link_dirs_ts_usec) {
94 link_config_ctx_free(ctx);
102 static void link_configs_free(link_config_ctx *ctx) {
103 link_config *link, *link_next;
108 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
109 free(link->filename);
110 free(link->match_path);
111 free(link->match_driver);
112 free(link->match_type);
113 free(link->description);
119 void link_config_ctx_free(link_config_ctx *ctx) {
123 if (ctx->ethtool_fd >= 0)
124 close_nointr_nofail(ctx->ethtool_fd);
126 sd_rtnl_unref(ctx->rtnl);
128 strv_free(ctx->link_dirs);
129 free(ctx->link_dirs_ts_usec);
130 link_configs_free(ctx);
137 static int load_link(link_config_ctx *ctx, const char *filename) {
142 file = fopen(filename, "re");
150 link = new0(link_config, 1);
156 link->mac_policy = _MACPOLICY_INVALID;
157 link->wol = _WOL_INVALID;
158 link->duplex = _DUP_INVALID;
161 r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
162 (void*) link_config_gperf_lookup, false, false, link);
164 log_warning("Colud not parse config file %s: %s", filename, strerror(-r));
167 log_info("Parsed configuration file %s", filename);
169 link->filename = strdup(filename);
171 LIST_PREPEND(links, ctx->links, link);
180 int link_config_load(link_config_ctx *ctx) {
184 link_configs_free(ctx);
186 /* update timestamps */
187 paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, true);
189 r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
191 log_error("failed to enumerate link files: %s", strerror(-r));
195 STRV_FOREACH_BACKWARDS(f, files) {
196 r = load_link(ctx, *f);
204 bool link_config_should_reload(link_config_ctx *ctx) {
205 return paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, false);
208 static bool match_config(link_config *match, struct udev_device *device) {
209 const char *property;
211 if (match->match_mac) {
212 property = udev_device_get_sysattr_value(device, "address");
213 if (!property || memcmp(match->match_mac, ether_aton(property), ETH_ALEN)) {
214 log_debug("Device MAC address (%s) did not match MACAddress=%s",
215 property, ether_ntoa(match->match_mac));
220 if (match->match_path) {
221 property = udev_device_get_property_value(device, "ID_PATH");
222 if (!streq_ptr(match->match_path, property)) {
223 log_debug("Device's persistent path (%s) did not match Path=%s",
224 property, match->match_path);
229 if (match->match_driver) {
230 property = udev_device_get_driver(device);
231 if (!streq_ptr(match->match_driver, property)) {
232 log_debug("Device driver (%s) did not match Driver=%s",
233 property, match->match_driver);
238 if (match->match_type) {
239 property = udev_device_get_devtype(device);
240 if (!streq_ptr(match->match_type, property)) {
241 log_debug("Device type (%s) did not match Type=%s",
242 property, match->match_type);
250 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
253 LIST_FOREACH(links, link, ctx->links) {
254 if (!match_config(link, device)) {
255 log_info("Config file %s does not apply to device %s", link->filename, udev_device_get_sysname(device));
257 log_info("Config file %s applies to device %s", link->filename, udev_device_get_sysname(device));
266 static int rtnl_set_properties(sd_rtnl *rtnl, int ifindex, const char *name, const struct ether_addr *mac, unsigned int mtu) {
267 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message;
268 bool need_update = false;
274 r = sd_rtnl_message_link_new(RTM_NEWLINK, ifindex, 0, 0, &message);
279 r = sd_rtnl_message_append(message, IFLA_IFNAME, name);
287 r = sd_rtnl_message_append(message, IFLA_ADDRESS, mac);
295 r = sd_rtnl_message_append(message, IFLA_MTU, &mtu);
303 r = sd_rtnl_send_with_reply_and_block(rtnl, message, 5 * USEC_PER_SEC, NULL);
311 static bool enable_name_policy(void) {
312 _cleanup_free_ char *line;
317 r = read_one_line_file("/proc/cmdline", &line);
319 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
320 return true; /* something is very wrong, let's not make it worse */
323 FOREACH_WORD_QUOTED(w, l, line, state)
324 if (strneq(w, "net.ifnames=0", l))
330 static bool mac_is_random(struct udev_device *device) {
334 s = udev_device_get_sysattr_value(device, "addr_assign_type");
337 type = strtoul(s, NULL, 0);
339 /* check for NET_ADDR_RANDOM */
343 static bool mac_is_permanent(struct udev_device *device) {
347 s = udev_device_get_sysattr_value(device, "addr_assign_type");
350 type = strtoul(s, NULL, 0);
352 /* check for NET_ADDR_PERM */
356 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
365 char machineid_buf[33];
366 const char *seed_str;
368 /* fetch some persistent data unique (on this machine) to this device */
369 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
371 name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
373 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
378 /* fetch some persistent data unique to this machine */
379 r = sd_id128_get_machine(&machine);
383 /* combine the data */
384 seed_str = strappenda(name, sd_id128_to_string(machine, machineid_buf));
386 /* hash to get seed */
387 seed = string_hash_func(seed_str);
392 for(i = 0; i < ETH_ALEN; i++) {
393 mac->ether_addr_octet[i] = random();
396 /* see eth_random_addr in the kernel */
397 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
398 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
403 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device) {
405 const char *new_name = NULL;
406 struct ether_addr generated_mac;
407 struct ether_addr *mac = NULL;
410 name = udev_device_get_sysname(device);
414 log_info("Configuring %s", name);
416 if (config->description) {
417 r = udev_device_set_sysattr_value(device, "ifalias",
418 config->description);
420 log_warning("Could not set description of %s to '%s': %s",
421 name, config->description, strerror(-r));
424 r = ethtool_set_speed(ctx->ethtool_fd, name, config->speed, config->duplex);
426 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
427 name, config->speed, duplex_to_string(config->duplex), strerror(-r));
429 r = ethtool_set_wol(ctx->ethtool_fd, name, config->wol);
431 log_warning("Could not set WakeOnLan of %s to %s: %s",
432 name, wol_to_string(config->wol), strerror(-r));
434 ifindex = udev_device_get_ifindex(device);
436 log_warning("Could not find ifindex");
440 if (config->name_policy && enable_name_policy()) {
443 for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
445 case NAMEPOLICY_ONBOARD:
446 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
448 case NAMEPOLICY_SLOT:
449 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
451 case NAMEPOLICY_PATH:
452 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
455 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
463 if (!new_name && config->name) {
464 new_name = config->name;
467 switch (config->mac_policy) {
468 case MACPOLICY_PERSISTENT:
469 if (!mac_is_permanent(device)) {
470 r = get_mac(device, false, &generated_mac);
473 mac = &generated_mac;
476 case MACPOLICY_RANDOM:
477 if (!mac_is_random(device)) {
478 r = get_mac(device, true, &generated_mac);
481 mac = &generated_mac;
488 r = rtnl_set_properties(ctx->rtnl, ifindex, new_name, mac, config->mtu);
490 log_warning("Could not set Name, MACAddress or MTU on %s: %s", name, strerror(-r));