diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 4fba788950..6a217d9cae 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -440,6 +440,16 @@ + + FDBMaxLearned= + + Specifies the maximum number of learned Ethernet addresses for the bridge. When the limit is + reached, no more addresses are learned. When unset, the kernel's default will be used. 0 disables the limit. + + + + + diff --git a/src/libsystemd/sd-netlink/netlink-types-rtnl.c b/src/libsystemd/sd-netlink/netlink-types-rtnl.c index e39a75cfe4..a85b4b16c3 100644 --- a/src/libsystemd/sd-netlink/netlink-types-rtnl.c +++ b/src/libsystemd/sd-netlink/netlink-types-rtnl.c @@ -173,6 +173,8 @@ static const NLAPolicy rtnl_link_info_data_bridge_policies[] = { [IFLA_BR_MCAST_MLD_VERSION] = BUILD_POLICY(U8), [IFLA_BR_VLAN_STATS_PER_PORT] = BUILD_POLICY(U8), [IFLA_BR_MULTI_BOOLOPT] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct br_boolopt_multi)), + [IFLA_BR_FDB_N_LEARNED] = BUILD_POLICY(U32), + [IFLA_BR_FDB_MAX_LEARNED] = BUILD_POLICY(U32), }; static const NLAPolicy rtnl_link_info_data_can_policies[] = { diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c index d426c0c501..27f3916476 100644 --- a/src/network/netdev/bridge.c +++ b/src/network/netdev/bridge.c @@ -137,6 +137,12 @@ static int netdev_bridge_post_create_message(NetDev *netdev, sd_netlink_message return r; } + if (b->fdb_max_learned_set) { + r = sd_netlink_message_append_u32(req, IFLA_BR_FDB_MAX_LEARNED, b->fdb_max_learned); + if (r < 0) + return r; + } + r = sd_netlink_message_close_container(req); if (r < 0) return r; @@ -230,6 +236,40 @@ int config_parse_bridge_port_priority( prio); } +int config_parse_bridge_fdb_max_learned( + 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) { + + Bridge *b = ASSERT_PTR(userdata); + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + b->fdb_max_learned_set = false; + return 0; + } + + r = config_parse_uint32_bounded(unit, filename, line, section, section_line, lvalue, rvalue, + 0, UINT32_MAX, true, &b->fdb_max_learned); + if (r <= 0) + return r; + + b->fdb_max_learned_set = true; + return 1; +} + static void bridge_init(NetDev *netdev) { Bridge *b = BRIDGE(netdev); diff --git a/src/network/netdev/bridge.h b/src/network/netdev/bridge.h index 72dd3e4205..7be00e1063 100644 --- a/src/network/netdev/bridge.h +++ b/src/network/netdev/bridge.h @@ -19,6 +19,8 @@ typedef struct Bridge { uint16_t group_fwd_mask; uint16_t default_pvid; uint8_t igmp_version; + uint32_t fdb_max_learned; + bool fdb_max_learned_set; usec_t forward_delay; usec_t hello_time; @@ -44,3 +46,4 @@ MulticastRouter multicast_router_from_string(const char *s) _pure_; CONFIG_PARSER_PROTOTYPE(config_parse_multicast_router); CONFIG_PARSER_PROTOTYPE(config_parse_bridge_igmp_version); CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority); +CONFIG_PARSER_PROTOTYPE(config_parse_bridge_fdb_max_learned); diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 4883a2652d..8325c8779d 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -234,6 +234,7 @@ Bridge.VLANFiltering, config_parse_tristate, Bridge.VLANProtocol, config_parse_vlanprotocol, 0, offsetof(Bridge, vlan_protocol) Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) Bridge.MulticastIGMPVersion, config_parse_uint8, 0, offsetof(Bridge, igmp_version) +Bridge.FDBMaxLearned, config_parse_bridge_fdb_max_learned, 0, offsetof(Bridge, fdb_max_learned) VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */ VRF.Table, config_parse_uint32, 0, offsetof(Vrf, table) BareUDP.DestinationPort, config_parse_ip_port, 0, offsetof(BareUDP, dest_port) diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 69e5ca869d..b51c7aa7e5 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -415,6 +415,9 @@ typedef struct LinkInfo { uint16_t priority; uint8_t mcast_igmp_version; uint8_t port_state; + uint32_t fdb_max_learned; + uint32_t fdb_n_learned; + bool has_fdb_learned; /* vxlan info */ VxLanInfo vxlan_info; @@ -522,6 +525,9 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) { (void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority); (void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version); (void) sd_netlink_message_read_u8(m, IFLA_BRPORT_STATE, &info->port_state); + if (sd_netlink_message_read_u32(m, IFLA_BR_FDB_MAX_LEARNED, &info->fdb_max_learned) >= 0 && + sd_netlink_message_read_u32(m, IFLA_BR_FDB_N_LEARNED, &info->fdb_n_learned) >= 0) + info->has_fdb_learned = true; } if (streq(info->netdev_kind, "bond")) { (void) sd_netlink_message_read_u8(m, IFLA_BOND_MODE, &info->mode); (void) sd_netlink_message_read_u32(m, IFLA_BOND_MIIMON, &info->miimon); @@ -1910,6 +1916,16 @@ static int link_status_one( if (r < 0) return table_log_add_error(r); + if (info->has_fdb_learned) { + r = table_add_many(table, + TABLE_FIELD, "FDB Learned", + TABLE_UINT32, info->fdb_n_learned, + TABLE_FIELD, "FDB Max Learned", + TABLE_UINT32, info->fdb_max_learned); + if (r < 0) + return table_log_add_error(r); + } + if (info->port_state <= BR_STATE_BLOCKING) { r = table_add_many(table, TABLE_FIELD, "Port State", diff --git a/test/test-network/conf/25-bridge.netdev b/test/test-network/conf/25-bridge.netdev index 46a0f78299..9e7fa545b2 100644 --- a/test/test-network/conf/25-bridge.netdev +++ b/test/test-network/conf/25-bridge.netdev @@ -17,3 +17,4 @@ VLANFiltering=yes VLANProtocol=802.1ad STP=true MulticastIGMPVersion=3 +FDBMaxLearned=4 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index ba8e65ee90..1b521d73ce 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1590,12 +1590,16 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'Priority: 9') self.assertRegex(output, 'STP: yes') self.assertRegex(output, 'Multicast IGMP Version: 3') + if 'FDB Max Learned' in output: + self.assertRegex(output, 'FDB Max Learned: 4') output = check_output('ip -d link show bridge99') print(output) self.assertIn('vlan_filtering 1 ', output) self.assertIn('vlan_protocol 802.1ad ', output) self.assertIn('vlan_default_pvid 9 ', output) + if 'fdb_max_learned' in output: + self.assertIn('fdb_max_learned 4 ', output) def test_bond(self): copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bond-property.netdev')