diff --git a/src/network.sh b/src/network.sh index 286793c..024d876 100644 --- a/src/network.sh +++ b/src/network.sh @@ -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/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