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 DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free);
53 #define _cleanup_link_config_ctx_free_ _cleanup_(link_config_ctx_freep)
55 int link_config_ctx_new(link_config_ctx **ret) {
56 _cleanup_link_config_ctx_free_ link_config_ctx *ctx = NULL;
61 ctx = new0(link_config_ctx, 1);
65 LIST_HEAD_INIT(ctx->links);
69 ctx->link_dirs = strv_new("/etc/systemd/network",
70 "/run/systemd/network",
71 "/usr/lib/systemd/network",
73 if (!ctx->link_dirs) {
74 log_error("failed to build link config directory array");
78 if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
79 log_error("failed to canonicalize link config directories\n");
89 static int link_config_ctx_connect(link_config_ctx *ctx) {
92 if (ctx->ethtool_fd >= 0 && ctx->rtnl)
95 r = ethtool_connect(&ctx->ethtool_fd);
99 r = sd_rtnl_open(0, &ctx->rtnl);
106 static void link_configs_free(link_config_ctx *ctx) {
107 link_config *link, *link_next;
112 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
113 free(link->filename);
114 free(link->match_path);
115 free(link->match_driver);
116 free(link->match_type);
117 free(link->description);
123 void link_config_ctx_free(link_config_ctx *ctx) {
127 if (ctx->ethtool_fd >= 0)
128 close_nointr_nofail(ctx->ethtool_fd);
130 sd_rtnl_unref(ctx->rtnl);
132 strv_free(ctx->link_dirs);
133 link_configs_free(ctx);
140 static int load_link(link_config_ctx *ctx, const char *filename) {
145 file = fopen(filename, "re");
153 link = new0(link_config, 1);
159 link->mac_policy = _MACPOLICY_INVALID;
160 link->wol = _WOL_INVALID;
161 link->duplex = _DUP_INVALID;
164 r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
165 (void*) link_config_gperf_lookup, false, false, link);
167 log_warning("Colud not parse config file %s: %s", filename, strerror(-r));
170 log_info("Parsed configuration file %s", filename);
172 link->filename = strdup(filename);
174 LIST_PREPEND(links, ctx->links, link);
183 int link_config_load(link_config_ctx *ctx) {
187 link_configs_free(ctx);
189 /* update timestamp */
190 paths_check_timestamp(ctx->link_dirs, &ctx->link_dirs_ts_usec, true);
192 r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
194 log_error("failed to enumerate link files: %s", strerror(-r));
198 STRV_FOREACH_BACKWARDS(f, files) {
199 r = load_link(ctx, *f);
207 bool link_config_should_reload(link_config_ctx *ctx) {
208 return paths_check_timestamp(ctx->link_dirs, &ctx->link_dirs_ts_usec, false);
211 static bool match_config(link_config *match, struct udev_device *device) {
212 const char *property;
214 if (match->match_mac) {
215 property = udev_device_get_sysattr_value(device, "address");
216 if (!property || memcmp(match->match_mac, ether_aton(property), ETH_ALEN)) {
217 log_debug("Device MAC address (%s) did not match MACAddress=%s",
218 property, ether_ntoa(match->match_mac));
223 if (match->match_path) {
224 property = udev_device_get_property_value(device, "ID_PATH");
225 if (!streq_ptr(match->match_path, property)) {
226 log_debug("Device's persistent path (%s) did not match Path=%s",
227 property, match->match_path);
232 if (match->match_driver) {
233 property = udev_device_get_driver(device);
234 if (!streq_ptr(match->match_driver, property)) {
235 log_debug("Device driver (%s) did not match Driver=%s",
236 property, match->match_driver);
241 if (match->match_type) {
242 property = udev_device_get_devtype(device);
243 if (!streq_ptr(match->match_type, property)) {
244 log_debug("Device type (%s) did not match Type=%s",
245 property, match->match_type);
253 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
256 LIST_FOREACH(links, link, ctx->links) {
257 if (!match_config(link, device)) {
258 log_info("Config file %s does not apply to device %s", link->filename, udev_device_get_sysname(device));
260 log_info("Config file %s applies to device %s", link->filename, udev_device_get_sysname(device));
269 static int rtnl_set_properties(sd_rtnl *rtnl, int ifindex, const char *name, const struct ether_addr *mac, unsigned int mtu) {
270 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message;
271 bool need_update = false;
277 r = sd_rtnl_message_link_new(RTM_NEWLINK, ifindex, 0, 0, &message);
282 r = sd_rtnl_message_append(message, IFLA_IFNAME, name);
290 r = sd_rtnl_message_append(message, IFLA_ADDRESS, mac);
298 r = sd_rtnl_message_append(message, IFLA_MTU, &mtu);
306 r = sd_rtnl_send_with_reply_and_block(rtnl, message, 0, NULL);
314 static bool enable_name_policy(void) {
315 _cleanup_free_ char *line;
320 r = read_one_line_file("/proc/cmdline", &line);
322 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
323 return true; /* something is very wrong, let's not make it worse */
326 FOREACH_WORD_QUOTED(w, l, line, state)
327 if (strneq(w, "net.ifnames=0", l))
333 static bool mac_is_random(struct udev_device *device) {
338 s = udev_device_get_sysattr_value(device, "addr_assign_type");
340 return false; /* if we don't know, assume it is not random */
341 r = safe_atou(s, &type);
345 /* check for NET_ADDR_RANDOM */
349 static bool mac_is_permanent(struct udev_device *device) {
354 s = udev_device_get_sysattr_value(device, "addr_assign_type");
356 return true; /* if we don't know, assume it is permanent */
357 r = safe_atou(s, &type);
361 /* check for NET_ADDR_PERM */
365 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) {
374 char machineid_buf[33];
375 const char *seed_str;
377 /* fetch some persistent data unique (on this machine) to this device */
378 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
380 name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
382 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
387 /* fetch some persistent data unique to this machine */
388 r = sd_id128_get_machine(&machine);
392 /* combine the data */
393 seed_str = strappenda(name, sd_id128_to_string(machine, machineid_buf));
395 /* hash to get seed */
396 seed = string_hash_func(seed_str);
401 for(i = 0; i < ETH_ALEN; i++) {
402 mac->ether_addr_octet[i] = random();
405 /* see eth_random_addr in the kernel */
406 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
407 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
412 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device) {
414 const char *new_name = NULL;
415 struct ether_addr generated_mac;
416 struct ether_addr *mac = NULL;
419 r = link_config_ctx_connect(ctx);
423 name = udev_device_get_sysname(device);
427 log_info("Configuring %s", name);
429 if (config->description) {
430 r = udev_device_set_sysattr_value(device, "ifalias",
431 config->description);
433 log_warning("Could not set description of %s to '%s': %s",
434 name, config->description, strerror(-r));
437 r = ethtool_set_speed(ctx->ethtool_fd, name, config->speed, config->duplex);
439 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
440 name, config->speed, duplex_to_string(config->duplex), strerror(-r));
442 r = ethtool_set_wol(ctx->ethtool_fd, name, config->wol);
444 log_warning("Could not set WakeOnLan of %s to %s: %s",
445 name, wol_to_string(config->wol), strerror(-r));
447 ifindex = udev_device_get_ifindex(device);
449 log_warning("Could not find ifindex");
453 if (config->name_policy && enable_name_policy()) {
456 for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) {
458 case NAMEPOLICY_ONBOARD:
459 new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
461 case NAMEPOLICY_SLOT:
462 new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
464 case NAMEPOLICY_PATH:
465 new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
468 new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC");
476 if (!new_name && config->name) {
477 new_name = config->name;
480 switch (config->mac_policy) {
481 case MACPOLICY_PERSISTENT:
482 if (!mac_is_permanent(device)) {
483 r = get_mac(device, false, &generated_mac);
486 mac = &generated_mac;
489 case MACPOLICY_RANDOM:
490 if (!mac_is_random(device)) {
491 r = get_mac(device, true, &generated_mac);
494 mac = &generated_mac;
501 r = rtnl_set_properties(ctx->rtnl, ifindex, new_name, mac, config->mtu);
503 log_warning("Could not set Name, MACAddress or MTU on %s: %s", name, strerror(-r));