diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index ba10b7cd9d..40aa1b449b 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1738,8 +1738,10 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix
FirewallMark=
Specifies the iptables firewall mark value to match (a number in the range
- 1…4294967295). Optionally, the firewall mask (also a number between 1…4294967295) can be
- suffixed with a slash (/), e.g., 7/255.
+ 0…4294967295). Optionally, the firewall mask (also a number between 0…4294967295) can be
+ suffixed with a slash (/), e.g., 7/255. When the
+ mark value is non-zero and no mask is explicitly specified, all bits of the mark are
+ compared.
diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c
index 6c723325cb..7c9bd6d0ea 100644
--- a/src/network/networkd-routing-policy-rule.c
+++ b/src/network/networkd-routing-policy-rule.c
@@ -615,7 +615,7 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule
if (r < 0)
return r;
- if (rule->fwmark > 0) {
+ if (rule->fwmark > 0 || rule->fwmask > 0) {
r = sd_netlink_message_append_u32(m, FRA_FWMARK, rule->fwmark);
if (r < 0)
return r;
@@ -1315,14 +1315,12 @@ static int parse_fwmark_fwmask(const char *s, uint32_t *ret_fwmark, uint32_t *re
if (r < 0)
return r;
- if (fwmark > 0) {
- if (slash) {
- r = safe_atou32(slash + 1, &fwmask);
- if (r < 0)
- return r;
- } else
- fwmask = UINT32_MAX;
- }
+ if (slash) {
+ r = safe_atou32(slash + 1, &fwmask);
+ if (r < 0)
+ return r;
+ } else if (fwmark > 0)
+ fwmask = UINT32_MAX;
*ret_fwmark = fwmark;
*ret_fwmask = fwmask;
diff --git a/test/test-network/conf/25-routing-policy-rule-test1.network b/test/test-network/conf/25-routing-policy-rule-test1.network
index 6e67f3f105..66ea59d3a9 100644
--- a/test/test-network/conf/25-routing-policy-rule-test1.network
+++ b/test/test-network/conf/25-routing-policy-rule-test1.network
@@ -48,6 +48,24 @@ From=10.1.0.0/16
Priority=104
Table=12
+[RoutingPolicyRule]
+IncomingInterface=test1
+FirewallMark=0/1
+Priority=200
+Table=20
+
+[RoutingPolicyRule]
+IncomingInterface=test1
+FirewallMark=7/255
+Priority=201
+Table=21
+
+[RoutingPolicyRule]
+IncomingInterface=test1
+FirewallMark=9999
+Priority=202
+Table=22
+
# The four routing policy rules below intentionally have the same config
# excepts for their To= addresses. See issue #35874.
[RoutingPolicyRule]
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index fc7a67c715..dbc4589aa2 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -3890,6 +3890,18 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
print(output)
self.assertIn('104: from 10.1.0.0/16 iif test1 lookup 12 nop', output)
+ output = check_output('ip rule list iif test1 priority 200')
+ print(output)
+ self.assertIn('200: from all fwmark 0/0x1 iif test1 lookup 20', output)
+
+ output = check_output('ip rule list iif test1 priority 201')
+ print(output)
+ self.assertIn('201: from all fwmark 0x7/0xff iif test1 lookup 21', output)
+
+ output = check_output('ip rule list iif test1 priority 202')
+ print(output)
+ self.assertIn('202: from all fwmark 0x270f iif test1 lookup 22', output)
+
output = check_output('ip rule list to 192.0.2.0/26')
print(output)
self.assertIn('to 192.0.2.0/26 lookup 1001', output)