Closes #5665.
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>
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>
#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"
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) {
if (!net_condition_test_strv(match_names, dev_name))
return false;
+ if (!net_condition_test_property(match_property, device))
+ return false;
+
return true;
}
}
}
+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,
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);
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);
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)
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.",
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);
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;
char **match_driver;
char **match_type;
char **match_name;
+ char **match_property;
LIST_HEAD(Condition, conditions);
char *description;
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)
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);
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.",
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;
char **match_driver;
char **match_type;
char **match_name;
+ char **match_property;
LIST_HEAD(Condition, conditions);
char *description;
Path=
Driver=
Type=
+Property=
Host=
Virtualization=
KernelCommandLine=
Architecture=
Path=
Name=
+Property=
Virtualization=
KernelCommandLine=
Host=