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/net/links",
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 r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
157 (void*) link_config_gperf_lookup, false, false, link);
159 log_warning("Colud not parse config file %s: %s", filename, strerror(-r));
162 log_info("Parsed configuration file %s", filename);
164 link->filename = strdup(filename);
166 LIST_PREPEND(links, ctx->links, link);
175 int link_config_load(link_config_ctx *ctx) {
179 link_configs_free(ctx);
181 /* update timestamps */
182 paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, true);
184 r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
186 log_error("failed to enumerate link files: %s", strerror(-r));
190 STRV_FOREACH_BACKWARDS(f, files) {
191 r = load_link(ctx, *f);
199 bool link_config_should_reload(link_config_ctx *ctx) {
200 return paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, false);
203 static bool match_config(link_config *match, struct udev_device *device) {
204 const char *property;
206 if (match->match_mac) {
207 property = udev_device_get_sysattr_value(device, "address");
208 if (!property || !streq(match->match_mac, property)) {
209 log_debug("Device MAC address (%s) did not match MACAddress=%s", property, match->match_mac);
214 if (match->match_path) {
215 property = udev_device_get_property_value(device, "ID_PATH");
216 if (!property || !streq(match->match_path, property)) {
217 log_debug("Device's persistent path (%s) did not match Path=%s", property, match->match_path);
222 if (match->match_driver) {
223 property = udev_device_get_driver(device);
224 if (!property || !streq(match->match_driver, property)) {
225 log_debug("Device driver (%s) did not match Driver=%s", property, match->match_driver);
230 if (match->match_type) {
231 property = udev_device_get_devtype(device);
232 if (!property || !streq(match->match_type, property)) {
233 log_debug("Device type (%s) did not match Type=%s", property, match->match_type);
241 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
244 LIST_FOREACH(links, link, ctx->links) {
245 if (!match_config(link, device)) {
246 log_info("Config file %s does not apply to device %s", link->filename, udev_device_get_sysname(device));
248 log_info("Config file %s applies to device %s", link->filename, udev_device_get_sysname(device));
257 static int rtnl_set_properties(sd_rtnl *rtnl, int ifindex, const char *name, const struct ether_addr *mac, unsigned int mtu) {
258 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message;
259 bool need_update = false;
265 r = sd_rtnl_message_link_new(RTM_NEWLINK, ifindex, 0, 0, &message);
270 r = sd_rtnl_message_append(message, IFLA_IFNAME, name);
278 r = sd_rtnl_message_append(message, IFLA_ADDRESS, mac);
286 r = sd_rtnl_message_append(message, IFLA_MTU, &mtu);
294 r = sd_rtnl_send_with_reply_and_block(rtnl, message, 5 * USEC_PER_SEC, NULL);
302 static bool enable_name_policy(void) {
303 _cleanup_free_ char *line;
308 r = read_one_line_file("/proc/cmdline", &line);
310 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
311 return true; /* something is very wrong, let's not make it worse */
314 FOREACH_WORD_QUOTED(w, l, line, state)
315 if (strneq(w, "net.ifnames=0", l))
321 static bool mac_is_random(struct udev_device *device) {
325 s = udev_device_get_sysattr_value(device, "addr_assign_type");
328 type = strtoul(s, NULL, 0);
330 /* check for NET_ADDR_RANDOM */
334 static bool mac_is_permanent(struct udev_device *device) {
338 s = udev_device_get_sysattr_value(device, "addr_assign_type");
341 type = strtoul(s, NULL, 0);
343 /* check for NET_ADDR_PERM */
347 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr **ret) {
348 struct ether_addr *mac;
352 mac = calloc(1, sizeof(struct ether_addr));
361 char machineid_buf[33];
362 const char *seed_str;
364 /* fetch some persistent data unique (on this machine) to this device */
365 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
367 name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
369 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
374 /* fetch some persistent data unique to this machine */
375 r = sd_id128_get_machine(&machine);
379 /* combine the data */
380 seed_str = strappenda(name, sd_id128_to_string(machine, machineid_buf));
382 /* hash to get seed */
383 seed = string_hash_func(seed_str);
388 for(i = 0; i < ETH_ALEN; i++) {
389 mac->ether_addr_octet[i] = random();
392 /* see eth_random_addr in the kernel */
393 mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
394 mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
401 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device) {
403 char *new_name = NULL;
404 struct ether_addr *mac = NULL;
407 name = udev_device_get_sysname(device);
411 log_info("Configuring %s", name);
413 if (config->description) {
414 r = udev_device_set_sysattr_value(device, "ifalias",
415 config->description);
417 log_warning("Could not set description of %s to '%s': %s",
418 name, config->description, strerror(-r));
420 log_info("Set link description of %s to '%s'", name,
421 config->description);
424 if (config->speed || config->duplex) {
425 r = ethtool_set_speed(ctx->ethtool_fd, name,
426 config->speed, config->duplex);
428 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
429 name, config->speed, config->duplex, strerror(-r));
431 log_info("Set speed or duplex of %s to %u Mbytes (%s)", name,
432 config->speed, config->duplex);
436 r = ethtool_set_wol(ctx->ethtool_fd, name, config->wol);
438 log_warning("Could not set WakeOnLan of %s to %s: %s",
439 name, config->wol, strerror(-r));
441 log_info("Set WakeOnLan of %s to %s", name, config->wol);
444 ifindex = udev_device_get_ifindex(device);
446 log_warning("Could not find ifindex");
450 if (config->name_policy && enable_name_policy()) {
453 STRV_FOREACH(policy, config->name_policy) {
454 if (streq(*policy, "onboard")) {
455 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD"), &new_name);
460 } else if (streq(*policy, "slot")) {
461 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_SLOT"), &new_name);
466 } else if (streq(*policy, "path")) {
467 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_PATH"), &new_name);
472 } else if (streq(*policy, "mac")) {
473 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_MAC"), &new_name);
479 log_warning("Invalid link naming policy '%s', ignoring.", *policy);
483 if (!new_name && config->name) {
484 new_name = calloc(1, IFNAMSIZ);
485 strscpy(new_name, IFNAMSIZ, config->name);
488 if (config->mac_policy) {
489 if (streq(config->mac_policy, "persistent")) {
490 if (!mac_is_permanent(device)) {
491 r = get_mac(device, false, &mac);
495 } else if (streq(config->mac_policy, "random")) {
496 if (!mac_is_random(device)) {
497 r = get_mac(device, true, &mac);
502 log_warning("Invalid MACAddress policy '%s', ignoring.", config->mac_policy);
505 if (!mac && config->mac) {
506 mac = calloc(1, sizeof(struct ether_addr));
507 r = sscanf(config->mac, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
508 &mac->ether_addr_octet[0],
509 &mac->ether_addr_octet[1],
510 &mac->ether_addr_octet[2],
511 &mac->ether_addr_octet[3],
512 &mac->ether_addr_octet[4],
513 &mac->ether_addr_octet[5]);
520 r = rtnl_set_properties(ctx->rtnl, ifindex, new_name, mac, config->mtu);
522 log_warning("Could not set Name, MACAddress or MTU on %s: %s", name, strerror(-r));