From: jauge-technica <102538870+jauge-technica@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:31:20 +0000 (+0200) Subject: Added support for L2 BridgeMDB entries (#32894) X-Git-Tag: v257-rc1~769 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=82f2a2f032e42ce7f18fc1c3eb0e2459ff7730fa;p=thirdparty%2Fsystemd.git Added support for L2 BridgeMDB entries (#32894) * Added support for L2 BridgeMDB entries --- diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 68d0c9be59b..8755a06eb0f 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -4570,7 +4570,7 @@ ServerAddress=192.168.0.1/24 MulticastGroupAddress= - Specifies the IPv4 or IPv6 multicast group address to add. This setting is mandatory. + Specifies the IPv4, IPv6, or L2 MAC multicast group address to add. This setting is mandatory. diff --git a/src/network/networkd-bridge-mdb.c b/src/network/networkd-bridge-mdb.c index 7ff4a188467..358ca4d2947 100644 --- a/src/network/networkd-bridge-mdb.c +++ b/src/network/networkd-bridge-mdb.c @@ -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 AF_INET6: - entry.addr.u.ip6 = mdb->group_addr.in6; - entry.addr.proto = htobe16(ETH_P_IPV6); + 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); + 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) - 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)) + 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 a local multicast address. " + "%s: MulticastGroupAddress= is not an L2 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)) + break; + case BRIDGE_MDB_ENTRY_TYPE_L3: + if (mdb->family == AF_UNSPEC) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: MulticastGroupAddress= is the multicast all nodes address. " + "%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); } 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); diff --git a/src/network/networkd-bridge-mdb.h b/src/network/networkd-bridge-mdb.h index edea255769d..fe6d6018790 100644 --- a/src/network/networkd-bridge-mdb.h +++ b/src/network/networkd-bridge-mdb.h @@ -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; diff --git a/test/test-network/conf/26-bridge-mdb-master.network b/test/test-network/conf/26-bridge-mdb-master.network index d92762d8997..116e95b1450 100644 --- a/test/test-network/conf/26-bridge-mdb-master.network +++ b/test/test-network/conf/26-bridge-mdb-master.network @@ -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 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index b4d5f80e117..01d871047aa 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -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')