Added support for L2 BridgeMDB entries (#32894)

* Added support for L2 BridgeMDB entries
This commit is contained in:
jauge-technica 2024-08-02 17:31:20 +02:00 committed by GitHub
parent 4d1fbe53c1
commit 82f2a2f032
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 92 additions and 35 deletions

View file

@ -4570,7 +4570,7 @@ ServerAddress=192.168.0.1/24</programlisting>
<varlistentry>
<term><varname>MulticastGroupAddress=</varname></term>
<listitem>
<para>Specifies the IPv4 or IPv6 multicast group address to add. This setting is mandatory.</para>
<para>Specifies the IPv4, IPv6, or L2 MAC multicast group address to add. This setting is mandatory.</para>
<xi:include href="version-info.xml" xpointer="v247"/>
</listitem>

View file

@ -71,6 +71,7 @@ static int bridge_mdb_new_static(
*mdb = (BridgeMDB) {
.network = network,
.section = TAKE_PTR(n),
.type = _BRIDGE_MDB_ENTRY_TYPE_INVALID,
};
r = hashmap_ensure_put(&network->bridge_mdb_entries_by_section, &config_section_hash_ops, mdb->section, mdb);
@ -125,24 +126,35 @@ static int bridge_mdb_configure(BridgeMDB *mdb, Link *link, Request *req) {
IN_ADDR_TO_STRING(mdb->family, &mdb->group_addr), mdb->vlan_id);
entry = (struct br_mdb_entry) {
/* If MDB entry is added on bridge master, then the state must be MDB_TEMPORARY.
/* If MDB entry is added on bridge master, then the state must be MDB_TEMPORARY,
* except on L2 routes, where they must always be permanent.
* See br_mdb_add_group() in net/bridge/br_mdb.c of kernel. */
.state = link->master_ifindex <= 0 ? MDB_TEMPORARY : MDB_PERMANENT,
.state = link->master_ifindex <= 0 && mdb->type == BRIDGE_MDB_ENTRY_TYPE_L3 ? MDB_TEMPORARY : MDB_PERMANENT,
.ifindex = link->ifindex,
.vid = mdb->vlan_id,
};
switch (mdb->family) {
case AF_INET:
entry.addr.u.ip4 = mdb->group_addr.in.s_addr;
entry.addr.proto = htobe16(ETH_P_IP);
switch (mdb->type) {
case BRIDGE_MDB_ENTRY_TYPE_L2:
memcpy(entry.addr.u.mac_addr, &mdb->l2_addr.ether_addr_octet, ETH_ALEN);
entry.addr.proto = 0;
break;
case BRIDGE_MDB_ENTRY_TYPE_L3:
switch (mdb->family) {
case AF_INET:
entry.addr.u.ip4 = mdb->group_addr.in.s_addr;
entry.addr.proto = htobe16(ETH_P_IP);
break;
case AF_INET6:
entry.addr.u.ip6 = mdb->group_addr.in6;
entry.addr.proto = htobe16(ETH_P_IPV6);
case AF_INET6:
entry.addr.u.ip6 = mdb->group_addr.in6;
entry.addr.proto = htobe16(ETH_P_IPV6);
break;
default:
assert_not_reached();
}
break;
default:
assert_not_reached();
}
@ -252,30 +264,49 @@ static int bridge_mdb_verify(BridgeMDB *mdb) {
if (section_is_invalid(mdb->section))
return -EINVAL;
if (mdb->family == AF_UNSPEC)
switch (mdb->type) {
case BRIDGE_MDB_ENTRY_TYPE_L2:
if (!ether_addr_is_multicast(&mdb->l2_addr))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: MulticastGroupAddress= is not an L2 multicast address. "
"Ignoring [BridgeMDB] section from line %u.",
mdb->section->filename, mdb->section->line);
break;
case BRIDGE_MDB_ENTRY_TYPE_L3:
if (mdb->family == AF_UNSPEC)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: [BridgeMDB] section without MulticastGroupAddress= field configured. "
"Ignoring [BridgeMDB] section from line %u.",
mdb->section->filename, mdb->section->line);
if (!in_addr_is_multicast(mdb->family, &mdb->group_addr))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: MulticastGroupAddress= is not a multicast address. "
"Ignoring [BridgeMDB] section from line %u.",
mdb->section->filename, mdb->section->line);
switch (mdb->family) {
case AF_INET:
if (in4_addr_is_local_multicast(&mdb->group_addr.in))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: MulticastGroupAddress= is a local multicast address. "
"Ignoring [BridgeMDB] section from line %u.",
mdb->section->filename, mdb->section->line);
break;
default:
if (in6_addr_is_link_local_all_nodes(&mdb->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->section->filename, mdb->section->line);
break;
}
break;
default:
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: [BridgeMDB] section without MulticastGroupAddress= field configured. "
"Ignoring [BridgeMDB] section from line %u.",
mdb->section->filename, mdb->section->line);
if (!in_addr_is_multicast(mdb->family, &mdb->group_addr))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: MulticastGroupAddress= is not a multicast address. "
"Ignoring [BridgeMDB] section from line %u.",
mdb->section->filename, mdb->section->line);
if (mdb->family == AF_INET) {
if (in4_addr_is_local_multicast(&mdb->group_addr.in))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: MulticastGroupAddress= is a local multicast address. "
"Ignoring [BridgeMDB] section from line %u.",
mdb->section->filename, mdb->section->line);
} else {
if (in6_addr_is_link_local_all_nodes(&mdb->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->section->filename, mdb->section->line);
}
return 0;
@ -355,10 +386,17 @@ int config_parse_mdb_group_address(
if (r < 0)
return log_oom();
r = in_addr_from_string_auto(rvalue, &mdb->family, &mdb->group_addr);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Cannot parse multicast group address: %m");
return 0;
r = parse_ether_addr(rvalue, &mdb->l2_addr);
if (r >= 0)
mdb->type = BRIDGE_MDB_ENTRY_TYPE_L2;
else {
r = in_addr_from_string_auto(rvalue, &mdb->family, &mdb->group_addr);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Cannot parse multicast group address as either L2 MAC, IPv4 or IPv6, ignoring: %m");
return 0;
}
mdb->type = BRIDGE_MDB_ENTRY_TYPE_L3;
}
TAKE_PTR(mdb);

View file

@ -10,10 +10,21 @@
typedef struct Link Link;
typedef struct Network Network;
typedef enum BridgeMDBEntryType {
BRIDGE_MDB_ENTRY_TYPE_L2,
BRIDGE_MDB_ENTRY_TYPE_L3,
_BRIDGE_MDB_ENTRY_TYPE_MAX,
_BRIDGE_MDB_ENTRY_TYPE_INVALID = -EINVAL,
} BridgeMDBEntryType;
typedef struct BridgeMDB {
Network *network;
ConfigSection *section;
BridgeMDBEntryType type;
struct ether_addr l2_addr;
int family;
union in_addr_union group_addr;
uint16_t vlan_id;

View file

@ -12,3 +12,7 @@ MulticastGroupAddress=ff02:aaaa:fee5:0000:0000:0000:0001:0004
[BridgeMDB]
VLANId=4067
MulticastGroupAddress=224.0.1.2
[BridgeMDB]
VLANId=4069
MulticastGroupAddress=01:80:c2:00:00:0e

View file

@ -5077,6 +5077,10 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066')
self.assertRegex(output, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067')
# Old kernel may not support L2 bridge MDB entries
if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 01:80:c2:00:00:0f permanent vid 4070') == 0:
self.assertRegex(output, 'dev bridge99 port bridge99 grp 01:80:c2:00:00:0e permanent *vid 4069')
def test_bridge_keep_master(self):
check_output('ip link add bridge99 type bridge')
check_output('ip link set bridge99 up')