chiark / gitweb /
udev: link-config: add ethtool support
authorTom Gundersen <teg@jklm.no>
Sat, 26 Oct 2013 16:54:16 +0000 (18:54 +0200)
committerTom Gundersen <teg@jklm.no>
Sat, 26 Oct 2013 20:09:20 +0000 (22:09 +0200)
This adds support for setting the link speed, duplex and WakeOnLan
settings.

Example:

[Link]
SpeedMBytes=100
Duplex=half
WakeOnLan=magic

Makefile.am
src/udev/net/ethtool-util.c [new file with mode: 0644]
src/udev/net/ethtool-util.h [new file with mode: 0644]
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h

index 22389a6..e619100 100644 (file)
@@ -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 (file)
index 0000000..0a4118c
--- /dev/null
@@ -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 <teg@jklm.no>
+
+  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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+
+#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 (file)
index 0000000..74bbada
--- /dev/null
@@ -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 <teg@jklm.no>
+
+  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 <http://www.gnu.org/licenses/>.
+***/
+
+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);
index c567e6d..9e2f4d4 100644 (file)
@@ -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)
index 7686d87..3beb28a 100644 (file)
@@ -21,6 +21,8 @@
 
 #include "link-config.h"
 
+#include "ethtool-util.h"
+
 #include "util.h"
 #include "log.h"
 #include "strv.h"
 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));
+                else
+                        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 description of %s to '%s': %s", name, config->description, strerror(-r));
+                        log_warning("Could not set WakeOnLan of %s to %s: %s",
+                                    name, config->wol, strerror(-r));
                 else
-                        log_info("Set link description of %s to '%s'", name, config->description);
+                        log_info("Set WakeOnLan of %s to %s", name, config->wol);
         }
 
         return 0;
index da5608c..57d2c9c 100644 (file)
@@ -38,6 +38,9 @@ struct link_config {
         char *match_type;
 
         char *description;
+        unsigned int speed;
+        char *duplex;
+        char *wol;
 
         LIST_FIELDS(link_config, links);
 };