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."""