From 44005bfb4eaae840e481e7a0d0b74ac9ee418302 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 23 Jun 2019 01:44:13 +0900 Subject: [PATCH] network,udev: add Property= setting in [Match] section Closes #5665. --- man/systemd.link.xml | 15 +++ man/systemd.network.xml | 15 +++ src/libsystemd-network/network-internal.c | 97 +++++++++++++++++++ src/libsystemd-network/network-internal.h | 2 + src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 9 +- src/network/networkd-network.h | 1 + src/udev/net/link-config-gperf.gperf | 1 + src/udev/net/link-config.c | 5 +- src/udev/net/link-config.h | 1 + test/fuzz/fuzz-link-parser/directives.link | 1 + .../fuzz-network-parser/directives.network | 1 + 12 files changed, 143 insertions(+), 6 deletions(-) diff --git a/man/systemd.link.xml b/man/systemd.link.xml index b1be32955e5..8539422efc7 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -115,6 +115,21 @@ property DEVTYPE. + + Property= + + A whitespace-separated list of udev property name with its value after a equal + (=). If multiple properties are specified, the test results are ANDed. + If the list is prefixed with a "!", the test is inverted. If a value contains white + spaces, then please quote whole key and value pair. If a value contains quotation, then + please escape the quotation with \. + + Example: if a .link file has the following: + Property=ID_MODEL_ID=9999 "ID_VENDOR_FROM_DATABASE=vendor name" "KEY=with \"quotation\"" + then, the .link file matches only when an interface has all the above three properties. + + + Host= diff --git a/man/systemd.network.xml b/man/systemd.network.xml index bad673b44e1..1509a07ac12 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -138,6 +138,21 @@ with a "!", the test is inverted. + + Property= + + A whitespace-separated list of udev property name with its value after a equal + (=). If multiple properties are specified, the test results are ANDed. + If the list is prefixed with a "!", the test is inverted. If a value contains white + spaces, then please quote whole key and value pair. If a value contains quotation, then + please escape the quotation with \. + + Example: if a .network file has the following: + Property=ID_MODEL_ID=9999 "ID_VENDOR_FROM_DATABASE=vendor name" "KEY=with \"quotation\"" + then, the .network file matches only when an interface has all the above three properties. + + + Host= diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 1f02a1e984f..1f2e5c7e65e 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -12,6 +12,7 @@ #include "conf-parser.h" #include "device-util.h" #include "dhcp-lease-internal.h" +#include "env-util.h" #include "ether-addr-util.h" #include "hexdecoct.h" #include "log.h" @@ -101,11 +102,46 @@ static bool net_condition_test_strv(char * const *patterns, const char *string) return has_positive_rule ? match : true; } +static int net_condition_test_property(char * const *match_property, sd_device *device) { + char * const *p; + + if (strv_isempty(match_property)) + return true; + + STRV_FOREACH(p, match_property) { + _cleanup_free_ char *key = NULL; + const char *val, *dev_val; + bool invert, v; + + invert = **p == '!'; + + val = strchr(*p + invert, '='); + if (!val) + return -EINVAL; + + key = strndup(*p + invert, val - *p - invert); + if (!key) + return -ENOMEM; + + val++; + + v = device && + sd_device_get_property_value(device, key, &dev_val) >= 0 && + fnmatch(val, dev_val, 0) == 0; + + if (invert ? v : !v) + return false; + } + + return true; +} + bool net_match_config(Set *match_mac, char * const *match_paths, char * const *match_drivers, char * const *match_types, char * const *match_names, + char * const *match_property, sd_device *device, const struct ether_addr *dev_mac, const char *dev_name) { @@ -139,6 +175,9 @@ bool net_match_config(Set *match_mac, if (!net_condition_test_strv(match_names, dev_name)) return false; + if (!net_condition_test_property(match_property, device)) + return false; + return true; } @@ -296,6 +335,64 @@ int config_parse_match_ifnames( } } +int config_parse_match_property( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + const char *p = rvalue; + char ***sv = data; + bool invert; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + invert = *p == '!'; + p += invert; + + for (;;) { + _cleanup_free_ char *word = NULL, *k = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE); + if (r == 0) + return 0; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + + if (!env_assignment_is_valid(word)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid property or value, ignoring assignment: %s", word); + continue; + } + + if (invert) { + k = strjoin("!", word); + if (!k) + return log_oom(); + } else + k = TAKE_PTR(word); + + r = strv_consume(sv, TAKE_PTR(k)); + if (r < 0) + return log_oom(); + } +} + int config_parse_ifalias(const char *unit, const char *filename, unsigned line, diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index f6e69078fa8..7059c8ae458 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -19,6 +19,7 @@ bool net_match_config(Set *match_mac, char * const *match_driver, char * const *match_type, char * const *match_name, + char * const *match_property, sd_device *device, const struct ether_addr *dev_mac, const char *dev_name); @@ -28,6 +29,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs); CONFIG_PARSER_PROTOTYPE(config_parse_match_strv); CONFIG_PARSER_PROTOTYPE(config_parse_match_ifnames); +CONFIG_PARSER_PROTOTYPE(config_parse_match_property); CONFIG_PARSER_PROTOTYPE(config_parse_ifalias); CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 1b3d4ff1bae..8d0a596c87d 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -26,6 +26,7 @@ Match.Path, config_parse_match_strv, Match.Driver, config_parse_match_strv, 0, offsetof(Network, match_driver) Match.Type, config_parse_match_strv, 0, offsetof(Network, match_type) Match.Name, config_parse_match_ifnames, 0, offsetof(Network, match_name) +Match.Property, config_parse_match_property, 0, offsetof(Network, match_property) Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions) Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions) Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index c58263aeeb0..8b8311058c8 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -157,7 +157,8 @@ int network_verify(Network *network) { if (set_isempty(network->match_mac) && strv_isempty(network->match_path) && strv_isempty(network->match_driver) && strv_isempty(network->match_type) && - strv_isempty(network->match_name) && !network->conditions) + strv_isempty(network->match_name) && strv_isempty(network->match_property) && + !network->conditions) log_warning("%s: No valid settings found in the [Match] section. " "The file will match all interfaces. " "If that is intended, please add Name=* in the [Match] section.", @@ -507,6 +508,7 @@ static Network *network_free(Network *network) { strv_free(network->match_driver); strv_free(network->match_type); strv_free(network->match_name); + strv_free(network->match_property); condition_free_list(network->conditions); free(network->description); @@ -614,9 +616,8 @@ int network_get(Manager *manager, sd_device *device, assert(ret); ORDERED_HASHMAP_FOREACH(network, manager->networks, i) - if (net_match_config(network->match_mac, network->match_path, - network->match_driver, network->match_type, - network->match_name, + if (net_match_config(network->match_mac, network->match_path, network->match_driver, + network->match_type, network->match_name, network->match_property, device, address, ifname)) { if (network->match_name && device) { const char *attr; diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 7b92a544266..2b7cca8bae3 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -104,6 +104,7 @@ struct Network { char **match_driver; char **match_type; char **match_name; + char **match_property; LIST_HEAD(Condition, conditions); char *description; diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 2bdb3dcb5ec..a3d7dec88cb 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -24,6 +24,7 @@ Match.OriginalName, config_parse_match_ifnames, 0, Match.Path, config_parse_match_strv, 0, offsetof(link_config, match_path) Match.Driver, config_parse_match_strv, 0, offsetof(link_config, match_driver) Match.Type, config_parse_match_strv, 0, offsetof(link_config, match_type) +Match.Property, config_parse_match_property, 0, offsetof(link_config, match_property) Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, conditions) Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, conditions) Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, conditions) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 9dc861fc87e..9989e6ab65b 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -51,6 +51,7 @@ static void link_config_free(link_config *link) { strv_free(link->match_driver); strv_free(link->match_type); strv_free(link->match_name); + strv_free(link->match_property); condition_free_list(link->conditions); free(link->description); @@ -161,7 +162,7 @@ int link_load_one(link_config_ctx *ctx, const char *filename) { if (set_isempty(link->match_mac) && strv_isempty(link->match_path) && strv_isempty(link->match_driver) && strv_isempty(link->match_type) && - strv_isempty(link->match_name) && !link->conditions) + strv_isempty(link->match_name) && strv_isempty(link->match_property) && !link->conditions) log_warning("%s: No valid settings found in the [Match] section. " "The file will match all interfaces. " "If that is intended, please add OriginalName=* in the [Match] section.", @@ -241,7 +242,7 @@ int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) LIST_FOREACH(links, link, ctx->links) { if (net_match_config(link->match_mac, link->match_path, link->match_driver, - link->match_type, link->match_name, + link->match_type, link->match_name, link->match_property, device, NULL, NULL)) { if (link->match_name) { unsigned name_assign_type = NET_NAME_UNKNOWN; diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index a45a0e709a9..cd99cd54d45 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -40,6 +40,7 @@ struct link_config { char **match_driver; char **match_type; char **match_name; + char **match_property; LIST_HEAD(Condition, conditions); char *description; diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link index 5925e5ad12d..61155063a8b 100644 --- a/test/fuzz/fuzz-link-parser/directives.link +++ b/test/fuzz/fuzz-link-parser/directives.link @@ -4,6 +4,7 @@ OriginalName= Path= Driver= Type= +Property= Host= Virtualization= KernelCommandLine= diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 496c52336cb..26dd83d8dad 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -20,6 +20,7 @@ Driver= Architecture= Path= Name= +Property= Virtualization= KernelCommandLine= Host=