Merge pull request #11767 from yuwata/network-bridge-enslaved

network: extend 'enslaved' state to bridge slave
This commit is contained in:
Lennart Poettering
2019-02-25 19:29:30 +01:00
committed by GitHub
9 changed files with 184 additions and 70 deletions
+5 -4
View File
@@ -118,21 +118,22 @@
<varlistentry>
<term>carrier</term>
<listitem>
<para>the link has a carrier, or for bond master, all bonding slave network interfaces are
enslaved to the master.</para>
<para>the link has a carrier, or for bond or bridge master, all bonding or bridge slave
network interfaces are enslaved to the master.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>degraded</term>
<listitem>
<para>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</para>
or bridge master, one of the bonding or bridge slave network interfaces is in off,
no-carrier, or dormant</para>
</listitem>
</varlistentry>
<varlistentry>
<term>enslaved</term>
<listitem>
<para>the link has carrier and is enslaved to other network interfaces</para>
<para>the link has carrier and is enslaved to bond or bridge master network interface</para>
</listitem>
</varlistentry>
<varlistentry>
+4 -4
View File
@@ -318,10 +318,10 @@
<varlistentry>
<term><varname>LinkLocalAddressing=</varname></term>
<listitem>
<para>Enables link-local address autoconfiguration. Accepts
<literal>yes</literal>, <literal>no</literal>,
<literal>ipv4</literal>, or <literal>ipv6</literal>. Defaults to
<literal>ipv6</literal>.</para>
<para>Enables link-local address autoconfiguration. Accepts <literal>yes</literal>,
<literal>no</literal>, <literal>ipv4</literal>, or <literal>ipv6</literal>. If
<varname>Bridge=</varname> is set, defaults to <literal>no</literal>, and if not,
defaults to <literal>ipv6</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
+92 -55
View File
@@ -158,7 +158,7 @@ static bool link_ipv6_enabled(Link *link) {
if (!socket_ipv6_is_supported())
return false;
if (link->network->bridge || link->network->bond)
if (link->network->bond)
return false;
if (manager_sysctl_ipv6_enabled(link->manager) == 0)
@@ -328,8 +328,39 @@ static int link_enable_ipv6(Link *link) {
return 0;
}
void link_update_operstate(Link *link, bool also_update_bond_master) {
static bool link_is_enslaved(Link *link) {
if (link->flags & IFF_SLAVE)
/* Even if the link is not managed by networkd, honor IFF_SLAVE flag. */
return true;
if (!link->enslaved_raw)
return false;
if (!link->network)
return false;
if (link->network->bridge)
/* TODO: support the case when link is not managed by networkd. */
return true;
return false;
}
static void link_update_master_operstate(Link *link, NetDev *netdev) {
Link *master;
if (!netdev)
return;
if (link_get(link->manager, netdev->ifindex, &master) < 0)
return;
link_update_operstate(master, true);
}
void link_update_operstate(Link *link, bool also_update_master) {
LinkOperationalState operstate;
Iterator i;
assert(link);
@@ -338,7 +369,6 @@ void link_update_operstate(Link *link, bool also_update_bond_master) {
else if (link_has_carrier(link)) {
Address *address;
uint8_t scope = RT_SCOPE_NOWHERE;
Iterator i;
/* if we have carrier, check what addresses we have */
SET_FOREACH(address, link->addresses, i) {
@@ -373,15 +403,13 @@ void link_update_operstate(Link *link, bool also_update_bond_master) {
operstate = LINK_OPERSTATE_OFF;
if (IN_SET(operstate, LINK_OPERSTATE_DEGRADED, LINK_OPERSTATE_CARRIER) &&
link->flags & IFF_SLAVE)
link_is_enslaved(link))
operstate = LINK_OPERSTATE_ENSLAVED;
if (IN_SET(operstate, LINK_OPERSTATE_CARRIER, LINK_OPERSTATE_ENSLAVED, LINK_OPERSTATE_ROUTABLE) &&
!hashmap_isempty(link->bond_slaves)) {
Iterator i;
if (IN_SET(operstate, LINK_OPERSTATE_CARRIER, LINK_OPERSTATE_ENSLAVED, LINK_OPERSTATE_ROUTABLE)) {
Link *slave;
HASHMAP_FOREACH(slave, link->bond_slaves, i) {
HASHMAP_FOREACH(slave, link->slaves, i) {
link_update_operstate(slave, false);
if (IN_SET(slave->operstate,
@@ -396,13 +424,9 @@ void link_update_operstate(Link *link, bool also_update_bond_master) {
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);
if (also_update_master && link->network) {
link_update_master_operstate(link, link->network->bond);
link_update_master_operstate(link, link->network->bridge);
}
}
@@ -592,8 +616,8 @@ static void link_detach_from_manager(Link *link) {
}
static Link *link_free(Link *link) {
Link *carrier, *master;
Address *address;
Link *carrier;
Route *route;
Iterator i;
@@ -659,7 +683,17 @@ 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);
hashmap_free(link->slaves);
if (link->network) {
if (link->network->bond &&
link_get(link->manager, link->network->bond->ifindex, &master) >= 0)
(void) hashmap_remove(master->slaves, INT_TO_PTR(link->ifindex));
if (link->network->bridge &&
link_get(link->manager, link->network->bridge->ifindex, &master) >= 0)
(void) hashmap_remove(master->slaves, INT_TO_PTR(link->ifindex));
}
return mfree(link);
}
@@ -953,23 +987,20 @@ void link_check_ready(Link *link) {
!link->ipv4ll_route)
return;
if (!link->network->bridge) {
if (link_ipv6ll_enabled(link) &&
in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
return;
if (link_ipv6ll_enabled(link) &&
in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
return;
if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) &&
!link->dhcp4_configured) ||
(link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) &&
!link->dhcp6_configured) ||
(link_dhcp4_enabled(link) && link_dhcp6_enabled(link) &&
!link->dhcp4_configured && !link->dhcp6_configured))
return;
if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) &&
!link->dhcp4_configured) ||
(link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) &&
!link->dhcp6_configured) ||
(link_dhcp4_enabled(link) && link_dhcp6_enabled(link) &&
!link->dhcp4_configured && !link->dhcp6_configured))
return;
if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
return;
}
if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
return;
if (link->state != LINK_STATE_CONFIGURED)
link_enter_configured(link);
@@ -1414,7 +1445,7 @@ int link_set_mtu(Link *link, uint32_t mtu) {
return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
/* If IPv6 not configured (no static IPv6 address and IPv6LL autoconfiguration is disabled)
* for this interface, or if it is a bridge slave, then disable IPv6 else enable it. */
* for this interface, then disable IPv6 else enable it. */
(void) link_enable_ipv6(link);
/* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes
@@ -1665,23 +1696,22 @@ static int link_set_bond(Link *link) {
return r;
}
static int link_append_bond_slave(Link *link) {
static int link_append_to_master(Link *link, NetDev *netdev) {
Link *master;
int r;
assert(link);
assert(link->network);
assert(link->network->bond);
assert(netdev);
r = link_get(link->manager, link->network->bond->ifindex, &master);
r = link_get(link->manager, netdev->ifindex, &master);
if (r < 0)
return r;
r = hashmap_ensure_allocated(&master->bond_slaves, NULL);
r = hashmap_ensure_allocated(&master->slaves, NULL);
if (r < 0)
return r;
r = hashmap_put(master->bond_slaves, INT_TO_PTR(link->ifindex), link);
r = hashmap_put(master->slaves, INT_TO_PTR(link->ifindex), link);
if (r < 0)
return r;
@@ -2478,6 +2508,10 @@ static int link_joined(Link *link) {
r = link_set_bridge(link);
if (r < 0)
log_link_error_errno(link, r, "Could not set bridge message: %m");
r = link_append_to_master(link, link->network->bridge);
if (r < 0)
log_link_error_errno(link, r, "Failed to add to bridge master's slave list: %m");
}
if (link->network->bond) {
@@ -2485,7 +2519,7 @@ static int link_joined(Link *link) {
if (r < 0)
log_link_error_errno(link, r, "Could not set bond message: %m");
r = link_append_bond_slave(link);
r = link_append_to_master(link, link->network->bond);
if (r < 0)
log_link_error_errno(link, r, "Failed to add to bond master's slave list: %m");
}
@@ -2511,6 +2545,8 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
assert(link);
assert(link->network);
assert(link->enslaving > 0);
assert(!link->enslaved_raw);
link->enslaving--;
@@ -2525,8 +2561,10 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
} else
log_link_debug(link, "Joined netdev");
if (link->enslaving <= 0)
if (link->enslaving == 0) {
link->enslaved_raw = true;
link_joined(link);
}
return 1;
}
@@ -2543,12 +2581,8 @@ static int link_enter_join_netdev(Link *link) {
link_set_state(link, LINK_STATE_CONFIGURING);
link_dirty(link);
if (!link->network->bridge &&
!link->network->bond &&
!link->network->vrf &&
hashmap_isempty(link->network->stacked_netdevs))
return link_joined(link);
link->enslaving = 0;
link->enslaved_raw = false;
if (link->network->bond) {
if (link->network->bond->state == NETDEV_STATE_READY &&
@@ -2560,6 +2594,8 @@ static int link_enter_join_netdev(Link *link) {
LOG_NETDEV_INTERFACE(link->network->bond),
LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bond->ifname));
link->enslaving++;
r = netdev_join(link->network->bond, link, netdev_join_handler);
if (r < 0) {
log_struct_errno(LOG_WARNING, r,
@@ -2569,8 +2605,6 @@ static int link_enter_join_netdev(Link *link) {
link_enter_failed(link);
return r;
}
link->enslaving++;
}
if (link->network->bridge) {
@@ -2579,6 +2613,8 @@ static int link_enter_join_netdev(Link *link) {
LOG_NETDEV_INTERFACE(link->network->bridge),
LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bridge->ifname));
link->enslaving++;
r = netdev_join(link->network->bridge, link, netdev_join_handler);
if (r < 0) {
log_struct_errno(LOG_WARNING, r,
@@ -2588,8 +2624,6 @@ static int link_enter_join_netdev(Link *link) {
link_enter_failed(link);
return r;
}
link->enslaving++;
}
if (link->network->vrf) {
@@ -2598,6 +2632,8 @@ static int link_enter_join_netdev(Link *link) {
LOG_NETDEV_INTERFACE(link->network->vrf),
LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->vrf->ifname));
link->enslaving++;
r = netdev_join(link->network->vrf, link, netdev_join_handler);
if (r < 0) {
log_struct_errno(LOG_WARNING, r,
@@ -2607,8 +2643,6 @@ static int link_enter_join_netdev(Link *link) {
link_enter_failed(link);
return r;
}
link->enslaving++;
}
HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) {
@@ -2623,6 +2657,8 @@ static int link_enter_join_netdev(Link *link) {
LOG_NETDEV_INTERFACE(netdev),
LOG_LINK_MESSAGE(link, "Enslaving by '%s'", netdev->ifname));
link->enslaving++;
r = netdev_join(netdev, link, netdev_join_handler);
if (r < 0) {
log_struct_errno(LOG_WARNING, r,
@@ -2632,10 +2668,11 @@ static int link_enter_join_netdev(Link *link) {
link_enter_failed(link);
return r;
}
link->enslaving++;
}
if (link->enslaving == 0)
return link_joined(link);
return 0;
}
+3 -1
View File
@@ -76,6 +76,8 @@ typedef struct Link {
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
unsigned enslaving;
/* link_is_enslaved() has additional checks. So, it is named _raw. */
bool enslaved_raw;
Set *addresses;
Set *addresses_foreign;
@@ -129,7 +131,7 @@ typedef struct Link {
Hashmap *bound_by_links;
Hashmap *bound_to_links;
Hashmap *bond_slaves;
Hashmap *slaves;
} Link;
typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*);
+3 -3
View File
@@ -1427,9 +1427,6 @@ void manager_free(Manager *m) {
sd_netlink_unref(m->genl);
sd_resolve_unref(m->resolve);
while ((network = m->networks))
network_free(network);
while ((link = hashmap_first(m->dhcp6_prefixes)))
manager_dhcp6_prefix_remove_all(m, link);
hashmap_free(m->dhcp6_prefixes);
@@ -1445,6 +1442,9 @@ void manager_free(Manager *m) {
m->links_requesting_uuid = set_free(m->links_requesting_uuid);
set_free(m->duids_requesting_uuid);
while ((network = m->networks))
network_free(network);
hashmap_free(m->networks_by_name);
m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
+4 -1
View File
@@ -141,7 +141,10 @@ static int network_verify(Network *network) {
}
if (network->link_local < 0)
network->link_local = ADDRESS_FAMILY_IPV6;
network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
if (network->ipv6_accept_ra < 0 && network->bridge)
network->ipv6_accept_ra = false;
/* IPMasquerade=yes implies IPForward=yes */
if (network->ip_masquerade)
@@ -4,4 +4,9 @@ Name=bridge99
[Network]
Address=192.168.0.15/24
Gateway=192.168.0.1
IPv6AcceptRA=no
IgnoreCarrierLoss=true
[RoutingPolicyRule]
To=8.8.8.8
Table=100
+1
View File
@@ -4,3 +4,4 @@ Name=bridge99
[Network]
Address=192.168.0.15/24
Gateway=192.168.0.1
IPv6AcceptRA=no
+67 -2
View File
@@ -1198,8 +1198,7 @@ class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities):
output = subprocess.check_output(['ip', 'addr', 'show', 'bridge99']).rstrip().decode('utf-8')
print(output)
self.assertRegex(output, '192.168.0.15')
self.assertRegex(output, '192.168.0.1')
self.assertRegex(output, '192.168.0.15/24')
output = subprocess.check_output(['bridge', '-d', 'link', 'show', 'dummy98']).rstrip().decode('utf-8')
print(output)
@@ -1212,13 +1211,37 @@ class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities):
if (os.path.exists('/sys/devices/virtual/net/bridge00/lower_dummy98/brport/multicast_to_unicast')):
self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast'), '1')
output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8')
self.assertRegex(output, 'State: enslaved \(configured\)')
output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
self.assertRegex(output, 'State: enslaved \(configured\)')
output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
self.assertRegex(output, 'State: routable \(configured\)')
self.assertEqual(subprocess.call(['ip', 'address', 'add', '192.168.0.16/24', 'dev', 'bridge99']), 0)
time.sleep(1)
output = subprocess.check_output(['ip', 'addr', 'show', 'bridge99']).rstrip().decode('utf-8')
print(output)
self.assertRegex(output, '192.168.0.16/24')
output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
self.assertRegex(output, 'State: routable \(configured\)')
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'test1']), 0)
time.sleep(3)
output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
self.assertRegex(output, 'State: degraded \(configured\)')
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
time.sleep(3)
output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
self.assertRegex(output, 'State: no-carrier \(configured\)')
output = subprocess.check_output(['ip', 'address', 'show', 'bridge99']).rstrip().decode('utf-8')
print(output)
self.assertRegex(output, 'NO-CARRIER')
@@ -1248,6 +1271,48 @@ class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
self.assertRegex(output, 'inet 192.168.0.16/24 scope global secondary bridge99')
subprocess.call(['ip', 'rule', 'del', 'table', '100'])
def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self):
self.copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-slave-interface-1.network',
'bridge99-ignore-carrier-loss.network')
self.start_networkd()
self.assertTrue(self.link_exits('bridge99'))
self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
time.sleep(3)
output = subprocess.check_output(['ip', 'address', 'show', 'bridge99']).rstrip().decode('utf-8')
print(output)
self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8')
self.assertRegex(output, 'State: routable \(configured\)')
output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8')
self.assertRegex(output, 'State: enslaved \(configured\)')
output = subprocess.check_output(['ip', 'rule', 'list', 'table', '100']).rstrip().decode('utf-8')
print(output)
self.assertEqual(output, '0: from all to 8.8.8.8 lookup 100')
subprocess.call(['ip', 'rule', 'del', 'table', '100'])
class NetworkdNetWorkLLDPTests(unittest.TestCase, Utilities):
links = ['veth99']