diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 0fb749a1f9..48326a25c1 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -427,7 +427,8 @@
has been unsuccessful for some time. (IPv4 link-local address autoconfiguration will usually
happen in parallel with repeated attempts to acquire a DHCPv4 lease).
- Defaults to when Bridge= is set or when the specified
+ Defaults to when KeepMaster= or
+ Bridge= is set or when the specified
MACVLAN=/MACVTAP= has Mode=passthru, or
otherwise.
@@ -923,6 +924,20 @@ IPv6Token=prefixstable:2002:da8:1::
An integer greater than or equal to 1280 bytes. When unset, the kernel's default will be used.
+
+ KeepMaster=
+ Bond=
+ Bridge=
+ VRF=
+
+ Takes a boolean value. When enabled, the current master interface index will not be
+ changed, and BatmanAdvanced=, Bond=,
+ Bridge=, and VRF= settings are ignored. This may be
+ useful when a netdev with a master interface is created by another program, e.g.
+ systemd-nspawn1.
+ Defaults to false.
+
+
BatmanAdvanced=
Bond=
diff --git a/src/network/networkd-bridge-mdb.c b/src/network/networkd-bridge-mdb.c
index 6cd10c78e0..ed8a10cad8 100644
--- a/src/network/networkd-bridge-mdb.c
+++ b/src/network/networkd-bridge-mdb.c
@@ -190,16 +190,6 @@ int link_request_static_bridge_mdb(Link *link) {
if (hashmap_isempty(link->network->bridge_mdb_entries_by_section))
goto finish;
- if (!link->network->bridge) {
- if (!streq_ptr(link->kind, "bridge")) {
- log_link_warning(link, "Link is neither a bridge master nor a bridge port, ignoring [BridgeMDB] sections.");
- goto finish;
- } else if (link->manager->bridge_mdb_on_master_not_supported) {
- log_link_debug(link, "Kernel seems not to support bridge MDB entries on bridge master, ignoring [BridgeMDB] sections.");
- goto finish;
- }
- }
-
HASHMAP_FOREACH(mdb, link->network->bridge_mdb_entries_by_section) {
r = link_queue_request(link, REQUEST_TYPE_BRIDGE_MDB, mdb, false,
&link->static_bridge_mdb_messages, bridge_mdb_configure_handler, NULL);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 405ddccc8b..ef5248fa38 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -79,6 +79,7 @@ SR-IOV.Trust, config_parse_sr_iov_boolean,
SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, 0
SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, 0
Network.Description, config_parse_string, 0, offsetof(Network, description)
+Network.KeepMaster, config_parse_bool, 0, offsetof(Network, keep_master)
Network.BatmanAdvanced, config_parse_ifname, 0, offsetof(Network, batadv_name)
Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name)
Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index cdca91c7c2..4aee6fa43c 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -135,6 +135,26 @@ int network_verify(Network *network) {
"%s: Conditions in the file do not match the system environment, skipping.",
network->filename);
+ if (network->keep_master) {
+ if (network->batadv_name)
+ log_warning("%s: BatmanAdvanced= set with KeepMaster= enabled, ignoring BatmanAdvanced=.",
+ network->filename);
+ if (network->bond_name)
+ log_warning("%s: Bond= set with KeepMaster= enabled, ignoring Bond=.",
+ network->filename);
+ if (network->bridge_name)
+ log_warning("%s: Bridge= set with KeepMaster= enabled, ignoring Bridge=.",
+ network->filename);
+ if (network->vrf_name)
+ log_warning("%s: VRF= set with KeepMaster= enabled, ignoring VRF=.",
+ network->filename);
+
+ network->batadv_name = mfree(network->batadv_name);
+ network->bond_name = mfree(network->bond_name);
+ network->bridge_name = mfree(network->bridge_name);
+ network->vrf_name = mfree(network->vrf_name);
+ }
+
(void) network_resolve_netdev_one(network, network->batadv_name, NETDEV_KIND_BATADV, &network->batadv);
(void) network_resolve_netdev_one(network, network->bond_name, NETDEV_KIND_BOND, &network->bond);
(void) network_resolve_netdev_one(network, network->bridge_name, NETDEV_KIND_BRIDGE, &network->bridge);
@@ -174,7 +194,7 @@ int network_verify(Network *network) {
if (network->link_local < 0) {
network->link_local = ADDRESS_FAMILY_IPV6;
- if (network->bridge)
+ if (network->keep_master || network->bridge)
network->link_local = ADDRESS_FAMILY_NO;
else {
NetDev *netdev;
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 95c86e7230..81d484ce53 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -84,6 +84,7 @@ struct Network {
LIST_HEAD(Condition, conditions);
/* Master or stacked netdevs */
+ bool keep_master;
NetDev *batadv;
NetDev *bridge;
NetDev *bond;
diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c
index 06aa2067f7..21e20f10f5 100644
--- a/src/network/networkd-setlink.c
+++ b/src/network/networkd-setlink.c
@@ -515,9 +515,16 @@ static bool link_is_ready_to_call_set_link(Request *req) {
switch (op) {
case SET_LINK_BOND:
case SET_LINK_BRIDGE:
+ if (!link->master_set)
+ return false;
+ if (link->network->keep_master && link->master_ifindex <= 0)
+ return false;
+ break;
case SET_LINK_BRIDGE_VLAN:
if (!link->master_set)
return false;
+ if (link->network->keep_master && link->master_ifindex <= 0 && !streq_ptr(link->kind, "bridge"))
+ return false;
break;
case SET_LINK_CAN:
/* Do not check link->set_flgas_messages here, as it is ok even if link->flags
@@ -672,8 +679,18 @@ int link_request_to_set_bond(Link *link) {
assert(link);
assert(link->network);
- if (!link->network->bond)
- return 0;
+ if (!link->network->bond) {
+ Link *master;
+
+ if (!link->network->keep_master)
+ return 0;
+
+ if (link_get_master(link, &master) < 0)
+ return 0;
+
+ if (!streq_ptr(master->kind, "bond"))
+ return 0;
+ }
return link_request_set_link(link, SET_LINK_BOND, link_set_bond_handler, NULL);
}
@@ -682,8 +699,18 @@ int link_request_to_set_bridge(Link *link) {
assert(link);
assert(link->network);
- if (!link->network->bridge)
- return 0;
+ if (!link->network->bridge) {
+ Link *master;
+
+ if (!link->network->keep_master)
+ return 0;
+
+ if (link_get_master(link, &master) < 0)
+ return 0;
+
+ if (!streq_ptr(master->kind, "bridge"))
+ return 0;
+ }
return link_request_set_link(link, SET_LINK_BRIDGE, link_set_bridge_handler, NULL);
}
@@ -695,8 +722,18 @@ int link_request_to_set_bridge_vlan(Link *link) {
if (!link->network->use_br_vlan)
return 0;
- if (!link->network->bridge && !streq_ptr(link->kind, "bridge"))
- return 0;
+ if (!link->network->bridge && !streq_ptr(link->kind, "bridge")) {
+ Link *master;
+
+ if (!link->network->keep_master)
+ return 0;
+
+ if (link_get_master(link, &master) < 0)
+ return 0;
+
+ if (!streq_ptr(master->kind, "bridge"))
+ return 0;
+ }
return link_request_set_link(link, SET_LINK_BRIDGE_VLAN, link_set_bridge_vlan_handler, NULL);
}
@@ -763,6 +800,11 @@ int link_request_to_set_master(Link *link) {
assert(link);
assert(link->network);
+ if (link->network->keep_master) {
+ link->master_set = true;
+ return 0;
+ }
+
link->master_set = false;
if (link->network->batadv || link->network->bond || link->network->bridge || link->network->vrf)
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index a3711cb77d..71ead0df9c 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -178,6 +178,7 @@ MultiPathRoute=
TCPAdvertisedMaximumSegmentSize=
NextHop=
[Network]
+KeepMaster=
IPv6DuplicateAddressDetection=
IPMasquerade=
ProxyARP=