diff --git a/man/systemd.link.xml b/man/systemd.link.xml
index 162674f769..fb091b883c 100644
--- a/man/systemd.link.xml
+++ b/man/systemd.link.xml
@@ -172,6 +172,17 @@
for details.
+
+ KernelVersion=
+
+ Checks whether the kernel version (as reported by uname -r) matches a certain
+ expression (or if prefixed with the exclamation mark does not match it). See
+ ConditionKernelVersion= in
+ systemd.unit5 for
+ details.
+
+
+
Architecture=
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index 4e5d47f585..b9647a2f7a 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -232,6 +232,16 @@
+
+ KernelVersion=
+
+ Checks whether the kernel version (as reported by uname -r) matches a certain
+ expression (or if prefixed with the exclamation mark does not match it). See
+ ConditionKernelVersion= in
+ systemd.unit5 for details.
+
+
+
Architecture=
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 3466f3a3cf..f78beaa7dc 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -193,6 +193,17 @@
+
+ KernelVersion=
+
+ Checks whether the kernel version (as reported by uname -r) matches a certain
+ expression (or if prefixed with the exclamation mark does not match it). See
+ ConditionKernelVersion= in
+ systemd.unit5 for
+ details.
+
+
+
Architecture=
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 005fdea73c..19a6db7f65 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -923,6 +923,7 @@
ConditionVirtualization=
ConditionHost=
ConditionKernelCommandLine=
+ ConditionKernelVersion=
ConditionSecurity=
ConditionCapability=
ConditionACPower=
@@ -1050,6 +1051,17 @@
the exact assignment is looked for with right and left hand
side matching.
+ ConditionKernelVersion= may be used to check whether the kernel version (as reported
+ by uname -r) matches a certain expression (or if prefixed with the exclamation mark does not
+ match it). The argument must be a single string. If the string starts with one of <,
+ <=, =, >=, > a relative
+ version comparison is done, otherwise the specified string is matched with shell-style globs.
+
+ Note that using the kernel version string is an unreliable way to determine which features are supported
+ by a kernel, because of the widespread practice of backporting drivers, features, and fixes from newer upstream
+ kernels into older versions provided by distributions. Hence, this check is inherently unportable and should
+ not be used for units which may be used on different distributions.
+
ConditionSecurity= may be used to
check whether the given security module is enabled on the
system. Currently, the recognized values are
@@ -1201,6 +1213,7 @@
AssertVirtualization=
AssertHost=
AssertKernelCommandLine=
+ AssertKernelVersion=
AssertSecurity=
AssertCapability=
AssertACPower=
diff --git a/src/basic/util.c b/src/basic/util.c
index 5dd3c3014f..2a39ff2b53 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -553,3 +553,65 @@ int version(void) {
SYSTEMD_FEATURES);
return 0;
}
+
+/* This is a direct translation of str_verscmp from boot.c */
+static bool is_digit(int c) {
+ return c >= '0' && c <= '9';
+}
+
+static int c_order(int c) {
+ if (c == 0 || is_digit(c))
+ return 0;
+
+ if ((c >= 'a') && (c <= 'z'))
+ return c;
+
+ return c + 0x10000;
+}
+
+int str_verscmp(const char *s1, const char *s2) {
+ const char *os1, *os2;
+
+ assert(s1);
+ assert(s2);
+
+ os1 = s1;
+ os2 = s2;
+
+ while (*s1 || *s2) {
+ int first;
+
+ while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
+ int order;
+
+ order = c_order(*s1) - c_order(*s2);
+ if (order != 0)
+ return order;
+ s1++;
+ s2++;
+ }
+
+ while (*s1 == '0')
+ s1++;
+ while (*s2 == '0')
+ s2++;
+
+ first = 0;
+ while (is_digit(*s1) && is_digit(*s2)) {
+ if (first == 0)
+ first = *s1 - *s2;
+ s1++;
+ s2++;
+ }
+
+ if (is_digit(*s1))
+ return 1;
+ if (is_digit(*s2))
+ return -1;
+
+ if (first != 0)
+ return first;
+ }
+
+ return strcmp(os1, os2);
+}
diff --git a/src/basic/util.h b/src/basic/util.h
index 8cd78171bc..20181ab917 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -189,3 +189,5 @@ uint64_t system_tasks_max_scale(uint64_t v, uint64_t max);
int update_reboot_parameter_and_warn(const char *param);
int version(void);
+
+int str_verscmp(const char *s1, const char *s2);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 5604312bc5..549c7eb4d6 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -241,6 +241,7 @@ Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_F
Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, conditions)
Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, conditions)
Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions)
+Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, conditions)
Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, conditions)
Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions)
Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions)
@@ -263,6 +264,7 @@ Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_F
Unit.AssertNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, asserts)
Unit.AssertFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, asserts)
Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts)
+Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, asserts)
Unit.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, asserts)
Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts)
Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts)
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index e48b7d22dd..c20e9fca35 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -116,7 +116,8 @@ bool net_match_config(const struct ether_addr *match_mac,
char * const *match_names,
Condition *match_host,
Condition *match_virt,
- Condition *match_kernel,
+ Condition *match_kernel_cmdline,
+ Condition *match_kernel_version,
Condition *match_arch,
const struct ether_addr *dev_mac,
const char *dev_path,
@@ -131,7 +132,10 @@ bool net_match_config(const struct ether_addr *match_mac,
if (match_virt && condition_test(match_virt) <= 0)
return false;
- if (match_kernel && condition_test(match_kernel) <= 0)
+ if (match_kernel_cmdline && condition_test(match_kernel_cmdline) <= 0)
+ return false;
+
+ if (match_kernel_version && condition_test(match_kernel_version) <= 0)
return false;
if (match_arch && condition_test(match_arch) <= 0)
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index a54adac602..4e69f1a598 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -37,7 +37,8 @@ bool net_match_config(const struct ether_addr *match_mac,
char * const *match_name,
Condition *match_host,
Condition *match_virt,
- Condition *match_kernel,
+ Condition *match_kernel_cmdline,
+ Condition *match_kernel_version,
Condition *match_arch,
const struct ether_addr *dev_mac,
const char *dev_path,
diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf
index 3aeba06ee0..03b8661957 100644
--- a/src/network/netdev/netdev-gperf.gperf
+++ b/src/network/netdev/netdev-gperf.gperf
@@ -33,7 +33,8 @@ struct ConfigPerfItem;
%%
Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host)
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel_cmdline)
+Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, match_kernel_version)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch)
NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname)
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index 7afe30f7fb..7cf110672f 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -149,7 +149,8 @@ static void netdev_free(NetDev *netdev) {
condition_free_list(netdev->match_host);
condition_free_list(netdev->match_virt);
- condition_free_list(netdev->match_kernel);
+ condition_free_list(netdev->match_kernel_cmdline);
+ condition_free_list(netdev->match_kernel_version);
condition_free_list(netdev->match_arch);
if (NETDEV_VTABLE(netdev) &&
@@ -642,7 +643,8 @@ static int netdev_load_one(Manager *manager, const char *filename) {
/* skip out early if configuration does not match the environment */
if (net_match_config(NULL, NULL, NULL, NULL, NULL,
netdev_raw->match_host, netdev_raw->match_virt,
- netdev_raw->match_kernel, netdev_raw->match_arch,
+ netdev_raw->match_kernel_cmdline, netdev_raw->match_kernel_version,
+ netdev_raw->match_arch,
NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
return 0;
diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h
index 24915b2b04..507b86a078 100644
--- a/src/network/netdev/netdev.h
+++ b/src/network/netdev/netdev.h
@@ -93,7 +93,8 @@ typedef struct NetDev {
Condition *match_host;
Condition *match_virt;
- Condition *match_kernel;
+ Condition *match_kernel_cmdline;
+ Condition *match_kernel_version;
Condition *match_arch;
NetDevState state;
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 57a96aff94..cb0c01fbe7 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -27,7 +27,8 @@ Match.Type, config_parse_strv,
Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name)
Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, match_host)
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel_cmdline)
+Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, match_kernel_version)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch)
Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 8e37a0a229..b037fc67e3 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -431,7 +431,8 @@ void network_free(Network *network) {
condition_free_list(network->match_host);
condition_free_list(network->match_virt);
- condition_free_list(network->match_kernel);
+ condition_free_list(network->match_kernel_cmdline);
+ condition_free_list(network->match_kernel_version);
condition_free_list(network->match_arch);
free(network->dhcp_server_timezone);
@@ -485,8 +486,8 @@ int network_get(Manager *manager, struct udev_device *device,
if (net_match_config(network->match_mac, network->match_path,
network->match_driver, network->match_type,
network->match_name, network->match_host,
- network->match_virt, network->match_kernel,
- network->match_arch,
+ network->match_virt, network->match_kernel_cmdline,
+ network->match_kernel_version, network->match_arch,
address, path, parent_driver, driver,
devtype, ifname)) {
if (network->match_name && device) {
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 49c62654b6..702aa7692a 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -112,7 +112,8 @@ struct Network {
Condition *match_host;
Condition *match_virt;
- Condition *match_kernel;
+ Condition *match_kernel_cmdline;
+ Condition *match_kernel_version;
Condition *match_arch;
char *description;
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index e97b5cdb79..4f255f620b 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -203,67 +203,8 @@ int boot_loader_read_conf(const char *path, BootConfig *config) {
return 0;
}
-/* This is a direct translation of str_verscmp from boot.c */
-static bool is_digit(int c) {
- return c >= '0' && c <= '9';
-}
-
-static int c_order(int c) {
- if (c == '\0')
- return 0;
- if (is_digit(c))
- return 0;
- else if ((c >= 'a') && (c <= 'z'))
- return c;
- else
- return c + 0x10000;
-}
-
-static int str_verscmp(const char *s1, const char *s2) {
- const char *os1 = s1;
- const char *os2 = s2;
-
- while (*s1 || *s2) {
- int first;
-
- while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
- int order;
-
- order = c_order(*s1) - c_order(*s2);
- if (order)
- return order;
- s1++;
- s2++;
- }
-
- while (*s1 == '0')
- s1++;
- while (*s2 == '0')
- s2++;
-
- first = 0;
- while (is_digit(*s1) && is_digit(*s2)) {
- if (first == 0)
- first = *s1 - *s2;
- s1++;
- s2++;
- }
-
- if (is_digit(*s1))
- return 1;
- if (is_digit(*s2))
- return -1;
-
- if (first != 0)
- return first;
- }
-
- return strcmp(os1, os2);
-}
-
static int boot_entry_compare(const void *a, const void *b) {
- const BootEntry *aa = a;
- const BootEntry *bb = b;
+ const BootEntry *aa = a, *bb = b;
return str_verscmp(aa->filename, bb->filename);
}
diff --git a/src/shared/condition.c b/src/shared/condition.c
index d4bbaf3c65..7a825c3973 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
#include
#include
@@ -143,6 +144,70 @@ static int condition_test_kernel_command_line(Condition *c) {
return false;
}
+static int condition_test_kernel_version(Condition *c) {
+ enum {
+ /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
+ * should be listed first. */
+ LOWER_OR_EQUAL,
+ GREATER_OR_EQUAL,
+ LOWER,
+ GREATER,
+ EQUAL,
+ _ORDER_MAX,
+ };
+
+ static const char *const prefix[_ORDER_MAX] = {
+ [LOWER_OR_EQUAL] = "<=",
+ [GREATER_OR_EQUAL] = ">=",
+ [LOWER] = "<",
+ [GREATER] = ">",
+ [EQUAL] = "=",
+ };
+ const char *p = NULL;
+ struct utsname u;
+ size_t i;
+ int k;
+
+ assert(c);
+ assert(c->parameter);
+ assert(c->type == CONDITION_KERNEL_VERSION);
+
+ assert_se(uname(&u) >= 0);
+
+ for (i = 0; i < _ORDER_MAX; i++) {
+ p = startswith(c->parameter, prefix[i]);
+ if (p)
+ break;
+ }
+
+ /* No prefix? Then treat as glob string */
+ if (!p)
+ return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0;
+
+ k = str_verscmp(u.release, skip_leading_chars(p, NULL));
+
+ switch (i) {
+
+ case LOWER:
+ return k < 0;
+
+ case LOWER_OR_EQUAL:
+ return k <= 0;
+
+ case EQUAL:
+ return k == 0;
+
+ case GREATER_OR_EQUAL:
+ return k >= 0;
+
+ case GREATER:
+ return k > 0;
+
+ default:
+ assert_not_reached("Can't compare");
+ }
+}
+
static int condition_test_user(Condition *c) {
uid_t id;
int r;
@@ -552,6 +617,7 @@ int condition_test(Condition *c) {
[CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
[CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
[CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
+ [CONDITION_KERNEL_VERSION] = condition_test_kernel_version,
[CONDITION_VIRTUALIZATION] = condition_test_virtualization,
[CONDITION_SECURITY] = condition_test_security,
[CONDITION_CAPABILITY] = condition_test_capability,
@@ -612,6 +678,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
[CONDITION_HOST] = "ConditionHost",
[CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
+ [CONDITION_KERNEL_VERSION] = "ConditionKernelVersion",
[CONDITION_SECURITY] = "ConditionSecurity",
[CONDITION_CAPABILITY] = "ConditionCapability",
[CONDITION_AC_POWER] = "ConditionACPower",
@@ -639,6 +706,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_VIRTUALIZATION] = "AssertVirtualization",
[CONDITION_HOST] = "AssertHost",
[CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
+ [CONDITION_KERNEL_VERSION] = "AssertKernelVersion",
[CONDITION_SECURITY] = "AssertSecurity",
[CONDITION_CAPABILITY] = "AssertCapability",
[CONDITION_AC_POWER] = "AssertACPower",
diff --git a/src/shared/condition.h b/src/shared/condition.h
index 715866be70..98af2daaee 100644
--- a/src/shared/condition.h
+++ b/src/shared/condition.h
@@ -31,6 +31,7 @@ typedef enum ConditionType {
CONDITION_VIRTUALIZATION,
CONDITION_HOST,
CONDITION_KERNEL_COMMAND_LINE,
+ CONDITION_KERNEL_VERSION,
CONDITION_SECURITY,
CONDITION_CAPABILITY,
CONDITION_AC_POWER,
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index bf455aac89..8323a66ad3 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -20,6 +20,7 @@
#include
#include
+#include
#include
#include "sd-id128.h"
@@ -38,10 +39,11 @@
#include "selinux-util.h"
#include "set.h"
#include "smack-util.h"
+#include "string-util.h"
#include "strv.h"
-#include "virt.h"
-#include "util.h"
#include "user-util.h"
+#include "util.h"
+#include "virt.h"
static void test_condition_test_path(void) {
Condition *condition;
@@ -298,6 +300,126 @@ static void test_condition_test_kernel_command_line(void) {
condition_free(condition);
}
+static void test_condition_test_kernel_version(void) {
+ Condition *condition;
+ struct utsname u;
+ const char *v;
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "*thisreallyshouldntbeinthekernelversion*", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "*", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ assert_se(uname(&u) >= 0);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ strshorten(u.release, 4);
+ strcpy(strchr(u.release, 0), "*");
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ /* 0.1.2 would be a very very very old kernel */
+ condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, ">= 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "< 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "<= 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "= 0.1.2", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ /* 4711.8.15 is a very very very future kernel */
+ condition = condition_new(CONDITION_KERNEL_VERSION, "< 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "<= 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "= 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "> 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, ">= 4711.8.15", false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ assert_se(uname(&u) >= 0);
+
+ v = strjoina(">=", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("= ", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("<=", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("> ", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+
+ v = strjoina("< ", u.release);
+ condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
+ assert_se(condition);
+ assert_se(!condition_test(condition));
+ condition_free(condition);
+}
+
static void test_condition_test_null(void) {
Condition *condition;
@@ -556,6 +678,7 @@ int main(int argc, char *argv[]) {
test_condition_test_host();
test_condition_test_architecture();
test_condition_test_kernel_command_line();
+ test_condition_test_kernel_version();
test_condition_test_null();
test_condition_test_security();
test_condition_test_virtualization();
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index 85f0a0625b..5cb126d870 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -26,7 +26,8 @@ Match.Driver, config_parse_strv, 0,
Match.Type, config_parse_strv, 0, offsetof(link_config, match_type)
Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host)
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel_cmdline)
+Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(link_config, match_kernel_version)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch)
Link.Description, config_parse_string, 0, offsetof(link_config, description)
Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy)
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index 92d41a6e95..a4368f088d 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -77,7 +77,8 @@ static void link_config_free(link_config *link) {
free(link->match_name);
free(link->match_host);
free(link->match_virt);
- free(link->match_kernel);
+ free(link->match_kernel_cmdline);
+ free(link->match_kernel_version);
free(link->match_arch);
free(link->description);
@@ -248,7 +249,8 @@ int link_config_get(link_config_ctx *ctx, struct udev_device *device,
if (net_match_config(link->match_mac, link->match_path, link->match_driver,
link->match_type, link->match_name, link->match_host,
- link->match_virt, link->match_kernel, link->match_arch,
+ link->match_virt, link->match_kernel_cmdline,
+ link->match_kernel_version, link->match_arch,
attr_value ? ether_aton(attr_value) : NULL,
udev_device_get_property_value(device, "ID_PATH"),
udev_device_get_driver(udev_device_get_parent(device)),
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index a413251bd0..dabc3eff8f 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -58,7 +58,8 @@ struct link_config {
char **match_name;
Condition *match_host;
Condition *match_virt;
- Condition *match_kernel;
+ Condition *match_kernel_cmdline;
+ Condition *match_kernel_version;
Condition *match_arch;
char *description;