diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 61bed263e5..971dee338f 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1257,7 +1257,18 @@
Sets the "cost" of sending packets of this interface.
Each port in a bridge may have a different speed and the cost
is used to decide which link to use. Faster interfaces
- should have lower costs.
+ should have lower costs. It is an interger value between 1 and
+ 65535.
+
+
+
+ Priority=
+
+ Sets the "priority" of sending packets on this interface.
+ Each port in a bridge may have a different priority which is used
+ to decide which link to use. Lower value means higher priority.
+ It is an interger value between 0 to 63. Networkd does not set any
+ default, meaning the kernel default value of 32 is used.
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 0c1229336b..1797f144b6 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1325,6 +1325,11 @@ static int link_set_bridge(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m");
}
+ if (link->network->priority != LINK_BRIDGE_PORT_PRIORITY_INVALID) {
+ r = sd_netlink_message_append_u16(req, IFLA_BRPORT_PRIORITY, link->network->priority);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PRIORITY attribute: %m");
+ }
r = sd_netlink_message_close_container(req);
if (r < 0)
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index e6190fbe57..010b38248a 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -33,6 +33,8 @@
#include "list.h"
#include "set.h"
+#define LINK_BRIDGE_PORT_PRIORITY_INVALID 128
+
typedef enum LinkState {
LINK_STATE_PENDING,
LINK_STATE_ENSLAVING,
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 68052ba544..9658978651 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -119,12 +119,13 @@ DHCPServer.EmitTimezone, config_parse_bool,
DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone)
DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset)
DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size)
-Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost)
+Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost)
Bridge.UseBPDU, config_parse_bool, 0, offsetof(Network, use_bpdu)
Bridge.HairPin, config_parse_bool, 0, offsetof(Network, hairpin)
Bridge.FastLeave, config_parse_bool, 0, offsetof(Network, fast_leave)
Bridge.AllowPortToBeRoot, config_parse_bool, 0, offsetof(Network, allow_port_to_be_root)
Bridge.UnicastFlood, config_parse_bool, 0, offsetof(Network, unicast_flood)
+Bridge.Priority, config_parse_uint16, 0, offsetof(Network, priority)
BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0
BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0
BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index fac42d8478..dd29b4ca48 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -165,6 +165,7 @@ static int network_load_one(Manager *manager, const char *filename) {
network->use_bpdu = true;
network->allow_port_to_be_root = true;
network->unicast_flood = true;
+ network->priority = LINK_BRIDGE_PORT_PRIORITY_INVALID;
network->lldp_mode = LLDP_MODE_ROUTERS_ONLY;
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 4ce066a764..d6f418d521 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -163,7 +163,8 @@ struct Network {
bool fast_leave;
bool allow_port_to_be_root;
bool unicast_flood;
- unsigned cost;
+ uint32_t cost;
+ uint16_t priority;
bool use_br_vlan;
uint16_t pvid;
diff --git a/test/networkd-test.py b/test/networkd-test.py
index e0dddeb053..eee8b65ec0 100755
--- a/test/networkd-test.py
+++ b/test/networkd-test.py
@@ -96,10 +96,20 @@ class NetworkdTestingUtilities:
dropin_path = os.path.join(dropin_dir, "%s.conf" % dropin_name)
os.makedirs(dropin_dir, exist_ok=True)
+ self.addCleanup(os.rmdir, dropin_dir)
with open(dropin_path, 'w') as dropin:
dropin.write(contents)
self.addCleanup(os.remove, dropin_path)
+ def read_attr(self, link, attribute):
+ """Read a link attributed from the sysfs."""
+ # Note we we don't want to check if interface `link' is managed, we
+ # want to evaluate link variable and pass the value of the link to
+ # assert_link_states e.g. eth0=managed.
+ self.assert_link_states(**{link:'managed'})
+ with open(os.path.join('/sys/class/net', link, attribute)) as f:
+ return f.readline().strip()
+
def assert_link_states(self, **kwargs):
"""Match networkctl link states to the given ones.
@@ -140,6 +150,74 @@ class NetworkdTestingUtilities:
self.fail("Missing links in status output: %s" % interfaces)
+class BridgeTest(NetworkdTestingUtilities, unittest.TestCase):
+ """Provide common methods for testing networkd against servers."""
+
+ def setUp(self):
+ self.write_network('port1.netdev', '''\
+[NetDev]
+Name=port1
+Kind=dummy
+MACAddress=12:34:56:78:9a:bc''')
+ self.write_network('port2.netdev', '''\
+[NetDev]
+Name=port2
+Kind=dummy
+MACAddress=12:34:56:78:9a:bd''')
+ self.write_network('mybridge.netdev', '''\
+[NetDev]
+Name=mybridge
+Kind=bridge''')
+ self.write_network('port1.network', '''\
+[Match]
+Name=port1
+[Network]
+Bridge=mybridge''')
+ self.write_network('port2.network', '''\
+[Match]
+Name=port2
+[Network]
+Bridge=mybridge''')
+ self.write_network('mybridge.network', '''\
+[Match]
+Name=mybridge
+[Network]
+DNS=192.168.250.1
+Address=192.168.250.33/24
+Gateway=192.168.250.1''')
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+
+ def tearDown(self):
+ subprocess.check_call(['systemctl', 'stop', 'systemd-networkd'])
+ subprocess.check_call(['ip', 'link', 'del', 'mybridge'])
+ subprocess.check_call(['ip', 'link', 'del', 'port1'])
+ subprocess.check_call(['ip', 'link', 'del', 'port2'])
+
+ def test_bridge_init(self):
+ self.assert_link_states(
+ port1='managed',
+ port2='managed',
+ mybridge='managed')
+
+ def test_bridge_port_priority(self):
+ self.assertEqual(self.read_attr('port1', 'brport/priority'), '32')
+ self.write_network_dropin('port1.network', 'priority', '''\
+[Bridge]
+Priority=28
+''')
+ subprocess.check_call(['systemctl', 'restart', 'systemd-networkd'])
+ self.assertEqual(self.read_attr('port1', 'brport/priority'), '28')
+
+ def test_bridge_port_priority_set_zero(self):
+ """It should be possible to set the bridge port priority to 0"""
+ self.assertEqual(self.read_attr('port2', 'brport/priority'), '32')
+ self.write_network_dropin('port2.network', 'priority', '''\
+[Bridge]
+Priority=0
+''')
+ subprocess.check_call(['systemctl', 'restart', 'systemd-networkd'])
+ self.assertEqual(self.read_attr('port2', 'brport/priority'), '0')
+
class ClientTestBase(NetworkdTestingUtilities):
"""Provide common methods for testing networkd against servers."""