From 5fde13d748749f0e06e2e6cdd15f0980a79ea82c Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Mon, 28 Oct 2013 20:59:56 +0100 Subject: [PATCH] udev: link-config - add proper parsing --- Makefile.am | 1 + src/shared/conf-parser.c | 1 + src/udev/net/ethtool-util.c | 75 +++++++++----- src/udev/net/ethtool-util.h | 34 ++++++- src/udev/net/link-config-gperf.gperf | 15 +-- src/udev/net/link-config-parse.c | 128 +++++++++++++++++++++++ src/udev/net/link-config.c | 146 ++++++++++----------------- src/udev/net/link-config.h | 42 ++++++-- 8 files changed, 311 insertions(+), 131 deletions(-) create mode 100644 src/udev/net/link-config-parse.c diff --git a/Makefile.am b/Makefile.am index 2f6ba211d..a06a79b95 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2317,6 +2317,7 @@ libudev_core_la_SOURCES = \ src/udev/udev-builtin-usb_id.c \ src/udev/net/link-config.h \ src/udev/net/link-config.c \ + src/udev/net/link-config-parse.c \ src/udev/net/ethtool-util.h \ src/udev/net/ethtool-util.c diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 6085d3339..16c3c71ea 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "conf-parser.h" #include "util.h" diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index 0a4118c6a..4fad52b46 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -29,6 +29,24 @@ #include "strxcpyx.h" #include "util.h" #include "log.h" +#include "conf-parser.h" + +static const char* const duplex_table[] = { + [DUP_FULL] = "full", + [DUP_HALF] = "half" +}; + +DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex); +DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting"); + +static const char* const wol_table[] = { + [WOL_PHY] = "phy", + [WOL_MAGIC] = "magic", + [WOL_OFF] = "off" +}; + +DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan); +DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting"); int ethtool_connect(int *ret) { int fd; @@ -45,14 +63,14 @@ int ethtool_connect(int *ret) { return 0; } -int ethtool_set_speed(int fd, const char *ifname, const unsigned int speed, const char *duplex) +int ethtool_set_speed(int fd, const char *ifname, unsigned int speed, Duplex duplex) { struct ifreq ifr; struct ethtool_cmd ecmd; bool need_update; int r; - if (speed == 0 && !duplex) + if (speed == 0 && duplex == _DUP_INVALID) return 0; memset(&ecmd, 0x00, sizeof(struct ethtool_cmd)); @@ -70,17 +88,21 @@ int ethtool_set_speed(int fd, const char *ifname, const unsigned int speed, cons need_update = true; } - if (duplex) { - if (streq(duplex, "half")) { + switch (duplex) { + case DUP_HALF: if (ecmd.duplex != DUPLEX_HALF) { ecmd.duplex = DUPLEX_HALF; need_update = true; } - } else if (streq(duplex, "full")) + break; + case DUP_FULL: if (ecmd.duplex != DUPLEX_FULL) { ecmd.duplex = DUPLEX_FULL; need_update = true; } + break; + default: + break; } if (need_update) { @@ -94,13 +116,13 @@ int ethtool_set_speed(int fd, const char *ifname, const unsigned int speed, cons return 0; } -int ethtool_set_wol(int fd, const char *ifname, const char *wol) { +int ethtool_set_wol(int fd, const char *ifname, WakeOnLan wol) { struct ifreq ifr; struct ethtool_wolinfo ecmd; bool need_update; int r; - if (!wol) + if (wol == _WOL_INVALID) return 0; memset(&ecmd, 0x00, sizeof(struct ethtool_wolinfo)); @@ -113,23 +135,28 @@ int ethtool_set_wol(int fd, const char *ifname, const char *wol) { 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; + switch (wol) { + case WOL_PHY: + if (ecmd.wolopts != WAKE_PHY) { + ecmd.wolopts = WAKE_PHY; + need_update = true; + } + break; + case WOL_MAGIC: + if (ecmd.wolopts != WAKE_MAGIC) { + ecmd.wolopts = WAKE_MAGIC; + need_update = true; + } + break; + case WOL_OFF: + if (ecmd.wolopts != 0) { + ecmd.wolopts = 0; + need_update = true; + } + break; + default: + break; + } if (need_update) { ecmd.cmd = ETHTOOL_SWOL; diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h index 74bbada6e..a02088fd9 100644 --- a/src/udev/net/ethtool-util.h +++ b/src/udev/net/ethtool-util.h @@ -19,7 +19,37 @@ along with systemd; If not, see . ***/ +#pragma once + +#include + +/* we can't use DUPLEX_ prefix, as it + * clashes with */ +typedef enum Duplex { + DUP_FULL, + DUP_HALF, + _DUP_MAX, + _DUP_INVALID = -1 +} Duplex; + +typedef enum WakeOnLan { + WOL_PHY, + WOL_MAGIC, + WOL_OFF, + _WOL_MAX, + _WOL_INVALID = -1 +} WakeOnLan; + 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); +int ethtool_set_speed(int fd, const char *ifname, unsigned int speed, Duplex duplex); +int ethtool_set_wol(int fd, const char *ifname, WakeOnLan wol); + +const char *duplex_to_string(Duplex d) _const_; +Duplex duplex_from_string(const char *d) _pure_; + +const char *wol_to_string(WakeOnLan wol) _const_; +WakeOnLan wol_from_string(const char *wol) _pure_; + +int config_parse_duplex(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_wol(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 1d905319e..130ebc9b9 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -2,6 +2,7 @@ #include #include "conf-parser.h" #include "link-config.h" +#include "ethtool-util.h" %} struct ConfigPerfItem; %null_strings @@ -14,16 +15,16 @@ struct ConfigPerfItem; %struct-type %includes %% -Match.MACAddress, config_parse_string, 0, offsetof(link_config, match_mac) +Match.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, match_mac) Match.Path, config_parse_string, 0, offsetof(link_config, match_path) 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.MACAddressPolicy, config_parse_string, 0, offsetof(link_config, mac_policy) -Link.MACAddress, config_parse_string, 0, offsetof(link_config, mac) -Link.NamePolicy, config_parse_strv, 0, offsetof(link_config, name_policy) -Link.Name, config_parse_string, 0, offsetof(link_config, name) +Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy) +Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac) +Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy) +Link.Name, config_parse_ifname, 0, offsetof(link_config, name) Link.MTU, config_parse_unsigned, 0, offsetof(link_config, mtu) 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) +Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex) +Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol) diff --git a/src/udev/net/link-config-parse.c b/src/udev/net/link-config-parse.c new file mode 100644 index 000000000..54dd57be5 --- /dev/null +++ b/src/udev/net/link-config-parse.c @@ -0,0 +1,128 @@ +/*-*- 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 "link-config.h" + +#include "utf8.h" +#include "conf-parser.h" + +static const char* const mac_policy_table[] = { + [MACPOLICY_PERSISTENT] = "persistent", + [MACPOLICY_RANDOM] = "random" +}; + +DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy); +DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy, "Failed to parse MAC address policy"); + +static const char* const name_policy_table[] = { + [NAMEPOLICY_ONBOARD] = "onboard", + [NAMEPOLICY_SLOT] = "slot", + [NAMEPOLICY_PATH] = "path", + [NAMEPOLICY_MAC] = "mac" +}; + +DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy); +DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, _NAMEPOLICY_INVALID, "Failed to parse interface name policy"); + +int config_parse_ifname(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char **s = data; + char *n; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + n = strdup(rvalue); + if (!n) + return log_oom(); + + if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue); + free(n); + return 0; + } + + free(*s); + if (*n) + *s = n; + else { + free(n); + *s = NULL; + } + + return 0; +} + +int config_parse_hwaddr(const char *unit, + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + struct ether_addr **hwaddr = data; + struct ether_addr *n; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + n = calloc(1, sizeof(struct ether_addr)); + if (!n) + return log_oom(); + + r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &n->ether_addr_octet[0], + &n->ether_addr_octet[1], + &n->ether_addr_octet[2], + &n->ether_addr_octet[3], + &n->ether_addr_octet[4], + &n->ether_addr_octet[5]); + if (r != 6) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Not a valid MAC address, ignoring assignment: %s", rvalue); + free(n); + return 0; + } + + free(*hwaddr); + *hwaddr = n; + + return 0; +} diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 0a86d0eb7..0b5916be4 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -153,6 +153,11 @@ static int load_link(link_config_ctx *ctx, const char *filename) { goto failure; } + link->mac_policy = _MACPOLICY_INVALID; + link->wol = _WOL_INVALID; + link->duplex = _DUP_INVALID; + + r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup, (void*) link_config_gperf_lookup, false, false, link); if (r < 0) { @@ -205,32 +210,36 @@ static bool match_config(link_config *match, struct udev_device *device) { if (match->match_mac) { property = udev_device_get_sysattr_value(device, "address"); - if (!property || !streq(match->match_mac, property)) { - log_debug("Device MAC address (%s) did not match MACAddress=%s", property, match->match_mac); + if (!property || memcmp(match->match_mac, ether_aton(property), ETH_ALEN)) { + log_debug("Device MAC address (%s) did not match MACAddress=%s", + property, ether_ntoa(match->match_mac)); return 0; } } if (match->match_path) { property = udev_device_get_property_value(device, "ID_PATH"); - if (!property || !streq(match->match_path, property)) { - log_debug("Device's persistent path (%s) did not match Path=%s", property, match->match_path); + if (!streq_ptr(match->match_path, property)) { + log_debug("Device's persistent path (%s) did not match Path=%s", + property, match->match_path); return 0; } } if (match->match_driver) { property = udev_device_get_driver(device); - if (!property || !streq(match->match_driver, property)) { - log_debug("Device driver (%s) did not match Driver=%s", property, match->match_driver); + if (!streq_ptr(match->match_driver, property)) { + log_debug("Device driver (%s) did not match Driver=%s", + property, match->match_driver); return 0; } } if (match->match_type) { property = udev_device_get_devtype(device); - if (!property || !streq(match->match_type, property)) { - log_debug("Device type (%s) did not match Type=%s", property, match->match_type); + if (!streq_ptr(match->match_type, property)) { + log_debug("Device type (%s) did not match Type=%s", + property, match->match_type); return 0; } } @@ -344,15 +353,10 @@ static bool mac_is_permanent(struct udev_device *device) { return type == 0; } -static int get_mac(struct udev_device *device, bool want_random, struct ether_addr **ret) { - struct ether_addr *mac; +static int get_mac(struct udev_device *device, bool want_random, struct ether_addr *mac) { unsigned int seed; int r, i; - mac = calloc(1, sizeof(struct ether_addr)); - if (!mac) - return -ENOMEM; - if (want_random) seed = random_u(); else { @@ -393,14 +397,13 @@ static int get_mac(struct udev_device *device, bool want_random, struct ether_ad mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ - *ret = mac; - return 0; } int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device) { const char *name; - char *new_name = NULL; + const char *new_name = NULL; + struct ether_addr generated_mac; struct ether_addr *mac = NULL; int r, ifindex; @@ -416,30 +419,17 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_dev 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); - } + 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, duplex_to_string(config->duplex), strerror(-r)); - 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); - } + 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, wol_to_string(config->wol), strerror(-r)); ifindex = udev_device_get_ifindex(device); if (ifindex <= 0) { @@ -448,84 +438,58 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_dev } if (config->name_policy && enable_name_policy()) { - char **policy; + NamePolicy *policy; - STRV_FOREACH(policy, config->name_policy) { - if (streq(*policy, "onboard")) { - r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD"), &new_name); - if (r < 0) - return r; - if (new_name) + for (policy = config->name_policy; !new_name && *policy != _NAMEPOLICY_INVALID; policy++) { + switch (*policy) { + case NAMEPOLICY_ONBOARD: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD"); break; - } else if (streq(*policy, "slot")) { - r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_SLOT"), &new_name); - if (r < 0) - return r; - if (new_name) + case NAMEPOLICY_SLOT: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT"); break; - } else if (streq(*policy, "path")) { - r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_PATH"), &new_name); - if (r < 0) - return r; - if (new_name) + case NAMEPOLICY_PATH: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH"); break; - } else if (streq(*policy, "mac")) { - r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_MAC"), &new_name); - if (r < 0) - return r; - if (new_name) + case NAMEPOLICY_MAC: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC"); break; - } else - log_warning("Invalid link naming policy '%s', ignoring.", *policy); + default: + break; + } } } if (!new_name && config->name) { - new_name = calloc(1, IFNAMSIZ); - strscpy(new_name, IFNAMSIZ, config->name); + new_name = config->name; } - if (config->mac_policy) { - if (streq(config->mac_policy, "persistent")) { + switch (config->mac_policy) { + case MACPOLICY_PERSISTENT: if (!mac_is_permanent(device)) { - r = get_mac(device, false, &mac); + r = get_mac(device, false, &generated_mac); if (r < 0) return r; + mac = &generated_mac; } - } else if (streq(config->mac_policy, "random")) { + break; + case MACPOLICY_RANDOM: if (!mac_is_random(device)) { - r = get_mac(device, true, &mac); + r = get_mac(device, true, &generated_mac); if (r < 0) return r; + mac = &generated_mac; } - } else - log_warning("Invalid MACAddress policy '%s', ignoring.", config->mac_policy); - } - - if (!mac && config->mac) { - mac = calloc(1, sizeof(struct ether_addr)); - r = sscanf(config->mac, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &mac->ether_addr_octet[0], - &mac->ether_addr_octet[1], - &mac->ether_addr_octet[2], - &mac->ether_addr_octet[3], - &mac->ether_addr_octet[4], - &mac->ether_addr_octet[5]); - if (r != 6) { - r = -EINVAL; - goto out; - } + break; + default: + mac = config->mac; } r = rtnl_set_properties(ctx->rtnl, ifindex, new_name, mac, config->mtu); if (r < 0) { log_warning("Could not set Name, MACAddress or MTU on %s: %s", name, strerror(-r)); - goto out; + return r; } return 0; -out: - free(new_name); - free(mac); - return r; } diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index d0a81f8bc..f5dee5a27 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -21,31 +21,48 @@ #pragma once +#include "ethtool-util.h" + #include "libudev.h" #include "util.h" #include "list.h" - typedef struct link_config_ctx link_config_ctx; typedef struct link_config link_config; +typedef enum MACPolicy { + MACPOLICY_PERSISTENT, + MACPOLICY_RANDOM, + _MACPOLICY_MAX, + _MACPOLICY_INVALID = -1 +} MACPolicy; + +typedef enum NamePolicy { + NAMEPOLICY_ONBOARD, + NAMEPOLICY_SLOT, + NAMEPOLICY_PATH, + NAMEPOLICY_MAC, + _NAMEPOLICY_MAX, + _NAMEPOLICY_INVALID = -1 +} NamePolicy; + struct link_config { char *filename; - char *match_mac; + struct ether_addr *match_mac; char *match_path; char *match_driver; char *match_type; char *description; - char *mac; - char *mac_policy; - char **name_policy; + struct ether_addr *mac; + MACPolicy mac_policy; + NamePolicy *name_policy; char *name; unsigned int mtu; unsigned int speed; - char *duplex; - char *wol; + Duplex duplex; + WakeOnLan wol; LIST_FIELDS(link_config, links); }; @@ -59,5 +76,16 @@ bool link_config_should_reload(link_config_ctx *ctx); int link_config_get(link_config_ctx *ctx, struct udev_device *device, struct link_config **ret); int link_config_apply(link_config_ctx *ctx, struct link_config *config, struct udev_device *device); +const char *name_policy_to_string(NamePolicy p) _const_; +NamePolicy name_policy_from_string(const char *p) _pure_; + +const char *mac_policy_to_string(MACPolicy p) _const_; +MACPolicy mac_policy_from_string(const char *p) _pure_; + /* gperf lookup function */ const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, unsigned length); + +int config_parse_hwaddr(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_ifname(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_mac_policy(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_name_policy(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -- 2.30.2