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>
24 #include "link-config.h"
26 #include "ethtool-util.h"
28 #include "libudev-private.h"
33 #include "path-util.h"
34 #include "conf-parser.h"
35 #include "conf-files.h"
37 struct link_config_ctx {
38 LIST_HEAD(link_config, links);
45 usec_t *link_dirs_ts_usec;
48 int link_config_ctx_new(link_config_ctx **ret) {
55 ctx = new0(link_config_ctx, 1);
59 r = ethtool_connect(&ctx->ethtool_fd);
61 link_config_ctx_free(ctx);
65 r = sd_rtnl_open(0, &ctx->rtnl);
67 link_config_ctx_free(ctx);
71 LIST_HEAD_INIT(ctx->links);
73 ctx->link_dirs = strv_new("/etc/net/links",
77 if (!ctx->link_dirs) {
78 log_error("failed to build link config directory array");
79 link_config_ctx_free(ctx);
82 if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
83 log_error("failed to canonicalize link config directories\n");
84 link_config_ctx_free(ctx);
88 ctx->link_dirs_ts_usec = calloc(strv_length(ctx->link_dirs), sizeof(usec_t));
89 if(!ctx->link_dirs_ts_usec) {
90 link_config_ctx_free(ctx);
98 static void link_configs_free(link_config_ctx *ctx) {
99 link_config *link, *link_next;
104 LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
105 free(link->filename);
106 free(link->match_path);
107 free(link->match_driver);
108 free(link->match_type);
109 free(link->description);
115 void link_config_ctx_free(link_config_ctx *ctx) {
119 if (ctx->ethtool_fd >= 0)
120 close_nointr_nofail(ctx->ethtool_fd);
122 sd_rtnl_unref(ctx->rtnl);
124 strv_free(ctx->link_dirs);
125 free(ctx->link_dirs_ts_usec);
126 link_configs_free(ctx);
133 static int load_link(link_config_ctx *ctx, const char *filename) {
138 file = fopen(filename, "re");
146 link = new0(link_config, 1);
152 r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
153 (void*) link_config_gperf_lookup, false, false, link);
155 log_warning("Colud not parse config file %s: %s", filename, strerror(-r));
158 log_info("Parsed configuration file %s", filename);
160 link->filename = strdup(filename);
162 LIST_PREPEND(links, ctx->links, link);
171 int link_config_load(link_config_ctx *ctx) {
175 link_configs_free(ctx);
177 /* update timestamps */
178 paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, true);
180 r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
182 log_error("failed to enumerate link files: %s", strerror(-r));
186 STRV_FOREACH_BACKWARDS(f, files) {
187 r = load_link(ctx, *f);
195 bool link_config_should_reload(link_config_ctx *ctx) {
196 return paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, false);
199 static bool match_config(link_config *match, struct udev_device *device) {
200 const char *property;
202 if (match->match_mac) {
203 property = udev_device_get_sysattr_value(device, "address");
204 if (!property || !streq(match->match_mac, property)) {
205 log_debug("Device MAC address (%s) did not match MACAddress=%s", property, match->match_mac);
210 if (match->match_path) {
211 property = udev_device_get_property_value(device, "ID_PATH");
212 if (!property || !streq(match->match_path, property)) {
213 log_debug("Device's persistent path (%s) did not match Path=%s", property, match->match_path);
218 if (match->match_driver) {
219 property = udev_device_get_driver(device);
220 if (!property || !streq(match->match_driver, property)) {
221 log_debug("Device driver (%s) did not match Driver=%s", property, match->match_driver);
226 if (match->match_type) {
227 property = udev_device_get_devtype(device);
228 if (!property || !streq(match->match_type, property)) {
229 log_debug("Device type (%s) did not match Type=%s", property, match->match_type);
237 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
240 LIST_FOREACH(links, link, ctx->links) {
241 if (!match_config(link, device)) {
242 log_info("Config file %s does not apply to device %s", link->filename, udev_device_get_sysname(device));
244 log_info("Config file %s applies to device %s", link->filename, udev_device_get_sysname(device));
253 static int rtnl_set_properties(sd_rtnl *rtnl, int ifindex, const char *name, const char *mac, unsigned int mtu) {
254 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message;
261 r = sd_rtnl_message_link_new(RTM_NEWLINK, ifindex, 0, 0, &message);
266 r = sd_rtnl_message_append(message, IFLA_IFNAME, name);
274 r = sd_rtnl_message_append(message, IFLA_ADDRESS, ether_aton(mac));
282 r = sd_rtnl_message_append(message, IFLA_MTU, &mtu);
290 r = sd_rtnl_send_with_reply_and_block(rtnl, message, 250 * USEC_PER_MSEC, NULL);
298 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device) {
302 name = udev_device_get_sysname(device);
306 log_info("Configuring %s", name);
308 if (config->description) {
309 r = udev_device_set_sysattr_value(device, "ifalias",
310 config->description);
312 log_warning("Could not set description of %s to '%s': %s",
313 name, config->description, strerror(-r));
315 log_info("Set link description of %s to '%s'", name,
316 config->description);
319 if (config->speed || config->duplex) {
320 r = ethtool_set_speed(ctx->ethtool_fd, name,
321 config->speed, config->duplex);
323 log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
324 name, config->speed, config->duplex, strerror(-r));
326 log_info("Set speed or duplex of %s to %u Mbytes (%s)", name,
327 config->speed, config->duplex);
331 r = ethtool_set_wol(ctx->ethtool_fd, name, config->wol);
333 log_warning("Could not set WakeOnLan of %s to %s: %s",
334 name, config->wol, strerror(-r));
336 log_info("Set WakeOnLan of %s to %s", name, config->wol);
339 ifindex = udev_device_get_ifindex(device);
341 log_warning("Could not find ifindex");
345 r = rtnl_set_properties(ctx->rtnl, ifindex, config->name, config->mac, config->mtu);
347 log_warning("Could not set Name, MACAddress or MTU on %s", name);