150 lines
6.5 KiB
Bash
Executable File
150 lines
6.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Setup TAP/Bridge networking for QEMU VM
|
|
# Defaults:
|
|
# BRIDGE_NAME=br-rego-vpn
|
|
# TAP_NAME=tap0
|
|
# BRIDGE_CIDR=100.100.0.1/24
|
|
# VM_NET_IP=100.100.0.2
|
|
# VM_SUBNET=100.100.0.0/24
|
|
|
|
set -e
|
|
|
|
BRIDGE_NAME="${BRIDGE_NAME:-br-rego-vpn}"
|
|
TAP_NAME="${TAP_NAME:-tap0}"
|
|
BRIDGE_CIDR="${BRIDGE_CIDR:-100.100.0.1}"
|
|
VM_NET_IP="${VM_NET_IP:-100.100.0.2}"
|
|
VM_SUBNET="${VM_SUBNET:-100.100.0.0}"
|
|
TARGET_IP="${TARGET_IP:-10.35.33.230}"
|
|
|
|
# Optional second bridge/tap for a second VM NIC (pure L2 with the container).
|
|
# This is opt-in: set BRIDGE2_NAME and TAP2_NAME (and optionally BRIDGE2_CIDR).
|
|
BRIDGE2_NAME="${BRIDGE2_NAME:-}"
|
|
TAP2_NAME="${TAP2_NAME:-}"
|
|
BRIDGE2_CIDR="${BRIDGE2_CIDR:-}"
|
|
BRIDGE2_UPLINK_IF="${BRIDGE2_UPLINK_IF:-}"
|
|
|
|
if [[ "$BRIDGE_CIDR" != */* ]]; then
|
|
BRIDGE_CIDR="$BRIDGE_CIDR/24"
|
|
fi
|
|
|
|
if [[ "$VM_SUBNET" != */* ]]; then
|
|
VM_SUBNET="$VM_SUBNET/24"
|
|
fi
|
|
|
|
# Pick the outbound interface based on the container's default route.
|
|
# (In Docker, this is not always eth1 when multiple networks are attached.)
|
|
WAN_IF="$(ip route show default 0.0.0.0/0 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="dev"){print $(i+1); exit}}')"
|
|
if [ -z "${WAN_IF}" ]; then
|
|
WAN_IF="eth1"
|
|
fi
|
|
|
|
# Ensure bridge exists
|
|
if ! ip link show "$BRIDGE_NAME" &>/dev/null; then
|
|
ip link add "$BRIDGE_NAME" type bridge
|
|
fi
|
|
|
|
# Ensure bridge has address and is up
|
|
ip addr show dev "$BRIDGE_NAME" | grep -qF "$BRIDGE_CIDR" || ip addr add "$BRIDGE_CIDR" dev "$BRIDGE_NAME" 2>/dev/null || true
|
|
ip link set "$BRIDGE_NAME" up
|
|
|
|
# Ensure TAP exists
|
|
if ! ip link show "$TAP_NAME" &>/dev/null; then
|
|
ip tuntap add "$TAP_NAME" mode tap
|
|
fi
|
|
|
|
# Ensure TAP is attached and up
|
|
ip link set "$TAP_NAME" master "$BRIDGE_NAME" 2>/dev/null || true
|
|
ip link set "$TAP_NAME" up
|
|
|
|
# Optional second bridge/tap (no NAT rules are applied here)
|
|
if [ -n "$BRIDGE2_NAME" ] || [ -n "$TAP2_NAME" ]; then
|
|
if [ -z "$BRIDGE2_NAME" ] || [ -z "$TAP2_NAME" ]; then
|
|
echo "[rego-tunnel] WARN: BRIDGE2_NAME and TAP2_NAME must both be set to enable the second bridge"
|
|
else
|
|
# Optionally bridge an existing container uplink into BRIDGE2 (e.g. eth0/eth1)
|
|
# so the VM NIC shares the same L2 network as that uplink.
|
|
if [ -n "$BRIDGE2_UPLINK_IF" ]; then
|
|
if ! ip link show "$BRIDGE2_UPLINK_IF" &>/dev/null; then
|
|
echo "[rego-tunnel] WARN: BRIDGE2_UPLINK_IF=$BRIDGE2_UPLINK_IF not found; skipping uplink bridging"
|
|
BRIDGE2_UPLINK_IF=""
|
|
fi
|
|
fi
|
|
|
|
if ! ip link show "$BRIDGE2_NAME" &>/dev/null; then
|
|
ip link add "$BRIDGE2_NAME" type bridge
|
|
fi
|
|
ip link set "$BRIDGE2_NAME" up
|
|
|
|
# If an uplink interface is provided, move its IPv4 address to the bridge.
|
|
# This keeps the container reachable on that network while allowing the VM
|
|
# to participate in the same L2 broadcast domain.
|
|
if [ -n "$BRIDGE2_UPLINK_IF" ]; then
|
|
UPLINK_CIDR="$(ip -o -4 addr show dev "$BRIDGE2_UPLINK_IF" 2>/dev/null | awk '{print $4}' | head -n1)"
|
|
UPLINK_DEFAULT_GW="$(ip route show default dev "$BRIDGE2_UPLINK_IF" 2>/dev/null | awk '{print $3}' | head -n1)"
|
|
UPLINK_DEFAULT_METRIC="$(ip route show default dev "$BRIDGE2_UPLINK_IF" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="metric"){print $(i+1); exit}}')"
|
|
if [ -z "$BRIDGE2_CIDR" ] && [ -n "$UPLINK_CIDR" ]; then
|
|
BRIDGE2_CIDR="$UPLINK_CIDR"
|
|
fi
|
|
if [ -n "$BRIDGE2_CIDR" ]; then
|
|
if [[ "$BRIDGE2_CIDR" != */* ]]; then
|
|
BRIDGE2_CIDR="$BRIDGE2_CIDR/24"
|
|
fi
|
|
ip addr show dev "$BRIDGE2_NAME" | grep -qF "$BRIDGE2_CIDR" || ip addr add "$BRIDGE2_CIDR" dev "$BRIDGE2_NAME" 2>/dev/null || true
|
|
fi
|
|
# Remove IP from uplink and enslave it into the bridge
|
|
ip addr flush dev "$BRIDGE2_UPLINK_IF" 2>/dev/null || true
|
|
ip link set "$BRIDGE2_UPLINK_IF" master "$BRIDGE2_NAME" 2>/dev/null || true
|
|
ip link set "$BRIDGE2_UPLINK_IF" up 2>/dev/null || true
|
|
|
|
# If the uplink carried the container default route, it may have been removed
|
|
# by the address flush. Restore it on the bridge.
|
|
if [ -n "$UPLINK_DEFAULT_GW" ]; then
|
|
if ip route show default 2>/dev/null | grep -q '^default '; then
|
|
ip route replace default via "$UPLINK_DEFAULT_GW" dev "$BRIDGE2_NAME" ${UPLINK_DEFAULT_METRIC:+metric "$UPLINK_DEFAULT_METRIC"} 2>/dev/null || true
|
|
else
|
|
ip route add default via "$UPLINK_DEFAULT_GW" dev "$BRIDGE2_NAME" ${UPLINK_DEFAULT_METRIC:+metric "$UPLINK_DEFAULT_METRIC"} 2>/dev/null || true
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if ! ip link show "$TAP2_NAME" &>/dev/null; then
|
|
ip tuntap add "$TAP2_NAME" mode tap
|
|
fi
|
|
ip link set "$TAP2_NAME" master "$BRIDGE2_NAME" 2>/dev/null || true
|
|
ip link set "$TAP2_NAME" up
|
|
echo "[rego-tunnel] Second bridge enabled: $BRIDGE2_NAME (tap $TAP2_NAME)${BRIDGE2_UPLINK_IF:+ uplink $BRIDGE2_UPLINK_IF}"
|
|
fi
|
|
fi
|
|
|
|
# Enable IP forwarding
|
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
|
|
# Setup NAT/masquerade for outbound traffic from VM
|
|
iptables -t nat -C POSTROUTING -s "$VM_SUBNET" -o "$WAN_IF" -j MASQUERADE 2>/dev/null || \
|
|
iptables -t nat -A POSTROUTING -s "$VM_SUBNET" -o "$WAN_IF" -j MASQUERADE
|
|
|
|
# Ensure forwarding between the VM bridge and outbound interface
|
|
iptables -C FORWARD -i "$BRIDGE_NAME" -o "$WAN_IF" -j ACCEPT 2>/dev/null || \
|
|
iptables -A FORWARD -i "$BRIDGE_NAME" -o "$WAN_IF" -j ACCEPT
|
|
iptables -C FORWARD -i "$WAN_IF" -o "$BRIDGE_NAME" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
|
|
iptables -A FORWARD -i "$WAN_IF" -o "$BRIDGE_NAME" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
|
|
# Forward traffic destined for VPN networks to VM (TARGET_IP defaults to IBM i)
|
|
# The VM will route this through its VPN tunnel
|
|
iptables -C FORWARD -d "$TARGET_IP" -j ACCEPT 2>/dev/null || iptables -A FORWARD -d "$TARGET_IP" -j ACCEPT
|
|
iptables -C FORWARD -s "$TARGET_IP" -j ACCEPT 2>/dev/null || iptables -A FORWARD -s "$TARGET_IP" -j ACCEPT
|
|
|
|
# Route to TARGET_IP through VM
|
|
ip route add "$TARGET_IP" via "$VM_NET_IP" 2>/dev/null || true
|
|
|
|
echo "Network setup complete"
|
|
echo "Bridge: $BRIDGE_NAME = $BRIDGE_CIDR"
|
|
echo "TAP: $TAP_NAME attached to $BRIDGE_NAME"
|
|
echo "Route: $TARGET_IP via $VM_NET_IP (VM)"
|
|
echo "Outbound interface: ${WAN_IF}"
|
|
|
|
if [ -n "$BRIDGE2_NAME" ] && [ -n "$TAP2_NAME" ]; then
|
|
echo "Bridge2: $BRIDGE2_NAME${BRIDGE2_CIDR:+ = $BRIDGE2_CIDR}"
|
|
echo "TAP2: $TAP2_NAME attached to $BRIDGE2_NAME"
|
|
fi
|