]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network,udev: add Property= setting in [Match] section
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 22 Jun 2019 16:44:13 +0000 (01:44 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 30 Jun 2019 16:24:42 +0000 (01:24 +0900)
Closes #5665.

12 files changed:
man/systemd.link.xml
man/systemd.network.xml
src/libsystemd-network/network-internal.c
src/libsystemd-network/network-internal.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h
test/fuzz/fuzz-link-parser/directives.link
test/fuzz/fuzz-network-parser/directives.network

index b1be32955e5f8aeb5e2688f4c09e38378617484d..8539422efc799356906d4460946d371cc768a07b 100644 (file)
           property <varname>DEVTYPE</varname>.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>Property=</varname></term>
+        <listitem>
+          <para>A whitespace-separated list of udev property name with its value after a equal
+          (<literal>=</literal>). 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 <literal>\</literal>.</para>
+
+          <para>Example: if a .link file has the following:
+          <programlisting>Property=ID_MODEL_ID=9999 "ID_VENDOR_FROM_DATABASE=vendor name" "KEY=with \"quotation\""</programlisting>
+          then, the .link file matches only when an interface has all the above three properties.
+          </para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>Host=</varname></term>
         <listitem>
index bad673b44e1c54bf460d9c37d51b1cc148d95761..1509a07ac12084eb918590d9310cda65dac9cd2d 100644 (file)
             with a "!", the test is inverted.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>Property=</varname></term>
+          <listitem>
+            <para>A whitespace-separated list of udev property name with its value after a equal
+            (<literal>=</literal>). 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 <literal>\</literal>.</para>
+
+            <para>Example: if a .network file has the following:
+            <programlisting>Property=ID_MODEL_ID=9999 "ID_VENDOR_FROM_DATABASE=vendor name" "KEY=with \"quotation\""</programlisting>
+            then, the .network file matches only when an interface has all the above three properties.
+            </para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><varname>Host=</varname></term>
           <listitem>
index 1f02a1e984f46a8c854b6a50d1dce276bf4b8327..1f2e5c7e65eb5c5318c41c1c7ddb1079e747494a 100644 (file)
@@ -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,
index f6e69078fa855b0267d6aa1841ba1d02f07aa804..7059c8ae45843ffcc5f444a5ea9e562007578cd0 100644 (file)
@@ -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);
 
index 1b3d4ff1bae752d7e298e6a31bbd56ed8532cd47..8d0a596c87d345e6ee5878ba17cde665ddc2de73 100644 (file)
@@ -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)
index c58263aeeb0af4f16c747faceab2c653f54ad131..8b8311058c8dd34e23cb425592ec5cbf917f3220 100644 (file)
@@ -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;
index 7b92a5442666930c01476f89844e743f580ba58e..2b7cca8bae35352a7fc23ef460217ed26b63310e 100644 (file)
@@ -104,6 +104,7 @@ struct Network {
         char **match_driver;
         char **match_type;
         char **match_name;
+        char **match_property;
         LIST_HEAD(Condition, conditions);
 
         char *description;
index 2bdb3dcb5ec596833cce3001e09bb3736c813b61..a3d7dec88cb0a1fdcd05aab7592acba198374360 100644 (file)
@@ -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)
index 9dc861fc87e9a7c3221dff801467ecfa42595e87..9989e6ab65bdc623ba0894e5974a0e19f511b84a 100644 (file)
@@ -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;
index a45a0e709a90b78f1d38099ea00cae87a50ad02d..cd99cd54d4541381963e6c2962d63d5bc0822123 100644 (file)
@@ -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;
index 5925e5ad12d046f7eecdfde0ef16297ccae384b9..61155063a8bfe137e28b545c9e58b8074cbfe8cd 100644 (file)
@@ -4,6 +4,7 @@ OriginalName=
 Path=
 Driver=
 Type=
+Property=
 Host=
 Virtualization=
 KernelCommandLine=
index 496c52336cb6d312241c306e52c48536ba4ba806..26dd83d8dad15ee7fc20c7030b20e6846764f504 100644 (file)
@@ -20,6 +20,7 @@ Driver=
 Architecture=
 Path=
 Name=
+Property=
 Virtualization=
 KernelCommandLine=
 Host=