From f2bfcdb94ab5201c3d1aaa0d9c01b9dfae0d6d60 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 9 Feb 2019 22:40:05 +0900 Subject: [PATCH 1/8] network: disable addressing on bond slave interface --- src/network/networkd-link.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index ae3b8cc27f..4818362876 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -51,6 +51,9 @@ static bool link_dhcp6_enabled(Link *link) { if (!link->network) return false; + if (link->network->bond) + return false; + return link->network->dhcp & ADDRESS_FAMILY_IPV6; } @@ -63,6 +66,9 @@ static bool link_dhcp4_enabled(Link *link) { if (!link->network) return false; + if (link->network->bond) + return false; + return link->network->dhcp & ADDRESS_FAMILY_IPV4; } @@ -75,6 +81,9 @@ static bool link_dhcp4_server_enabled(Link *link) { if (!link->network) return false; + if (link->network->bond) + return false; + return link->network->dhcp_server; } @@ -90,6 +99,9 @@ static bool link_ipv4ll_enabled(Link *link) { if (streq_ptr(link->kind, "wireguard")) return false; + if (link->network->bond) + return false; + return link->network->link_local & ADDRESS_FAMILY_IPV4; } @@ -108,6 +120,9 @@ static bool link_ipv6ll_enabled(Link *link) { if (streq_ptr(link->kind, "wireguard")) return false; + if (link->network->bond) + return false; + return link->network->link_local & ADDRESS_FAMILY_IPV6; } @@ -117,7 +132,7 @@ static bool link_ipv6_enabled(Link *link) { if (!socket_ipv6_is_supported()) return false; - if (link->network->bridge) + if (link->network->bridge || link->network->bond) return false; /* DHCPv6 client will not be started if no IPv6 link-local address is configured. */ From 7fcee284172f56dcc688f224543b8b5d994ea8c6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 9 Feb 2019 22:41:13 +0900 Subject: [PATCH 2/8] network: introduce specific netlink async handler for link_set_bond() This also rename link_bond_set() to link_set_bond(). --- src/network/networkd-link.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 4818362876..adc33b3865 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1535,7 +1535,26 @@ static int link_set_bridge(Link *link) { return r; } -static int link_bond_set(Link *link) { +static int link_set_bond_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + assert(link->ifname); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set bonding interface: %m"); + return 1; + } + + return 1; +} + +static int link_set_bond(Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -1578,7 +1597,7 @@ static int link_bond_set(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, req, set_flags_handler, + r = netlink_call_async(link->manager->rtnl, NULL, req, link_set_bond_handler, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); @@ -2389,7 +2408,7 @@ static int link_joined(Link *link) { } if (link->network->bond) { - r = link_bond_set(link); + r = link_set_bond(link); if (r < 0) log_link_error_errno(link, r, "Could not set bond message: %m"); } From 14153d1b6e8725f995d9ad41e48daf67b140bf6a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 15 Feb 2019 12:29:59 +0900 Subject: [PATCH 3/8] network: introduce new operational state 'enslaved' If an interface has IFF_SLAVE flag, then its operational state becomes not in 'degraded' or 'carrier', but the new 'enslaved' state. --- src/network/networkd-link.c | 6 ++++++ src/network/networkd-link.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index adc33b3865..22905e7079 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -301,6 +301,7 @@ static int link_enable_ipv6(Link *link) { void link_update_operstate(Link *link) { LinkOperationalState operstate; + assert(link); if (link->kernel_operstate == IF_OPER_DORMANT) @@ -342,6 +343,10 @@ void link_update_operstate(Link *link) { else operstate = LINK_OPERSTATE_OFF; + if (IN_SET(operstate, LINK_OPERSTATE_DEGRADED, LINK_OPERSTATE_CARRIER) && + link->flags & IFF_SLAVE) + operstate = LINK_OPERSTATE_ENSLAVED; + if (link->operstate != operstate) { link->operstate = operstate; link_send_changed(link, "OperationalState", NULL); @@ -4215,6 +4220,7 @@ static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = { [LINK_OPERSTATE_DORMANT] = "dormant", [LINK_OPERSTATE_CARRIER] = "carrier", [LINK_OPERSTATE_DEGRADED] = "degraded", + [LINK_OPERSTATE_ENSLAVED] = "enslaved", [LINK_OPERSTATE_ROUTABLE] = "routable", }; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index dcb1ea68dd..2bee3eb500 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -34,6 +34,7 @@ typedef enum LinkOperationalState { LINK_OPERSTATE_DORMANT, LINK_OPERSTATE_CARRIER, LINK_OPERSTATE_DEGRADED, + LINK_OPERSTATE_ENSLAVED, LINK_OPERSTATE_ROUTABLE, _LINK_OPERSTATE_MAX, _LINK_OPERSTATE_INVALID = -1 From 959f65d32ec15cf84afe3efff1a18b0987b56c60 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 13 Feb 2019 06:32:48 +0900 Subject: [PATCH 4/8] network: make bond master follow operstates of slaves If one of bond slaves is in off, no-carrier, or dormant, then bond master is set to degraded. --- src/network/networkd-address.c | 4 +-- src/network/networkd-link.c | 56 ++++++++++++++++++++++++++++++++-- src/network/networkd-link.h | 3 +- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 06e2662cdd..aa827d6ba6 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -351,7 +351,7 @@ int address_update( address->scope = scope; address->cinfo = *cinfo; - link_update_operstate(address->link); + link_update_operstate(address->link, true); link_check_ready(address->link); if (!ready && @@ -380,7 +380,7 @@ int address_drop(Address *address) { address_release(address); address_free(address); - link_update_operstate(link); + link_update_operstate(link, true); if (link && !ready) link_check_ready(link); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 22905e7079..acb4943b84 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -299,7 +299,7 @@ static int link_enable_ipv6(Link *link) { return 0; } -void link_update_operstate(Link *link) { +void link_update_operstate(Link *link, bool also_update_bond_master) { LinkOperationalState operstate; assert(link); @@ -347,11 +347,34 @@ void link_update_operstate(Link *link) { link->flags & IFF_SLAVE) operstate = LINK_OPERSTATE_ENSLAVED; + if (IN_SET(operstate, LINK_OPERSTATE_CARRIER, LINK_OPERSTATE_ENSLAVED, LINK_OPERSTATE_ROUTABLE) && + !hashmap_isempty(link->bond_slaves)) { + Iterator i; + Link *slave; + + HASHMAP_FOREACH(slave, link->bond_slaves, i) { + link_update_operstate(slave, false); + + if (IN_SET(slave->operstate, + LINK_OPERSTATE_OFF, LINK_OPERSTATE_NO_CARRIER, LINK_OPERSTATE_DORMANT)) + operstate = LINK_OPERSTATE_DEGRADED; + } + } + if (link->operstate != operstate) { link->operstate = operstate; link_send_changed(link, "OperationalState", NULL); link_dirty(link); } + + if (also_update_bond_master && link->network && link->network->bond) { + Link *master; + + if (link_get(link->manager, link->network->bond->ifindex, &master) < 0) + return; + + link_update_operstate(master, true); + } } #define FLAG_STRING(string, flag, old, new) \ @@ -426,7 +449,7 @@ static int link_update_flags(Link *link, sd_netlink_message *m) { link->flags = flags; link->kernel_operstate = operstate; - link_update_operstate(link); + link_update_operstate(link, true); return 0; } @@ -607,6 +630,8 @@ static Link *link_free(Link *link) { hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex)); hashmap_free(link->bound_by_links); + hashmap_free(link->bond_slaves); + return mfree(link); } @@ -1612,6 +1637,29 @@ static int link_set_bond(Link *link) { return r; } +static int link_append_bond_slave(Link *link) { + Link *master; + int r; + + assert(link); + assert(link->network); + assert(link->network->bond); + + r = link_get(link->manager, link->network->bond->ifindex, &master); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&master->bond_slaves, NULL); + if (r < 0) + return r; + + r = hashmap_put(master->bond_slaves, INT_TO_PTR(link->ifindex), link); + if (r < 0) + return r; + + return 0; +} + static int link_lldp_save(Link *link) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -2416,6 +2464,10 @@ static int link_joined(Link *link) { r = link_set_bond(link); if (r < 0) log_link_error_errno(link, r, "Could not set bond message: %m"); + + r = link_append_bond_slave(link); + if (r < 0) + log_link_error_errno(link, r, "Failed to add to bond master's slave list: %m"); } if (link->network->use_br_vlan && diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 2bee3eb500..9a43986a32 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -129,6 +129,7 @@ typedef struct Link { Hashmap *bound_by_links; Hashmap *bound_to_links; + Hashmap *bond_slaves; } Link; typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*); @@ -151,7 +152,7 @@ int link_initialized(Link *link, sd_device *device); void link_check_ready(Link *link); -void link_update_operstate(Link *link); +void link_update_operstate(Link *link, bool also_update_bond_master); int link_update(Link *link, sd_netlink_message *message); void link_dirty(Link *link); From 85323805be46a0723a9f02c374c688e372d1c66c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 10 Feb 2019 03:49:12 +0900 Subject: [PATCH 5/8] networkctl: make enslaved operstate green --- src/network/networkctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 20edac4679..2803f5210b 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -65,7 +65,7 @@ static void operational_state_to_color(const char *state, const char **on, const assert(on); assert(off); - if (streq_ptr(state, "routable")) { + if (STRPTR_IN_SET(state, "routable", "enslaved")) { *on = ansi_highlight_green(); *off = ansi_normal(); } else if (streq_ptr(state, "degraded")) { From 806c86adb2a5e18e2edafce8f0f0fae011335527 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 10 Feb 2019 03:50:48 +0900 Subject: [PATCH 6/8] man: mention new enslaved operational state --- man/networkctl.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/man/networkctl.xml b/man/networkctl.xml index 7b419d6f72..360eb1ce77 100644 --- a/man/networkctl.xml +++ b/man/networkctl.xml @@ -127,6 +127,12 @@ the link has carrier and addresses valid on the local link configured + + enslaved + + the link has carrier and is enslaved to other network interfaces + + routable From 250860e423eb6f8f5f528145b77f989b9373e2e4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 13 Feb 2019 06:46:26 +0900 Subject: [PATCH 7/8] man: mention that bond master follows slave operstates --- man/networkctl.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/man/networkctl.xml b/man/networkctl.xml index 360eb1ce77..955d199949 100644 --- a/man/networkctl.xml +++ b/man/networkctl.xml @@ -118,13 +118,15 @@ carrier - the link has a carrier + the link has a carrier, or for bond master, all bonding slave network interfaces are + enslaved to the master. degraded - the link has carrier and addresses valid on the local link configured + the link has carrier and addresses valid on the local link configured, or for bond + master, one of the bonding slave network interfaces is in off, no-carrier, or dormant From c3a8853f6e2da0677bfe962fc2fd828e7bce6c9a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 9 Feb 2019 22:56:42 +0900 Subject: [PATCH 8/8] test-network: add tests for bonding --- test/test-network/conf/bond99.network | 6 ++ test/test-network/conf/dhcp-server.network | 1 + test/test-network/conf/veth-bond.network | 6 ++ test/test-network/conf/vlan6.netdev | 7 ++ test/test-network/conf/vlan6.network | 6 ++ test/test-network/systemd-networkd-tests.py | 74 +++++++++++++++++++++ 6 files changed, 100 insertions(+) create mode 100644 test/test-network/conf/bond99.network create mode 100644 test/test-network/conf/veth-bond.network create mode 100644 test/test-network/conf/vlan6.netdev create mode 100644 test/test-network/conf/vlan6.network diff --git a/test/test-network/conf/bond99.network b/test/test-network/conf/bond99.network new file mode 100644 index 0000000000..9c18eeb80c --- /dev/null +++ b/test/test-network/conf/bond99.network @@ -0,0 +1,6 @@ +[Match] +Name=bond99 + +[Network] +IPv6AcceptRA=no +DHCP=yes diff --git a/test/test-network/conf/dhcp-server.network b/test/test-network/conf/dhcp-server.network index 9e49691a9b..439258a539 100644 --- a/test/test-network/conf/dhcp-server.network +++ b/test/test-network/conf/dhcp-server.network @@ -3,6 +3,7 @@ Name=veth-peer [Network] Address=192.168.5.1/24 +IPv6AcceptRA=false DHCPServer=yes [DHCPServer] diff --git a/test/test-network/conf/veth-bond.network b/test/test-network/conf/veth-bond.network new file mode 100644 index 0000000000..2095b7ac26 --- /dev/null +++ b/test/test-network/conf/veth-bond.network @@ -0,0 +1,6 @@ +[Match] +Name=veth99 + +[Network] +Bond=bond99 +IPv6AcceptRA=false diff --git a/test/test-network/conf/vlan6.netdev b/test/test-network/conf/vlan6.netdev new file mode 100644 index 0000000000..310be91fa5 --- /dev/null +++ b/test/test-network/conf/vlan6.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=vlan6 +Kind=vlan +MTUBytes=1500 + +[VLAN] +Id=6 diff --git a/test/test-network/conf/vlan6.network b/test/test-network/conf/vlan6.network new file mode 100644 index 0000000000..64e9db520e --- /dev/null +++ b/test/test-network/conf/vlan6.network @@ -0,0 +1,6 @@ +[Match] +Name=vlan6 + +[Network] +IPv6AcceptRA=false +Address=100.100.100.2/24 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 8bd8581697..9d70a0afcd 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1009,6 +1009,80 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') self.assertRegex(output, 'State: routable \(configured\)') +class NetworkdNetWorkBondTests(unittest.TestCase, Utilities): + links = [ + 'bond99', + 'veth99'] + + units = [ + '25-bond.netdev', + '25-veth.netdev', + 'bond99.network', + 'dhcp-server.network', + 'veth-bond.network'] + + def setUp(self): + self.link_remove(self.links) + + def tearDown(self): + self.link_remove(self.links) + self.remove_unit_from_networkd_path(self.units) + + def test_bridge_property(self): + self.copy_unit_to_networkd_unit_path('25-bond.netdev', '25-veth.netdev', 'bond99.network', + 'dhcp-server.network', 'veth-bond.network') + self.start_networkd() + + self.assertTrue(self.link_exits('bond99')) + self.assertTrue(self.link_exits('veth99')) + self.assertTrue(self.link_exits('veth-peer')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'veth-peer']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'UP,LOWER_UP') + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'veth99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'SLAVE,UP,LOWER_UP') + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'MASTER,UP,LOWER_UP') + + output = subprocess.check_output(['networkctl', 'status', 'veth-peer']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: routable \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: enslaved \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: routable \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'veth99', 'down']), 0) + time.sleep(2) + + output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: off \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: degraded \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'veth99', 'up']), 0) + time.sleep(2) + + output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: enslaved \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: routable \(configured\)') + class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities): links = [ 'bridge99',