From 54a8423788ec3cc6240959ab9f5cdac40baf047a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 23 Jun 2019 03:18:52 +0900 Subject: [PATCH 1/6] network,udev: fix multiple invert matching lines Previously, ``` [Match] Name=!aaa Name=!bbb ``` does not work. This fixes the issue. --- src/libsystemd-network/network-internal.c | 148 ++++++++++++++++------ src/libsystemd-network/network-internal.h | 3 +- src/network/networkd-network-gperf.gperf | 8 +- src/network/test-networkd-conf.c | 2 +- src/udev/net/link-config-gperf.gperf | 8 +- 5 files changed, 122 insertions(+), 47 deletions(-) diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 9a1b2fba89b..60227cf2864 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -73,26 +73,32 @@ int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_ return 0; } -static bool net_condition_test_strv(char * const *raw_patterns, - const char *string) { - if (strv_isempty(raw_patterns)) +static bool net_condition_test_strv(char * const *patterns, const char *string) { + char * const *p; + bool match = false, has_positive_rule = false; + + if (strv_isempty(patterns)) return true; - /* If the patterns begin with "!", edit it out and negate the test. */ - if (raw_patterns[0][0] == '!') { - char **patterns; - size_t i, length; + STRV_FOREACH(p, patterns) { + const char *q = *p; + bool invert; - length = strv_length(raw_patterns) + 1; /* Include the NULL. */ - patterns = newa(char*, length); - patterns[0] = raw_patterns[0] + 1; /* Skip the "!". */ - for (i = 1; i < length; i++) - patterns[i] = raw_patterns[i]; + invert = *q == '!'; + q += invert; - return !string || !strv_fnmatch(patterns, string, 0); + if (!invert) + has_positive_rule = true; + + if (string && fnmatch(q, string, 0) == 0) { + if (invert) + return false; + else + match = true; + } } - return string && strv_fnmatch(raw_patterns, string, 0); + return has_positive_rule ? match : true; } bool net_match_config(Set *match_mac, @@ -164,7 +170,7 @@ int config_parse_net_condition(const char *unit, return 0; } -int config_parse_ifnames( +int config_parse_match_strv( const char *unit, const char *filename, unsigned line, @@ -176,7 +182,9 @@ int config_parse_ifnames( void *data, void *userdata) { + const char *p = rvalue; char ***sv = data; + bool invert; int r; assert(filename); @@ -184,30 +192,96 @@ int config_parse_ifnames( assert(rvalue); assert(data); - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&rvalue, &word, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue); - return 0; - } - if (r == 0) - break; - - if (!ifname_valid(word)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); - return 0; - } - - r = strv_push(sv, word); - if (r < 0) - return log_oom(); - - word = NULL; + if (isempty(rvalue)) { + *sv = strv_free(*sv); + return 0; } - return 0; + invert = *p == '!'; + p += invert; + + for (;;) { + _cleanup_free_ char *word = NULL, *k = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); + if (r == 0) + return 0; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + + 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_match_ifnames( + 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, 0); + if (r == 0) + return 0; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse interface name list: %s", rvalue); + return 0; + } + + if (!ifname_valid(word)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Interface name is not valid or too long, 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, diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index ebfb1c3c754..958938d3fe2 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -28,7 +28,8 @@ bool net_match_config(Set *match_mac, CONFIG_PARSER_PROTOTYPE(config_parse_net_condition); CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs); -CONFIG_PARSER_PROTOTYPE(config_parse_ifnames); +CONFIG_PARSER_PROTOTYPE(config_parse_match_strv); +CONFIG_PARSER_PROTOTYPE(config_parse_match_ifnames); 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 2986ff1123d..1b3d4ff1bae 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -22,10 +22,10 @@ struct ConfigPerfItem; %includes %% Match.MACAddress, config_parse_hwaddrs, 0, offsetof(Network, match_mac) -Match.Path, config_parse_strv, 0, offsetof(Network, match_path) -Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver) -Match.Type, config_parse_strv, 0, offsetof(Network, match_type) -Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name) +Match.Path, config_parse_match_strv, 0, offsetof(Network, match_path) +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.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/test-networkd-conf.c b/src/network/test-networkd-conf.c index dfb41f801b3..4b599c430bd 100644 --- a/src/network/test-networkd-conf.c +++ b/src/network/test-networkd-conf.c @@ -174,7 +174,7 @@ static void test_config_parse_address_one(const char *rvalue, int family, unsign assert_se(network = new0(Network, 1)); network->n_ref = 1; assert_se(network->filename = strdup("hogehoge.network")); - assert_se(config_parse_ifnames("network", "filename", 1, "section", 1, "Name", 0, "*", &network->match_name, network) == 0); + assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "*", &network->match_name, network) == 0); assert_se(config_parse_address("network", "filename", 1, "section", 1, "Address", 0, rvalue, network, network) == 0); assert_se(network->n_static_addresses == 1); assert_se(network_verify(network) >= 0); diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 9698211d1da..2bdb3dcb5ec 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -20,10 +20,10 @@ struct ConfigPerfItem; %includes %% Match.MACAddress, config_parse_hwaddrs, 0, offsetof(link_config, match_mac) -Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name) -Match.Path, config_parse_strv, 0, offsetof(link_config, match_path) -Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver) -Match.Type, config_parse_strv, 0, offsetof(link_config, match_type) +Match.OriginalName, config_parse_match_ifnames, 0, offsetof(link_config, match_name) +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.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) From b38de0e9cc1dfd60a89cf9ec998ed94e71610d50 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 23 Jun 2019 01:08:51 +0900 Subject: [PATCH 2/6] network,udev: make net_match_config() take sd_device --- src/libsystemd-network/network-internal.c | 18 +++++++++++++--- src/libsystemd-network/network-internal.h | 4 +--- src/network/networkd-network.c | 11 +--------- src/udev/net/link-config.c | 25 ++++++----------------- 4 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 60227cf2864..1f02a1e984f 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -106,12 +106,24 @@ bool net_match_config(Set *match_mac, char * const *match_drivers, char * const *match_types, char * const *match_names, + sd_device *device, const struct ether_addr *dev_mac, - const char *dev_path, - const char *dev_driver, - const char *dev_type, const char *dev_name) { + const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str; + + if (device) { + (void) sd_device_get_property_value(device, "ID_PATH", &dev_path); + (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver); + (void) sd_device_get_devtype(device, &dev_type); + + if (!dev_name) + (void) sd_device_get_sysname(device, &dev_name); + if (!dev_mac && + sd_device_get_sysattr_value(device, "address", &mac_str) >= 0) + dev_mac = ether_aton(mac_str); + } + if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac))) return false; diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 958938d3fe2..f6e69078fa8 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -19,10 +19,8 @@ bool net_match_config(Set *match_mac, char * const *match_driver, char * const *match_type, char * const *match_name, + sd_device *device, const struct ether_addr *dev_mac, - const char *dev_path, - const char *dev_driver, - const char *dev_type, const char *dev_name); CONFIG_PARSER_PROTOTYPE(config_parse_net_condition); diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 0741dfe646d..c58263aeeb0 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -607,26 +607,17 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret) { int network_get(Manager *manager, sd_device *device, const char *ifname, const struct ether_addr *address, Network **ret) { - const char *path = NULL, *driver = NULL, *devtype = NULL; Network *network; Iterator i; assert(manager); assert(ret); - if (device) { - (void) sd_device_get_property_value(device, "ID_PATH", &path); - - (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &driver); - - (void) sd_device_get_devtype(device, &devtype); - } - 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, - address, path, driver, devtype, ifname)) { + device, address, ifname)) { if (network->match_name && device) { const char *attr; uint8_t name_assign_type = NET_NAME_UNKNOWN; diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 611add9ae0d..9dc861fc87e 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -240,42 +240,29 @@ int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) assert(ret); LIST_FOREACH(links, link, ctx->links) { - const char *address = NULL, *id_path = NULL, *id_net_driver = NULL, *devtype = NULL, *sysname = NULL; - - (void) sd_device_get_sysattr_value(device, "address", &address); - (void) sd_device_get_property_value(device, "ID_PATH", &id_path); - (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &id_net_driver); - (void) sd_device_get_devtype(device, &devtype); - (void) sd_device_get_sysname(device, &sysname); - if (net_match_config(link->match_mac, link->match_path, link->match_driver, link->match_type, link->match_name, - address ? ether_aton(address) : NULL, - id_path, - id_net_driver, - devtype, - sysname)) { + device, NULL, NULL)) { if (link->match_name) { unsigned name_assign_type = NET_NAME_UNKNOWN; (void) link_unsigned_attribute(device, "name_assign_type", &name_assign_type); if (name_assign_type == NET_NAME_ENUM && !strv_contains(link->match_name, "*")) { - log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'", - link->filename, sysname); + log_device_warning(device, "Config file %s applies to device based on potentially unpredictable interface name", + link->filename); *ret = link; return 0; } else if (name_assign_type == NET_NAME_RENAMED) { - log_warning("Config file %s matches device based on renamed interface name '%s', ignoring", - link->filename, sysname); + log_device_warning(device, "Config file %s matches device based on renamed interface name, ignoring", + link->filename); continue; } } - log_debug("Config file %s applies to device %s", - link->filename, sysname); + log_device_debug(device, "Config file %s is applied", link->filename); *ret = link; return 0; From 44005bfb4eaae840e481e7a0d0b74ac9ee418302 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 23 Jun 2019 01:44:13 +0900 Subject: [PATCH 3/6] 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= From c382025adf1e5ee15455f1b2b2f0b961c48f13d7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 23 Jun 2019 04:05:51 +0900 Subject: [PATCH 4/6] man: fix wrong udev property name --- man/systemd.link.xml | 2 +- man/systemd.network.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 8539422efc7..7ea9a71107b 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -103,7 +103,7 @@ Driver= A whitespace-separated list of shell-style globs matching the driver currently bound to the - device, as exposed by the udev property DRIVER of its parent device, or if that + device, as exposed by the udev property ID_NET_DRIVER of its parent device, or if that is not set, the driver as exposed by ethtool -i of the device itself. diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 1509a07ac12..d32b60a2c9f 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -113,7 +113,7 @@ A whitespace-separated list of shell-style globs matching the driver currently bound to the device, as - exposed by the udev property DRIVER + exposed by the udev property ID_NET_DRIVER of its parent device, or if that is not set the driver as exposed by ethtool -i of the device itself. If the list is prefixed with a "!", the From f706340979a7046136714e7321242c2858f3fcd4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 27 Jun 2019 22:03:02 +0900 Subject: [PATCH 5/6] test: add tests for config_parse_match_{strv,ifnames}() --- src/network/test-networkd-conf.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c index 4b599c430bd..07ca127654f 100644 --- a/src/network/test-networkd-conf.c +++ b/src/network/test-networkd-conf.c @@ -215,6 +215,27 @@ static void test_config_parse_address(void) { test_config_parse_address_one("::1/-1", AF_INET6, 0, NULL, 0); } +static void test_config_parse_match_ifnames(void) { + _cleanup_strv_free_ char **names = NULL; + + assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "!hoge hogehoge foo", &names, NULL) == 0); + assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "!baz", &names, NULL) == 0); + assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "aaa bbb ccc", &names, NULL) == 0); + + strv_equal(names, STRV_MAKE("!hoge", "!hogehoge", "!foo", "!baz", "aaa", "bbb", "ccc")); +} + +static void test_config_parse_match_strv(void) { + _cleanup_strv_free_ char **names = NULL; + + assert_se(config_parse_match_strv("network", "filename", 1, "section", 1, "Name", 0, "!hoge hogehoge foo", &names, NULL) == 0); + assert_se(config_parse_match_strv("network", "filename", 1, "section", 1, "Name", 0, "!baz", &names, NULL) == 0); + assert_se(config_parse_match_strv("network", "filename", 1, "section", 1, "Name", 0, + "KEY=val \"KEY2=val with space\" \"KEY3=val with \\\"quotation\\\"\"", &names, NULL) == 0); + + strv_equal(names, STRV_MAKE("!hoge", "!hogehoge", "!foo", "!baz", "KEY=val", "KEY2=val with space", "KEY3=val with \"quotation\"")); +} + int main(int argc, char **argv) { log_parse_environment(); log_open(); @@ -223,6 +244,8 @@ int main(int argc, char **argv) { test_config_parse_duid_rawdata(); test_config_parse_hwaddr(); test_config_parse_address(); + test_config_parse_match_ifnames(); + test_config_parse_match_strv(); return 0; } From 6b9518a0e84b01b1cfa07d0f3b18e7764ac25085 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 30 Jun 2019 02:28:42 +0900 Subject: [PATCH 6/6] test-network: add test for Property= in [Match] section --- .../conf/13-not-match-udev-property.network | 6 ++++++ test/test-network/conf/14-match-udev-property.network | 7 +++++++ test/test-network/systemd-networkd-tests.py | 11 +++++++++++ 3 files changed, 24 insertions(+) create mode 100644 test/test-network/conf/13-not-match-udev-property.network create mode 100644 test/test-network/conf/14-match-udev-property.network diff --git a/test/test-network/conf/13-not-match-udev-property.network b/test/test-network/conf/13-not-match-udev-property.network new file mode 100644 index 00000000000..f0a530e9dbe --- /dev/null +++ b/test/test-network/conf/13-not-match-udev-property.network @@ -0,0 +1,6 @@ +[Match] +Name=dummy98 +Property=INTERFACE=hoge + +[Network] +IPv6AcceptRA=false diff --git a/test/test-network/conf/14-match-udev-property.network b/test/test-network/conf/14-match-udev-property.network new file mode 100644 index 00000000000..b632af166a0 --- /dev/null +++ b/test/test-network/conf/14-match-udev-property.network @@ -0,0 +1,7 @@ +[Match] +Name=dummy98 +Property=INTERFACE=dummy98 + +[Network] +IPv6AcceptRA=false +Address=10.1.2.3/16 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 9c274c128de..75863c39105 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -463,6 +463,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '11-dummy.netdev', '11-dummy.network', '12-dummy.netdev', + '13-not-match-udev-property.network', + '14-match-udev-property.network', '15-name-conflict-test.netdev', '21-macvlan.netdev', '21-macvtap.netdev', @@ -578,6 +580,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, '00:50:56:c0:00:28') + def test_match_udev_property(self): + copy_unit_to_networkd_unit_path('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network') + start_networkd() + wait_online(['dummy98:routable']) + + output = check_output('networkctl status dummy98') + print(output) + self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property') + def test_wait_online_any(self): copy_unit_to_networkd_unit_path('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network') start_networkd()