mirror of
https://github.com/systemd/systemd.git
synced 2025-03-24 14:50:17 +03:00
network: bridge: add support for configuring locked ports (#36150)
"Recently" (as of 5.18) the Linux kernel gained the ability of locking bridge ports to restrict network access to authenticated hosts only. This is implemented by disabling automated learning and dropping incoming traffic from unknown hosts. User space is then expected to add fdb entries for authenticated hosts. Once a fdb entry exist, traffic for that host will be forwarded as expected. This was later extended with "Mac Authentication Bypass", where the locking was extended to fdb entries. In this mode the kernel adds fdb entries again automatically, but they are locked by default. To properly configure this, add two network options and one netdev option: * `LinkLocalLearning=` to prevent the kernel from creating unlocked entries based on link-local traffic, which would bypass any authentication. Needed when enabling learning on a locked port. * `Locked=` to allow setting a bridge port to locked. * `MACAuthenticationBypass=` to allow enabling Mac Authentication Bypass on a port. Requires learning to be enabled on the port as well (and consequently `LinkLocalLearning` disabled on the bridge). An authenticator (e.g. hostapd) is still needed to do the actual authentication, the kernel only provides the access control.
This commit is contained in:
commit
d90c01d02c
man
src
libsystemd/sd-netlink
network
test/test-network
@ -456,6 +456,16 @@
|
||||
<xi:include href="version-info.xml" xpointer="v257"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>LinkLocalLearning=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes a boolean. This enables learning source addresses from link local frames. When unset, the
|
||||
kernel's default will be used.
|
||||
</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
@ -4624,6 +4624,24 @@ ServerAddress=192.168.0.1/24</programlisting>
|
||||
<xi:include href="version-info.xml" xpointer="v234"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>Locked=</varname></term>
|
||||
<listitem>
|
||||
<para>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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
<term><varname>MACAuthenticationBypass=</varname></term>
|
||||
<listitem>
|
||||
<para>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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
@ -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[] = {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -18,3 +18,4 @@ VLANProtocol=802.1ad
|
||||
STP=true
|
||||
MulticastIGMPVersion=3
|
||||
FDBMaxLearned=4
|
||||
LinkLocalLearning=no
|
||||
|
@ -10,3 +10,5 @@ Bridge=bridge99
|
||||
|
||||
[Bridge]
|
||||
Priority=0
|
||||
Locked=true
|
||||
MACAuthenticationBypass=true
|
||||
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user