networkd: Add bridge port priority setting #5545

Merged
merged 1 commit into from Apr 11, 2017

Conversation

Projects
None yet
4 participants
Contributor

xnox commented Mar 6, 2017

Allow setting bridge port priority in the Bridge section of the network file,
similar to e.g. bridge port path cost setting.

Contributor

xnox commented Mar 6, 2017

I've tested this successfully works as intended on Ubuntu development release (zesty 17.04)

LGTM, thanks! But this could do with a test case in test/networkd-test.py. I suppose the priority can be verified through bridge link show or sysfs?

@martinpitt martinpitt added the network label Mar 7, 2017

Contributor

xnox commented Mar 7, 2017

@martinpitt there are no bridge tests in test/networkd-test.py do you want me to create interface(s) and add them into a bridge to test these?

Contributor

martinpitt commented Mar 7, 2017

@xnox: Ah right (seems we seriously lack bridge tests then). But that should be straightforward to add, so why not start with this feature. Are you generally able to run those? Unless you use networkd, they should be safe to run on your laptop, otherwise you can run them in lxd or an autopkgtest VM (Debian's systemd package also runs it as autopkgtest).

Contributor

xnox commented Mar 7, 2017

@martinpitt I don't think the new bridge tests need to be part of this merge proposal. It seems like bridge test for networkd-test.py deserve a separate merge proposal.

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 != 0) {
@ssahani

ssahani Mar 8, 2017

Contributor

We can set 0 too .

@xnox

xnox Mar 10, 2017

Contributor

If I am reading the kernel source code right, the default priority is 32, so I could set 32 as the default value in the gperf . Then change this condition and only set priority when it's not 32? Or e.g. always set it? (But then default priority definition effectively moves from kernel to systemd)

@xnox

xnox Mar 13, 2017

Contributor

Actually using an invalid value by default makes sense. Thus I now set priority at 128, which means don't update the priority such that whichever priority the device is at, it would stick. This is similar to using 0 in the path_cost, as 0 is an invalid setting for the path_cost.

@ssahani

ssahani Mar 13, 2017

Contributor

That ok as we already have such settings #define DEFAULT_TNL_HOP_LIMIT 64.
Default priority 32768 .

sudo ip link add name bridge_name type bridge

$ ip  -d link show bridge_name | grep priority
    bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q bridge_id 8000.0:0:0:0:0:0 designated_root 8000.0:0:0:0:0:0 root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0 hello_timer    0.00 tcn_timer    0.00 topology_change_timer    0.00 gc_timer    0.00 vlan_default_pvid 1 group_fwd_mask 0 group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1 mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4 mcast_hash_max 512 mcast_last_member_count 2 mcast_startup_query_count 2 mcast_last_member_interval 100 mcast_membership_interval 26000 mcast_querier_interval 25500 mcast_query_interval 12500 mcast_query_response_interval 1000 mcast_startup_query_interval 3125 nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 addrgenmode eui64 numtxqueues 1 numrxqueues 1 
@ssahani

ssahani Mar 13, 2017

Contributor

The default as it looks 32768

ip -d link show bridge_name
8: bridge_name: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 6a:67:73:86:30:e2 brd ff:ff:ff:ff:ff:ff promiscuity 0 
    bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q bridge_id 8000.0:0:0:0:0:0 designated_root 8000.0:0:0:0:0:0 root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0 hello_timer    0.00 tcn_timer    0.00 topology_change_timer    0.00 gc_timer    0.00 vlan_default_pvid 1 group_fwd_mask 0 group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1 mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4 mcast_hash_max 512 mcast_last_member_count 2 mcast_startup_query_count 2 mcast_last_member_interval 100 mcast_membership_interval 26000 mcast_querier_interval 25500 mcast_query_interval 12500 mcast_query_response_interval 1000 mcast_startup_query_interval 3125 nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 addrgenmode eui64 numtxqueues 1 numrxqueues 1 

Please use #define DEFAULT_ABC 100

@xnox

xnox Mar 15, 2017

Contributor

@ssahani that is Bridge's priority. I'm working on Bridge Port's priority which is 32 with $ ip -d link show eth0 in the line prefixed "bridge_slave". Please see updated testsuite. Note, previously systemd would use [BridgePort] section in the .network file to define bridge port settings, but that was renamed to just [Bridge]. The sysfs file for it is /sys/class/net/port1/brport/priority and 128 is an out of range value for it.

@ssahani

ssahani Mar 15, 2017

Contributor

yes actually I mistaken thanks .

Contributor

xnox commented Mar 15, 2017

@martinpitt I have addressed your request for a testcase which is now part of this merge proposal.

Looks great, thanks for adding those! I have a couple of nitpicks, but the biggest issue is that the test seems to have a race condition because it doesn't wait for the bridge to become online?

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 != 128) {
@martinpitt

martinpitt Mar 20, 2017

Contributor

Please replace the magic 128 with a macro like LINK_PRIORITY_INVALID.

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 = 128;
@martinpitt

martinpitt Mar 20, 2017

Contributor

LINK_PRIORITY_INVALID again, please.

test/networkd-test.py
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."""
+ self.assert_link_states(**{link:'managed'})
@martinpitt

martinpitt Mar 20, 2017

Contributor

assert_link_states(link="managed") is much more natural and cleaner to read.

@xnox

xnox Mar 27, 2017

Contributor

ack

@xnox

xnox Mar 27, 2017

Contributor

actually no, I cannot use "nice" syntax. As I don't want to pass {"link":"managed"} kwarg, but i want to pass {*link: "managed"} kwarg, e.g. when read_attr("eth0", "foo") is passed, I want to call assert_link_states(eth0="managed"), not assert_link_states(link="managed").

@martinpitt

martinpitt Mar 31, 2017

Contributor

Oh I see, this needs to evaluate link. OK then.

test/networkd-test.py
+ def read_attr(self, link, attribute):
+ """Read a link attributed from the sysfs."""
+ self.assert_link_states(**{link:'managed'})
+ with open('/'.join(['/sys/class/net', link, attribute])) as f:
@martinpitt

martinpitt Mar 20, 2017

Contributor

os.path.join() is the standard Python way to build paths.

+DNS=192.168.250.1
+Address=192.168.250.33/24
+Gateway=192.168.250.1''')
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
@martinpitt

martinpitt Mar 20, 2017

Contributor

None of the tests call NETWORKD_WAIT_ONLINE, so how is that not racy?

@xnox

xnox Mar 27, 2017

Contributor

All of them do indirectly...
They call read_attr, which always calls to assert_link_states, which calls NETWORKD_WAIT_ONLINE

@martinpitt

martinpitt Mar 31, 2017

Contributor

OK. Can you please add this as a comment to read_attr()'s docstring, as it's not really evident at first sight? Thanks!

+ self.assert_link_states(
+ port1='managed',
+ port2='managed',
+ mybridge='managed')
@martinpitt

martinpitt Mar 20, 2017

Contributor

Heh, here you use the "nice" syntax :-)

Contributor

xnox commented Mar 27, 2017

Pushed updates; and rebutted one comment. The comments above were misplaced a bit. Still need to retest the updated code.

a few notes.

(yeah, it's a bit unfair, as the Cost= field doesn't get all this right either, but we really should fix that too, and get it right for Priority= from the beginning.)

(Bonus points if you fix the Cost= type chaos too btw... ;-))

man/systemd.network.xml
+ <listitem>
+ <para>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.</para>
@poettering

poettering Mar 30, 2017

Owner

please document here that this an integer, which range is permissible and whether the higher priority is referred to by higher or lower numbers.

@poettering

poettering Mar 30, 2017

Owner

(extra bonus points if you add the same for Cost=, where this was forgotten...)

src/network/networkd-network-gperf.gperf
@@ -125,6 +125,7 @@ Bridge.HairPin, config_parse_bool,
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_unsigned, 0, offsetof(Network, priority)
@poettering

poettering Mar 30, 2017

Owner

should be config_parse_uint16 I figure (see other comment)

src/network/networkd-network.h
@@ -164,6 +164,7 @@ struct Network {
bool allow_port_to_be_root;
bool unicast_flood;
unsigned cost;
+ unsigned priority;
@poettering

poettering Mar 30, 2017

Owner

hmm, you define that as "unsigned" here (i.e. 32bit), but the netlink type appears to be 16bit only. Hence please use uint16_t here too, so that we use the same and correct type all the way

@poettering

poettering Mar 30, 2017

Owner

(and yeah, "cost" appears broken too, the netlink type appears to be uint32_t, and not unsigned, and we should use uint32_t here hence too...)

@xnox

xnox Apr 11, 2017

Contributor

.... and despite netlink type uint16_t, the actual bridge port priority valid values are 0..63. And brctl manpage is a lie.

Owner

poettering commented Mar 30, 2017

patch looks pretty OK to me, except for the type issues, see comments. I'll leave the review of the python test code to @martinpitt, he's our python test guru! ;-)

Tests look good to me, aside from the read_attr docstring. Please fix that along with Lennart's review points, then this should be golden. Thanks!

test/networkd-test.py
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."""
+ self.assert_link_states(**{link:'managed'})
@martinpitt

martinpitt Mar 20, 2017

Contributor

assert_link_states(link="managed") is much more natural and cleaner to read.

@xnox

xnox Mar 27, 2017

Contributor

ack

@xnox

xnox Mar 27, 2017

Contributor

actually no, I cannot use "nice" syntax. As I don't want to pass {"link":"managed"} kwarg, but i want to pass {*link: "managed"} kwarg, e.g. when read_attr("eth0", "foo") is passed, I want to call assert_link_states(eth0="managed"), not assert_link_states(link="managed").

@martinpitt

martinpitt Mar 31, 2017

Contributor

Oh I see, this needs to evaluate link. OK then.

+DNS=192.168.250.1
+Address=192.168.250.33/24
+Gateway=192.168.250.1''')
+ subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
@martinpitt

martinpitt Mar 20, 2017

Contributor

None of the tests call NETWORKD_WAIT_ONLINE, so how is that not racy?

@xnox

xnox Mar 27, 2017

Contributor

All of them do indirectly...
They call read_attr, which always calls to assert_link_states, which calls NETWORKD_WAIT_ONLINE

@martinpitt

martinpitt Mar 31, 2017

Contributor

OK. Can you please add this as a comment to read_attr()'s docstring, as it's not really evident at first sight? Thanks!

networkd: Add bridge port priority setting
Allow setting bridge port priority in the Bridge section of the network file,
similar to e.g. port path cost setting.

Set the default to an invalid value of 128, and only set the port priority when
it's not 128. Unlike e.g. path cost, zero is a valid priority value.

Add a networkd-test.py to check that bridge port priority is correctly set.

Incidently, fix bridge port cost type and document valid ranges.
Contributor

xnox commented Apr 11, 2017

I think I have addressed all the comments from the last round of review now.

Contributor

martinpitt commented Apr 11, 2017

Thanks @xnox for the updates! @poettering's and my comments are addressed, tests are green.

@martinpitt martinpitt merged commit b56be29 into systemd:master Apr 11, 2017

5 checks passed

default Build finished.
Details
semaphoreci The build passed on Semaphore.
Details
xenial-amd64 autopkgtest finished (success)
Details
xenial-i386 autopkgtest finished (success)
Details
xenial-s390x autopkgtest finished (success)
Details

Uh, there are two issues with this...

@@ -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)
@poettering

poettering Apr 12, 2017

Owner

uh, this is problematic. You also have to change the type in the Network structure now... The config_parse_xxx() call always should match the type used. Meaning: if there's a variable of type "uint32_t" you should use config_parse_uint32 and if there's one of type "unsigned" you should use config_parse_unsigned... Hence, please fix the type here too...

Yes, on all archs we support uint32_t and unsigned are the same size, but still, we should always use the appropriate types through the entire pipeline

@xnox

xnox Apr 18, 2017

Contributor

But I have changed offsetof(Network, cost) in src/network/networkd-network.h whilst doing so. No further actions needed here, then?

-        unsigned cost;
+        uint32_t cost;
+        uint16_t priority;
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)
@poettering

poettering Apr 12, 2017

Owner

If the priority value only as a small permitted range, then please provide a smal config_parse_bridge_priority() call that does what config_parse_uint16 does, but also filters out the invalid range...

@xnox

xnox Apr 18, 2017

Contributor

I was pondering that, such that logs will print that invalid values have been specified.

Owner

poettering commented Apr 12, 2017

I opened #5729 now, given that this PR is already commited but needs fixing, and we can track this. @xnox any chance you can prep a quick fix for that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment