summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2020-09-17 06:52:44 +0200
committerGitHub <noreply@github.com>2020-09-17 06:52:44 +0200
commite8345097602584ab025a283849bf9c8219363419 (patch)
tree3ab23316bb0016d87127fd406c7909f1e6d59026
parentMerge pull request #17080 from poettering/repart-umask-fix (diff)
parenttest-network: add test for [BridgeMDB] section (diff)
downloadsystemd-e8345097602584ab025a283849bf9c8219363419.tar.xz
systemd-e8345097602584ab025a283849bf9c8219363419.zip
Merge pull request #17059 from yuwata/network-mdb-follow-ups
network: follow ups for bridge MDB support
-rw-r--r--src/basic/in-addr-util.c16
-rw-r--r--src/basic/in-addr-util.h2
-rw-r--r--src/network/networkd-link.c54
-rw-r--r--src/network/networkd-link.h2
-rw-r--r--src/network/networkd-mdb.c261
-rw-r--r--src/network/networkd-mdb.h2
-rw-r--r--src/network/networkd-network.c8
-rw-r--r--test/test-network/conf/26-bridge-mdb-master.network5
-rw-r--r--test/test-network/conf/26-bridge-mdb-slave.network14
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py13
10 files changed, 257 insertions, 120 deletions
diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c
index 8783ab3c9b..c102504fdd 100644
--- a/src/basic/in-addr-util.c
+++ b/src/basic/in-addr-util.c
@@ -54,6 +54,16 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) {
return -EAFNOSUPPORT;
}
+bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a) {
+ assert(a);
+
+ /* ff02::1 */
+ return be32toh(a->s6_addr32[0]) == UINT32_C(0xff020000) &&
+ a->s6_addr32[1] == 0 &&
+ a->s6_addr32[2] == 0 &&
+ be32toh(a->s6_addr32[3]) == UINT32_C(0x00000001);
+}
+
int in_addr_is_multicast(int family, const union in_addr_union *u) {
assert(u);
@@ -66,6 +76,12 @@ int in_addr_is_multicast(int family, const union in_addr_union *u) {
return -EAFNOSUPPORT;
}
+bool in4_addr_is_local_multicast(const struct in_addr *a) {
+ assert(a);
+
+ return (be32toh(a->s_addr) & UINT32_C(0xffffff00)) == UINT32_C(0xe0000000);
+}
+
bool in4_addr_is_localhost(const struct in_addr *a) {
assert(a);
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
index e5df2a8058..45c93a0056 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -27,10 +27,12 @@ int in_addr_is_multicast(int family, const union in_addr_union *u);
bool in4_addr_is_link_local(const struct in_addr *a);
int in_addr_is_link_local(int family, const union in_addr_union *u);
+bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a);
bool in4_addr_is_localhost(const struct in_addr *a);
int in_addr_is_localhost(int family, const union in_addr_union *u);
+bool in4_addr_is_local_multicast(const struct in_addr *a);
bool in4_addr_is_non_local(const struct in_addr *a);
bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 297b26cb69..4194dcb1bb 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1157,6 +1157,11 @@ void link_check_ready(Link *link) {
return;
}
+ if (!link->bridge_mdb_configured) {
+ log_link_debug(link, "%s(): Bridge MDB is not configured.", __func__);
+ return;
+ }
+
if (link_has_carrier(link) || !link->network->configure_without_carrier) {
bool has_ndisc_address = false;
NDiscAddress *n;
@@ -1254,30 +1259,6 @@ static int link_set_bridge_fdb(Link *link) {
return 0;
}
-static int link_set_bridge_mdb(Link *link) {
- MdbEntry *mdb_entry;
- int r;
-
- if (!link->network)
- return 0;
-
- if (LIST_IS_EMPTY(link->network->static_mdb_entries))
- return 0;
-
- if (!link->network->bridge) {
- log_link_error(link, "Cannot configure MDB entries on non-bridge port");
- return 0;
- }
-
- LIST_FOREACH(static_mdb_entries, mdb_entry, link->network->static_mdb_entries) {
- r = mdb_entry_configure(link, mdb_entry);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to add entry to multicast group database: %m");
- }
-
- return 0;
-}
-
static int static_address_ready_callback(Address *address) {
Address *a;
Link *link;
@@ -1410,6 +1391,10 @@ static int link_request_set_addresses(Link *link) {
if (r < 0)
return r;
+ r = link_set_bridge_mdb(link);
+ if (r < 0)
+ return r;
+
r = link_request_set_neighbors(link);
if (r < 0)
return r;
@@ -3931,9 +3916,24 @@ static int link_carrier_gained(Link *link) {
if (r < 0)
return r;
- r = link_set_bridge_mdb(link);
- if (r < 0)
- return r;
+ if (!link->bridge_mdb_configured) {
+ r = link_set_bridge_mdb(link);
+ if (r < 0)
+ return r;
+ }
+
+ if (streq_ptr(link->kind, "bridge")) {
+ Link *slave;
+
+ SET_FOREACH(slave, link->slaves) {
+ if (slave->bridge_mdb_configured)
+ continue;
+
+ r = link_set_bridge_mdb(slave);
+ if (r < 0)
+ link_enter_failed(slave);
+ }
+ }
return 0;
}
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index a0c5661149..1550db8a23 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -85,6 +85,7 @@ typedef struct Link {
unsigned tc_messages;
unsigned sr_iov_messages;
unsigned enslaving;
+ unsigned bridge_mdb_messages;
Set *addresses;
Set *addresses_foreign;
@@ -124,6 +125,7 @@ typedef struct Link {
bool setting_mtu:1;
bool setting_genmode:1;
bool ipv6_mtu_set:1;
+ bool bridge_mdb_configured:1;
LIST_HEAD(Address, pool_addresses);
diff --git a/src/network/networkd-mdb.c b/src/network/networkd-mdb.c
index 3dec19b1c2..f27d4da254 100644
--- a/src/network/networkd-mdb.c
+++ b/src/network/networkd-mdb.c
@@ -1,14 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <linux/if_bridge.h>
-#include <net/ethernet.h>
#include <net/if.h>
-#include "alloc-util.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "networkd-mdb.h"
-#include "util.h"
+#include "string-util.h"
#include "vlan-util.h"
#define STATIC_MDB_ENTRIES_PER_NETWORK_MAX 1024U
@@ -75,82 +72,6 @@ static int mdb_entry_new_static(
return 0;
}
-/* parse the VLAN Id from config files. */
-int config_parse_mdb_vlan_id(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *network = userdata;
- _cleanup_(mdb_entry_free_or_set_invalidp) MdbEntry *mdb_entry = NULL;
- int r;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = mdb_entry_new_static(network, filename, section_line, &mdb_entry);
- if (r < 0)
- return log_oom();
-
- r = config_parse_vlanid(unit, filename, line, section,
- section_line, lvalue, ltype,
- rvalue, &mdb_entry->vlan_id, userdata);
- if (r < 0)
- return r;
-
- mdb_entry = NULL;
-
- return 0;
-}
-
-/* parse the multicast group from config files. */
-int config_parse_mdb_group_address(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *network = userdata;
- _cleanup_(mdb_entry_free_or_set_invalidp) MdbEntry *mdb_entry = NULL;
- int r;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = mdb_entry_new_static(network, filename, section_line, &mdb_entry);
- if (r < 0)
- return log_oom();
-
- r = in_addr_from_string_auto(rvalue, &mdb_entry->family, &mdb_entry->group_addr);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r, "Cannot parse multicast group address: %m");
- return 0;
- }
-
- mdb_entry = NULL;
-
- return 0;
-}
-
/* remove and MDB entry. */
MdbEntry *mdb_entry_free(MdbEntry *mdb_entry) {
if (!mdb_entry)
@@ -174,6 +95,9 @@ static int set_mdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
int r;
assert(link);
+ assert(link->bridge_mdb_messages > 0);
+
+ link->bridge_mdb_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
@@ -185,23 +109,16 @@ static int set_mdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
return 1;
}
- return 1;
-}
-
-int mdb_entry_verify(MdbEntry *mdb_entry) {
- if (section_is_invalid(mdb_entry->section))
- return -EINVAL;
-
- if (in_addr_is_multicast(mdb_entry->family, &mdb_entry->group_addr) <= 0) {
- log_error("No valid MulticastGroupAddress= assignment in this section");
- return -EINVAL;
+ if (link->bridge_mdb_messages == 0) {
+ link->bridge_mdb_configured = true;
+ link_check_ready(link);
}
- return 0;
+ return 1;
}
/* send a request to the kernel to add an MDB entry */
-int mdb_entry_configure(Link *link, MdbEntry *mdb_entry) {
+static int mdb_entry_configure(Link *link, MdbEntry *mdb_entry) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
struct br_mdb_entry entry;
int r;
@@ -211,6 +128,14 @@ int mdb_entry_configure(Link *link, MdbEntry *mdb_entry) {
assert(link->manager);
assert(mdb_entry);
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *a = NULL;
+
+ (void) in_addr_to_string(mdb_entry->family, &mdb_entry->group_addr, &a);
+ log_link_debug(link, "Configuring bridge MDB entry: MulticastGroupAddress=%s, VLANId=%u",
+ strna(a), mdb_entry->vlan_id);
+ }
+
entry = (struct br_mdb_entry) {
.state = MDB_PERMANENT,
.ifindex = link->ifindex,
@@ -250,3 +175,155 @@ int mdb_entry_configure(Link *link, MdbEntry *mdb_entry) {
return 1;
}
+
+int link_set_bridge_mdb(Link *link) {
+ MdbEntry *mdb_entry;
+ Link *master;
+ int r;
+
+ assert(link);
+
+ link->bridge_mdb_configured = false;
+
+ if (!link->network)
+ return 0;
+
+ if (!link->network->bridge) {
+ link->bridge_mdb_configured = true;
+ return 0;
+ }
+
+ if (!link_has_carrier(link))
+ return log_link_debug(link, "Link does not have carrier yet, setting MDB entries later.");
+
+ r = link_get(link->manager, link->network->bridge->ifindex, &master);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get Link object for Bridge=%s", link->network->bridge->ifname);
+
+ if (!link_has_carrier(master))
+ return log_link_debug(link, "Bridge interface %s does not have carrier yet, setting MDB entries later.", link->network->bridge->ifname);
+
+ LIST_FOREACH(static_mdb_entries, mdb_entry, link->network->static_mdb_entries) {
+ r = mdb_entry_configure(link, mdb_entry);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to add MDB entry to multicast group database: %m");
+
+ link->bridge_mdb_messages++;
+ }
+
+ if (link->bridge_mdb_messages == 0) {
+ link->bridge_mdb_configured = true;
+ link_check_ready(link);
+ }
+
+ return 0;
+}
+
+/* parse the VLAN Id from config files. */
+int config_parse_mdb_vlan_id(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_(mdb_entry_free_or_set_invalidp) MdbEntry *mdb_entry = NULL;
+ Network *network = userdata;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = mdb_entry_new_static(network, filename, section_line, &mdb_entry);
+ if (r < 0)
+ return log_oom();
+
+ r = config_parse_vlanid(unit, filename, line, section,
+ section_line, lvalue, ltype,
+ rvalue, &mdb_entry->vlan_id, userdata);
+ if (r < 0)
+ return r;
+
+ mdb_entry = NULL;
+
+ return 0;
+}
+
+/* parse the multicast group from config files. */
+int config_parse_mdb_group_address(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_(mdb_entry_free_or_set_invalidp) MdbEntry *mdb_entry = NULL;
+ Network *network = userdata;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = mdb_entry_new_static(network, filename, section_line, &mdb_entry);
+ if (r < 0)
+ return log_oom();
+
+ r = in_addr_from_string_auto(rvalue, &mdb_entry->family, &mdb_entry->group_addr);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Cannot parse multicast group address: %m");
+ return 0;
+ }
+
+ mdb_entry = NULL;
+
+ return 0;
+}
+
+int mdb_entry_verify(MdbEntry *mdb_entry) {
+ if (section_is_invalid(mdb_entry->section))
+ return -EINVAL;
+
+ if (mdb_entry->family == AF_UNSPEC)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: [BridgeMDB] section without MulticastGroupAddress= field configured. "
+ "Ignoring [BridgeMDB] section from line %u.",
+ mdb_entry->section->filename, mdb_entry->section->line);
+
+ if (!in_addr_is_multicast(mdb_entry->family, &mdb_entry->group_addr))
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: MulticastGroupAddress= is not a multicast address. "
+ "Ignoring [BridgeMDB] section from line %u.",
+ mdb_entry->section->filename, mdb_entry->section->line);
+
+ if (mdb_entry->family == AF_INET) {
+ if (in4_addr_is_local_multicast(&mdb_entry->group_addr.in))
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: MulticastGroupAddress= is a local multicast address. "
+ "Ignoring [BridgeMDB] section from line %u.",
+ mdb_entry->section->filename, mdb_entry->section->line);
+ } else {
+ if (in6_addr_is_link_local_all_nodes(&mdb_entry->group_addr.in6))
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: MulticastGroupAddress= is the multicast all nodes address. "
+ "Ignoring [BridgeMDB] section from line %u.",
+ mdb_entry->section->filename, mdb_entry->section->line);
+ }
+
+ return 0;
+}
diff --git a/src/network/networkd-mdb.h b/src/network/networkd-mdb.h
index 9ac8a18188..d46ab4a50e 100644
--- a/src/network/networkd-mdb.h
+++ b/src/network/networkd-mdb.h
@@ -24,7 +24,7 @@ struct MdbEntry {
int mdb_entry_verify(MdbEntry *mdb_entry);
MdbEntry *mdb_entry_free(MdbEntry *mdb_entry);
-int mdb_entry_configure(Link *link, MdbEntry *mdb_entry);
+int link_set_bridge_mdb(Link *link);
DEFINE_NETWORK_SECTION_FUNCTIONS(MdbEntry, mdb_entry_free);
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 81876902f5..36d01283c0 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -306,6 +306,14 @@ int network_verify(Network *network) {
if (section_is_invalid(fdb->section))
fdb_entry_free(fdb);
+ if (!LIST_IS_EMPTY(network->static_mdb_entries) && !network->bridge) {
+ log_warning("%s: Cannot configure MDB entries on non-bridge port, ignoring [BridgeMDB] sections.",
+ network->filename);
+
+ while ((mdb = network->static_mdb_entries))
+ mdb_entry_free(mdb);
+ }
+
LIST_FOREACH_SAFE(static_mdb_entries, mdb, mdb_next, network->static_mdb_entries)
if (mdb_entry_verify(mdb) < 0)
mdb_entry_free(mdb);
diff --git a/test/test-network/conf/26-bridge-mdb-master.network b/test/test-network/conf/26-bridge-mdb-master.network
new file mode 100644
index 0000000000..b88ea397c4
--- /dev/null
+++ b/test/test-network/conf/26-bridge-mdb-master.network
@@ -0,0 +1,5 @@
+[Match]
+Name=bridge99
+
+[Network]
+IPv6AcceptRA=false
diff --git a/test/test-network/conf/26-bridge-mdb-slave.network b/test/test-network/conf/26-bridge-mdb-slave.network
new file mode 100644
index 0000000000..dbada0b130
--- /dev/null
+++ b/test/test-network/conf/26-bridge-mdb-slave.network
@@ -0,0 +1,14 @@
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+Bridge=bridge99
+
+[BridgeMDB]
+VLANId=4064
+MulticastGroupAddress=ff02:aaaa:fee5:0000:0000:0000:0001:0003
+
+[BridgeMDB]
+VLANId=4065
+MulticastGroupAddress=224.0.1.1
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index 6660ce06af..12f91bf3c9 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -2870,6 +2870,8 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
'12-dummy.netdev',
'26-bridge.netdev',
'26-bridge-configure-without-carrier.network',
+ '26-bridge-mdb-master.network',
+ '26-bridge-mdb-slave.network',
'26-bridge-slave-interface-1.network',
'26-bridge-slave-interface-2.network',
'26-bridge-vlan-master.network',
@@ -2910,6 +2912,17 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
self.assertRegex(output, f'{i}')
self.assertNotRegex(output, '4095')
+ def test_bridge_mdb(self):
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-mdb-slave.network',
+ '26-bridge.netdev', '26-bridge-mdb-master.network')
+ start_networkd()
+ self.wait_online(['test1:enslaved', 'bridge99:degraded'])
+
+ output = check_output('bridge mdb show dev bridge99')
+ print(output)
+ self.assertRegex(output, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064')
+ self.assertRegex(output, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065')
+
def test_bridge_property(self):
copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
'26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',