From a501033335ed402c8f7e86fe41a15531ba69abd7 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Sat, 26 Oct 2013 18:54:16 +0200 Subject: [PATCH] udev: link-config: add ethtool support This adds support for setting the link speed, duplex and WakeOnLan settings. Example: [Link] SpeedMBytes=100 Duplex=half WakeOnLan=magic --- Makefile.am | 4 +- src/udev/net/ethtool-util.c | 143 +++++++++++++++++++++++++++ src/udev/net/ethtool-util.h | 25 +++++ src/udev/net/link-config-gperf.gperf | 3 + src/udev/net/link-config.c | 41 +++++++- src/udev/net/link-config.h | 3 + 6 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 src/udev/net/ethtool-util.c create mode 100644 src/udev/net/ethtool-util.h diff --git a/Makefile.am b/Makefile.am index 22389a6b90..e61910064d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2295,7 +2295,9 @@ libudev_core_la_SOURCES = \ src/udev/udev-builtin-path_id.c \ src/udev/udev-builtin-usb_id.c \ src/udev/net/link-config.h \ - src/udev/net/link-config.c + src/udev/net/link-config.c \ + src/udev/net/ethtool-util.h \ + src/udev/net/ethtool-util.c nodist_libudev_core_la_SOURCES = \ src/udev/keyboard-keys-from-name.h \ diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c new file mode 100644 index 0000000000..0a4118c6ae --- /dev/null +++ b/src/udev/net/ethtool-util.c @@ -0,0 +1,143 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2013 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "ethtool-util.h" + +#include "strxcpyx.h" +#include "util.h" +#include "log.h" + +int ethtool_connect(int *ret) { + int fd; + + assert_return(ret, -EINVAL); + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + return -errno; + } + + *ret = fd; + + return 0; +} + +int ethtool_set_speed(int fd, const char *ifname, const unsigned int speed, const char *duplex) +{ + struct ifreq ifr; + struct ethtool_cmd ecmd; + bool need_update; + int r; + + if (speed == 0 && !duplex) + return 0; + + memset(&ecmd, 0x00, sizeof(struct ethtool_cmd)); + ecmd.cmd = ETHTOOL_GSET; + memset(&ifr, 0x00, sizeof(struct ifreq)); + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + ifr.ifr_data = (void *)&ecmd; + + r = ioctl(fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + if (ethtool_cmd_speed(&ecmd) != speed) { + ethtool_cmd_speed_set(&ecmd, speed); + need_update = true; + } + + if (duplex) { + if (streq(duplex, "half")) { + if (ecmd.duplex != DUPLEX_HALF) { + ecmd.duplex = DUPLEX_HALF; + need_update = true; + } + } else if (streq(duplex, "full")) + if (ecmd.duplex != DUPLEX_FULL) { + ecmd.duplex = DUPLEX_FULL; + need_update = true; + } + } + + if (need_update) { + ecmd.cmd = ETHTOOL_SSET; + + r = ioctl(fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + } + + return 0; +} + +int ethtool_set_wol(int fd, const char *ifname, const char *wol) { + struct ifreq ifr; + struct ethtool_wolinfo ecmd; + bool need_update; + int r; + + if (!wol) + return 0; + + memset(&ecmd, 0x00, sizeof(struct ethtool_wolinfo)); + ecmd.cmd = ETHTOOL_GWOL; + memset(&ifr, 0x00, sizeof(struct ifreq)); + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + ifr.ifr_data = (void *)&ecmd; + + r = ioctl(fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + if (streq(wol, "phy")) { + if (ecmd.wolopts != WAKE_PHY) { + ecmd.wolopts = WAKE_PHY; + need_update = true; + } + } else if (streq(wol, "magic")) { + if (ecmd.wolopts != WAKE_MAGIC) { + ecmd.wolopts = WAKE_MAGIC; + need_update = true; + } + } else if (streq(wol, "off")) { + if (ecmd.wolopts != 0) { + ecmd.wolopts = 0; + need_update = true; + } + } else + return -EINVAL; + + if (need_update) { + ecmd.cmd = ETHTOOL_SWOL; + + r = ioctl(fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + } + + return 0; +} diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h new file mode 100644 index 0000000000..74bbada6ed --- /dev/null +++ b/src/udev/net/ethtool-util.h @@ -0,0 +1,25 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2013 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int ethtool_connect(int *ret); + +int ethtool_set_speed(int fd, const char *ifname, const unsigned int speed, const char *duplex); +int ethtool_set_wol(int fd, const char *ifname, const char *wol); diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index c567e6dfaa..9e2f4d4f52 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -19,3 +19,6 @@ Match.Path, config_parse_string, 0, offsetof(link Match.Driver, config_parse_string, 0, offsetof(link_config, match_driver) Match.Type, config_parse_string, 0, offsetof(link_config, match_type) Link.Description, config_parse_string, 0, offsetof(link_config, description) +Link.SpeedMBytes, config_parse_unsigned, 0, offsetof(link_config, speed) +Link.Duplex, config_parse_string, 0, offsetof(link_config, duplex) +Link.WakeOnLan, config_parse_string, 0, offsetof(link_config, wol) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 7686d87f80..3beb28acba 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -21,6 +21,8 @@ #include "link-config.h" +#include "ethtool-util.h" + #include "util.h" #include "log.h" #include "strv.h" @@ -31,12 +33,15 @@ struct link_config_ctx { LIST_HEAD(link_config, links); + int ethtool_fd; + char **link_dirs; usec_t *link_dirs_ts_usec; }; int link_config_ctx_new(link_config_ctx **ret) { link_config_ctx *ctx; + int r; if (!ret) return -EINVAL; @@ -45,6 +50,12 @@ int link_config_ctx_new(link_config_ctx **ret) { if (!ctx) return -ENOMEM; + r = ethtool_connect(&ctx->ethtool_fd); + if (r < 0) { + link_config_ctx_free(ctx); + return r; + } + LIST_HEAD_INIT(ctx->links); ctx->link_dirs = strv_new("/etc/net/links", @@ -93,6 +104,7 @@ void link_config_ctx_free(link_config_ctx *ctx) { if (!ctx) return; + close_nointr_nofail(ctx->ethtool_fd); strv_free(ctx->link_dirs); free(ctx->link_dirs_ts_usec); link_configs_free(ctx); @@ -233,11 +245,34 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_dev log_info("Configuring %s", name); if (config->description) { - r = udev_device_set_sysattr_value(device, "ifalias", config->description); + r = udev_device_set_sysattr_value(device, "ifalias", + config->description); if (r < 0) - log_warning("Could not set description of %s to '%s': %s", name, config->description, strerror(-r)); + log_warning("Could not set description of %s to '%s': %s", + name, config->description, strerror(-r)); else - log_info("Set link description of %s to '%s'", name, config->description); + log_info("Set link description of %s to '%s'", name, + config->description); + } + + if (config->speed || config->duplex) { + r = ethtool_set_speed(ctx->ethtool_fd, name, + config->speed, config->duplex); + if (r < 0) + log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s", + name, config->speed, config->duplex, strerror(-r)); + else + log_info("Set speed or duplex of %s to %u Mbytes (%s)", name, + config->speed, config->duplex); + } + + if (config->wol) { + r = ethtool_set_wol(ctx->ethtool_fd, name, config->wol); + if (r < 0) + log_warning("Could not set WakeOnLan of %s to %s: %s", + name, config->wol, strerror(-r)); + else + log_info("Set WakeOnLan of %s to %s", name, config->wol); } return 0; diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index da5608ce4c..57d2c9c3cd 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -38,6 +38,9 @@ struct link_config { char *match_type; char *description; + unsigned int speed; + char *duplex; + char *wol; LIST_FIELDS(link_config, links); };