feat: Improve NAT networking (#1300)

This commit is contained in:
Kroese
2026-07-18 15:34:46 +02:00
committed by GitHub
parent 82a62c0240
commit 5d5bbbcf4d
+89 -12
View File
@@ -325,10 +325,9 @@ natGuestIP() {
local ip="$1"
local start="" guest="" subnet=""
local second="" third="" fourth="" rc=""
local second="" third="" rc=""
third=$(cut -d. -f3 <<< "$ip")
fourth=$(cut -d. -f4 <<< "$ip")
if [[ "$ip" == "172.30."* ]]; then
start="31"
@@ -336,11 +335,8 @@ natGuestIP() {
start="30"
fi
guest=$(guestIP "172.$start.$third.$fourth" 2)
fourth="${guest##*.}"
for (( second=start; second<=254; second++ )); do
guest="172.$second.$third.$fourth"
guest=$(guestIP "172.$second.$third.0" 2)
subnet=$(networkCIDR "$guest") || return 1
if subnetInUse "$subnet"; then
@@ -355,7 +351,7 @@ natGuestIP() {
done
for (( second=30; second<start; second++ )); do
guest="172.$second.$third.$fourth"
guest=$(guestIP "172.$second.$third.0" 2)
subnet=$(networkCIDR "$guest") || return 1
if subnetInUse "$subnet"; then
@@ -1031,13 +1027,16 @@ checkExistingTables() {
local own_rule="--comment[[:space:]]+\"?$rule_tag\"?([[:space:]]|\$)"
rules=$(
iptables -t nat -S PREROUTING 2>/dev/null |
{
iptables -t nat -S PREROUTING 2>/dev/null || true
iptables -t nat -S OUTPUT 2>/dev/null || true
} |
awk '$1 == "-A"' |
grep -Ev -- "$own_rule" || true
)
conflicts=$(grep -E -- \
'^-A PREROUTING .*(-j DNAT|-j REDIRECT)( |$)' \
'^-A (PREROUTING|OUTPUT) .*(-j DNAT|-j REDIRECT)( |$)' \
<<< "$rules" || true)
if [ -n "$conflicts" ]; then
@@ -1071,6 +1070,7 @@ checkExistingTables() {
fi
showRules nat PREROUTING "NAT PREROUTING" "$rule_tag"
showRules nat OUTPUT "NAT OUTPUT" "$rule_tag"
showRules filter FORWARD "filter FORWARD" "$rule_tag"
showRules nat POSTROUTING "NAT POSTROUTING" "$rule_tag"
@@ -1217,6 +1217,17 @@ applyTables() {
return 1
fi
# Process locally generated traffic addressed to the container uplink.
if ! runTableRule "$silent" table_error \
iptables -t nat -A OUTPUT \
-d "$UPLINK" \
-m addrtype --dst-type LOCAL \
-m comment --comment "$rule_tag" \
-j "$dnat_chain"; then
tableError "$silent" "$table_error"
return 1
fi
# Hack for guest VMs complaining about "bad udp checksums in 5 packets".
runTableRule "Y" table_error \
iptables -t mangle -A POSTROUTING \
@@ -1369,12 +1380,34 @@ clearTables() {
return 0
}
hasTaggedRules() {
local save="$1"
local rules=""
local dnat_chain="QEMU_DNAT"
local rule_tag="$dnat_chain"
local own_rule="--comment[[:space:]]+\"?$rule_tag\"?([[:space:]]|\$)"
local tagged_rule="^:${dnat_chain}[[:space:]]|$own_rule"
# Return 2 when the backend cannot be inspected.
if ! rules=$("$save" 2>/dev/null); then
return 2
fi
if grep -Eq -- "$tagged_rule" <<< "$rules"; then
return 0
fi
return 1
}
configureTables() {
local ip="$1"
local subnet="$2"
local preferred=""
local alternate="" rc=0
local alternate=""
local alternate_save=""
local preferred_clean="N"
local alternate_dirty="N"
@@ -1393,6 +1426,50 @@ configureTables() {
return 1 ;;
esac
# Inspect the alternate backend without changing the active alternative.
alternate_save=$(command -v "iptables-$alternate-save" 2>/dev/null || true)
if [ -n "$alternate_save" ]; then
if hasTaggedRules "$alternate_save"; then
# Only switch backends when stale QEMU rules were positively found.
if ! setTables "$alternate"; then
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
warn "failed to select the $alternate IP tables backend for cleanup!"
return 1
fi
if ! clearTables; then
alternate_dirty="Y"
fi
# Always restore the originally selected backend after cleanup.
if ! setTables "$preferred"; then
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
warn "failed to restore the preferred $preferred IP tables backend!"
return 1
fi
if enabled "$alternate_dirty"; then
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
warn "failed to clean up the existing $alternate IP tables configuration!"
return 1
fi
else
local rc=$?
# An unavailable alternate backend does not affect normal startup.
if (( rc == 2 )) && enabled "$DEBUG"; then
warn "failed to inspect the $alternate IP tables backend!"
fi
fi
fi
# Try the preferred backend first.
if clearTables; then
@@ -1413,7 +1490,7 @@ configureTables() {
else
rc=$?
local rc=$?
# The preferred backend was accessible, but its rules could not be removed.
# Do not switch while partial or stale rules may still be active.
@@ -1460,7 +1537,7 @@ configureTables() {
else
rc=$?
local rc=$?
# Only mark the alternate backend dirty when it was accessible but cleanup failed.
if (( rc == 1 )); then