diff --git a/man/networkd.conf.xml b/man/networkd.conf.xml
index cac1d3b6e7..9daa1254b6 100644
--- a/man/networkd.conf.xml
+++ b/man/networkd.conf.xml
@@ -131,6 +131,12 @@
for more details about the sysctl options. Defaults to unset and the sysctl options will not be
changed.
+ If an interface is configured with a .network file that enables IPMasquerade=
+ for IPv4 (that is, ipv4 or both), this setting is implied
+ unless explicitly specified. See IPMasquerade= in
+ systemd.network5
+ for more details.
+
@@ -145,6 +151,12 @@
for more details about the sysctl options. Defaults to unset and the sysctl options will not be
changed.
+ If an interface is configured with a .network file that enables IPMasquerade=
+ for IPv6 (that is, ipv6 or both), this setting is implied
+ unless explicitly specified. See IPMasquerade= in
+ systemd.network5
+ for more details.
+
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index ebcb6d0481..0374dc7cdc 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -888,15 +888,15 @@ DuplicateAddressDetection=none
ipv6, both, or no. Defaults to
no. Note. Any positive boolean values such as yes or
true are now deprecated. Please use one of the values above. Specifying
- ipv4 or both implies IPv4Forwarding=,
- unless it is explicitly specified. Similarly for IPv6Forwarding= when
- ipv6 or both is specified. These implications are only on
- this interface. Hence, to make the IP packet forwarding works,
- IPv4Forwarding=/IPv6Forwarding= need to be enabled on an
- upstream interface, or globally enabled by specifying them in
- networkd.conf5.
- See IPv4Forwarding=/IPv6Forwarding= in the above for more
- details.
+ ipv4 or both implies IPv4Forwarding=
+ settings in both .network file for this interface and the global
+ networkd.conf5
+ unless they are explicitly specified. Similarly for IPv6Forwarding= when
+ ipv6 or both is specified. See
+ IPv4Forwarding=/IPv6Forwarding= in the above for the per-link
+ settings, and
+ networkd.conf5
+ for the global settings.
diff --git a/src/libsystemd-network/sd-lldp-tx.c b/src/libsystemd-network/sd-lldp-tx.c
index 2b822af01a..01c476ecde 100644
--- a/src/libsystemd-network/sd-lldp-tx.c
+++ b/src/libsystemd-network/sd-lldp-tx.c
@@ -590,6 +590,7 @@ int sd_lldp_tx_stop(sd_lldp_tx *lldp_tx) {
return 1;
}
+
int sd_lldp_tx_start(sd_lldp_tx *lldp_tx) {
usec_t delay;
int r;
diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c
index f48781ef8b..cc22fbf2d6 100644
--- a/src/network/networkd-lldp-tx.c
+++ b/src/network/networkd-lldp-tx.c
@@ -86,6 +86,36 @@ int link_lldp_tx_configure(Link *link) {
return 0;
}
+int link_lldp_tx_update_capabilities(Link *link) {
+ int r;
+
+ assert(link);
+
+ if (!link->lldp_tx)
+ return 0; /* disabled, or not configured yet. */
+
+ r = sd_lldp_tx_set_capabilities(link->lldp_tx,
+ SD_LLDP_SYSTEM_CAPABILITIES_STATION |
+ SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE |
+ SD_LLDP_SYSTEM_CAPABILITIES_ROUTER,
+ (link_get_ip_forwarding(link, AF_INET) > 0 || link_get_ip_forwarding(link, AF_INET6) > 0) ?
+ SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : SD_LLDP_SYSTEM_CAPABILITIES_STATION);
+ if (r < 0)
+ return r;
+
+ if (sd_lldp_tx_is_running(link->lldp_tx)) {
+ r = sd_lldp_tx_stop(link->lldp_tx);
+ if (r < 0)
+ return r;
+
+ r = sd_lldp_tx_start(link->lldp_tx);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static const char * const lldp_multicast_mode_table[_SD_LLDP_MULTICAST_MODE_MAX] = {
[SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE] = "nearest-bridge",
[SD_LLDP_MULTICAST_MODE_NON_TPMR_BRIDGE] = "non-tpmr-bridge",
diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h
index 73757f1725..346eb5cb1c 100644
--- a/src/network/networkd-lldp-tx.h
+++ b/src/network/networkd-lldp-tx.h
@@ -6,5 +6,6 @@
typedef struct Link Link;
int link_lldp_tx_configure(Link *link);
+int link_lldp_tx_update_capabilities(Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_lldp_multicast_mode);
diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c
index 68c23e0eb7..a454322fd0 100644
--- a/src/network/networkd-sysctl.c
+++ b/src/network/networkd-sysctl.c
@@ -7,7 +7,9 @@
#include "af-list.h"
#include "missing_network.h"
#include "networkd-link.h"
+#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
+#include "networkd-ndisc.h"
#include "networkd-network.h"
#include "networkd-sysctl.h"
#include "socket-util.h"
@@ -130,7 +132,7 @@ int link_get_ip_forwarding(Link *link, int family) {
return link->manager->ip_forwarding[family == AF_INET6];
}
-static int link_set_ip_forwarding(Link *link, int family) {
+static int link_set_ip_forwarding_impl(Link *link, int family) {
int r, t;
assert(link);
@@ -151,6 +153,65 @@ static int link_set_ip_forwarding(Link *link, int family) {
return 0;
}
+static int link_reapply_ip_forwarding(Link *link, int family) {
+ int r, ret = 0;
+
+ assert(link);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
+
+ (void) link_set_ip_forwarding_impl(link, family);
+
+ r = link_lldp_tx_update_capabilities(link);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not update LLDP capabilities, ignoring: %m"));
+
+ if (family == AF_INET6 && !link_ndisc_enabled(link)) {
+ r = ndisc_stop(link);
+ if (r < 0)
+ RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery, ignoring: %m"));
+
+ ndisc_flush(link);
+ }
+
+ return ret;
+}
+
+static int link_set_ip_forwarding(Link *link, int family) {
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->network);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+
+ if (!link_is_configured_for_family(link, family))
+ return 0;
+
+ /* When IPMasquerade= is enabled and the global setting is unset, enable _global_ IP forwarding, and
+ * re-apply per-link setting for all links. */
+ if (FLAGS_SET(link->network->ip_masquerade, family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6) &&
+ link->manager->ip_forwarding[family == AF_INET6] < 0) {
+
+ link->manager->ip_forwarding[family == AF_INET6] = true;
+ manager_set_ip_forwarding(link->manager, family);
+
+ Link *other;
+ HASHMAP_FOREACH(other, link->manager->links_by_index) {
+ r = link_reapply_ip_forwarding(other, family);
+ if (r < 0)
+ link_enter_failed(other);
+ }
+
+ return 0;
+ }
+
+ /* Otherwise, apply per-link setting for _this_ link. */
+ return link_set_ip_forwarding_impl(link, family);
+}
+
static int link_set_ipv4_rp_filter(Link *link) {
assert(link);
diff --git a/test/TEST-13-NSPAWN/test.sh b/test/TEST-13-NSPAWN/test.sh
index 5c85b0c5dc..3ab52d0eee 100755
--- a/test/TEST-13-NSPAWN/test.sh
+++ b/test/TEST-13-NSPAWN/test.sh
@@ -17,6 +17,8 @@ test_append_files() {
# For virtual wlan interface.
instmods mac80211_hwsim
+ # for IPMasquerade=
+ instmods "=net/netfilter"
generate_module_dependencies
# Create a dummy container "template" with a minimal toolset, which we can
diff --git a/test/test-functions b/test/test-functions
index e41e5792a3..8f08d84ad8 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -1454,10 +1454,31 @@ install_missing_libraries() {
[[ -e "$libgcc_s" ]] && inst_library "$libgcc_s"
done < <(ldconfig -p | awk '/\/libgcc_s.so.1$/ { print $4 }')
- local lib path
+ local lib path libs
# A number of dependencies is now optional via dlopen, so the install
# script will not pick them up, since it looks at linkage.
- for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive libgcrypt libkmod; do
+ libs=(
+ libarchive
+ libbpf
+ libcryptsetup
+ libdw
+ libelf
+ libfido2
+ libgcrypt
+ libidn
+ libidn2
+ libip4tc
+ libkmod
+ libqrencode
+ p11-kit-1
+ pwquality
+ tss2-esys
+ tss2-mu
+ tss2-rc
+ tss2-tcti-device
+ xkbcommon
+ )
+ for lib in "${libs[@]}"; do
ddebug "Searching for $lib via pkg-config"
if pkg-config --exists "$lib"; then
path="$(pkg-config --variable=libdir "$lib")"
diff --git a/test/units/TEST-13-NSPAWN.nspawn.sh b/test/units/TEST-13-NSPAWN.nspawn.sh
index 0c74d2e16e..2de9f9079f 100755
--- a/test/units/TEST-13-NSPAWN.nspawn.sh
+++ b/test/units/TEST-13-NSPAWN.nspawn.sh
@@ -943,6 +943,17 @@ matrix_run_one() {
return 0
}
+testcase_api_vfs() {
+ local api_vfs_writable
+
+ for api_vfs_writable in yes no network; do
+ matrix_run_one no no $api_vfs_writable
+ matrix_run_one yes no $api_vfs_writable
+ matrix_run_one no yes $api_vfs_writable
+ matrix_run_one yes yes $api_vfs_writable
+ done
+}
+
testcase_check_os_release() {
# https://github.com/systemd/systemd/issues/29185
local base common_opts root
@@ -1003,11 +1014,46 @@ EOF
rm -fr "$root"
}
-run_testcases
+testcase_ip_masquerade() {
+ local root
-for api_vfs_writable in yes no network; do
- matrix_run_one no no $api_vfs_writable
- matrix_run_one yes no $api_vfs_writable
- matrix_run_one no yes $api_vfs_writable
- matrix_run_one yes yes $api_vfs_writable
-done
+ if ! command -v networkctl >/dev/null; then
+ echo "This test requires systemd-networkd, skipping..."
+ return 0
+ fi
+
+ systemctl unmask systemd-networkd.service
+ systemctl edit --runtime --stdin systemd-networkd.service --drop-in=debug.conf <