diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index d072501a45..b809bd220e 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -456,6 +456,16 @@ + + LinkLocalLearning= + + Takes a boolean. This enables learning source addresses from link local frames. When unset, the + kernel's default will be used. + + + + + diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 126accaca9..f2777e640f 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -4624,6 +4624,24 @@ ServerAddress=192.168.0.1/24 + + Locked= + + Takes a boolean. Configures whether the port is "locked" and does not allow traffic forwarded + until fully authenticated, e.g. via 802.1x. When unset, the kernel's default will be used. + + + + MACAuthenticationBypass= + + Takes a boolean. Configures whether a locked port has "MAC Authentication Bypass" enabled and + creates newly learned fdb entries in a "locked" state. User space can authenticate these entries by + clearing the locked flag. Requires Learning to be enabled. When unset, the kernel's default will be + used. + + + + diff --git a/src/libsystemd/sd-netlink/netlink-types-rtnl.c b/src/libsystemd/sd-netlink/netlink-types-rtnl.c index a85b4b16c3..187d9b6756 100644 --- a/src/libsystemd/sd-netlink/netlink-types-rtnl.c +++ b/src/libsystemd/sd-netlink/netlink-types-rtnl.c @@ -485,6 +485,8 @@ static const struct NLAPolicy rtnl_bridge_port_policies[] = { [IFLA_BRPORT_MRP_IN_OPEN] = BUILD_POLICY(U8), [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = BUILD_POLICY(U32), [IFLA_BRPORT_MCAST_EHT_HOSTS_CNT] = BUILD_POLICY(U32), + [IFLA_BRPORT_LOCKED] = BUILD_POLICY(U8), + [IFLA_BRPORT_MAB] = BUILD_POLICY(U8), }; static const NLAPolicySetUnionElement rtnl_link_info_slave_data_policy_set_union_elements[] = { diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c index d3ba4989d9..06f9840d1c 100644 --- a/src/network/netdev/bridge.c +++ b/src/network/netdev/bridge.c @@ -47,6 +47,7 @@ static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Ne static int netdev_bridge_post_create_message(NetDev *netdev, sd_netlink_message *req) { Bridge *b = BRIDGE(netdev); + struct br_boolopt_multi bm = {}; int r; r = sd_netlink_message_open_container(req, IFLA_LINKINFO); @@ -142,6 +143,17 @@ static int netdev_bridge_post_create_message(NetDev *netdev, sd_netlink_message return r; } + if (b->linklocal_learn >= 0) { + bm.optmask |= 1 << BR_BOOLOPT_NO_LL_LEARN; + SET_FLAG(bm.optval, 1 << BR_BOOLOPT_NO_LL_LEARN, !b->linklocal_learn); + } + + if (bm.optmask != 0) { + r = sd_netlink_message_append_data(req, IFLA_BR_MULTI_BOOLOPT, &bm, sizeof(bm)); + if (r < 0) + return r; + } + r = sd_netlink_message_close_container(req); if (r < 0) return r; @@ -279,6 +291,7 @@ static void bridge_init(NetDev *netdev) { b->default_pvid = VLANID_INVALID; b->forward_delay = USEC_INFINITY; b->ageing_time = USEC_INFINITY; + b->linklocal_learn = -1; } static bool bridge_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) { diff --git a/src/network/netdev/bridge.h b/src/network/netdev/bridge.h index 7be00e1063..a1d682bf8f 100644 --- a/src/network/netdev/bridge.h +++ b/src/network/netdev/bridge.h @@ -21,6 +21,7 @@ typedef struct Bridge { uint8_t igmp_version; uint32_t fdb_max_learned; bool fdb_max_learned_set; + int linklocal_learn; usec_t forward_delay; usec_t hello_time; diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index be61e208a0..dcd786d87f 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -236,6 +236,7 @@ Bridge.VLANProtocol, config_parse_vlanprotocol, 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) +Bridge.LinkLocalLearning, config_parse_tristate, 0, offsetof(Bridge, linklocal_learn) 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/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 83ef927b8b..bdbb3ad2c8 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -383,6 +383,8 @@ Bridge.ProxyARP, config_parse_tristate, Bridge.ProxyARPWiFi, config_parse_tristate, 0, offsetof(Network, bridge_proxy_arp_wifi) Bridge.Priority, config_parse_bridge_port_priority, 0, offsetof(Network, priority) Bridge.MulticastRouter, config_parse_multicast_router, 0, offsetof(Network, multicast_router) +Bridge.Locked, config_parse_tristate, 0, offsetof(Network, bridge_locked) +Bridge.MACAuthenticationBypass, config_parse_tristate, 0, offsetof(Network, bridge_mac_authentication_bypass) BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0 BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0 BridgeFDB.Destination, config_parse_fdb_destination, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 7de0027aae..080e184ae1 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -456,6 +456,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .bridge_proxy_arp_wifi = -1, .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID, .multicast_router = _MULTICAST_ROUTER_INVALID, + .bridge_locked = -1, + .bridge_mac_authentication_bypass = -1, .bridge_vlan_pvid = BRIDGE_VLAN_KEEP_PVID, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index b61914ea7a..cec5f98d5b 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -297,6 +297,8 @@ struct Network { uint32_t cost; uint16_t priority; MulticastRouter multicast_router; + int bridge_locked; + int bridge_mac_authentication_bypass; /* Bridge VLAN */ uint16_t bridge_vlan_pvid; diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 467fadb3ea..2f4a6c6e03 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -320,6 +320,18 @@ static int link_configure_fill_message( return r; } + if (link->network->bridge_locked >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_LOCKED, link->network->bridge_locked); + if (r < 0) + return r; + } + + if (link->network->bridge_mac_authentication_bypass >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MAB, link->network->bridge_mac_authentication_bypass); + if (r < 0) + return r; + } + r = sd_netlink_message_close_container(req); if (r < 0) return r; diff --git a/test/test-network/conf/25-bridge.netdev b/test/test-network/conf/25-bridge.netdev index 9e7fa545b2..398637b643 100644 --- a/test/test-network/conf/25-bridge.netdev +++ b/test/test-network/conf/25-bridge.netdev @@ -18,3 +18,4 @@ VLANProtocol=802.1ad STP=true MulticastIGMPVersion=3 FDBMaxLearned=4 +LinkLocalLearning=no diff --git a/test/test-network/conf/26-bridge-slave-interface-2.network b/test/test-network/conf/26-bridge-slave-interface-2.network index 42b197eeef..c76f17201f 100644 --- a/test/test-network/conf/26-bridge-slave-interface-2.network +++ b/test/test-network/conf/26-bridge-slave-interface-2.network @@ -10,3 +10,5 @@ Bridge=bridge99 [Bridge] Priority=0 +Locked=true +MACAuthenticationBypass=true diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index b4339e282b..c865fbdbe3 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1804,6 +1804,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping'))) self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state'))) self.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version'))) + self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'no_linklocal_learn'))) output = networkctl_status('bridge99') print(output) @@ -5911,6 +5912,9 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): output = check_output('bridge -d link show test1') print(output) self.check_bridge_port_attr('bridge99', 'test1', 'priority', '0') + self.assertIn('locked on', output) + if ' mab ' in output: # This is new in kernel and iproute2 v6.2 + self.assertIn('mab on', output) def test_bridge_property(self): copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',