#!/bin/sh

set -eu

VPN_CONTAINER="vpn-container"
VPN_NETWORK="vpn_network_default"
CLIENTS_FILE="/path/to/vpn-platform/gateway/config/vpn-clients.conf"

ROUTING_TABLE="201"
RULE_PRIORITY_START="11000"

LAN1_INTERFACE="eth0"
LAN1_ADDRESS="192.168.100.11"
LAN1_NETWORK="192.168.100.0/24"

LAN2_INTERFACE="eth1"
LAN2_ADDRESS="192.168.200.11"
LAN2_NETWORK="192.168.200.0/24"

CONTAINER_LAN_INTERFACE="eth0"
TUN_INTERFACE="tun0"

HOST_CHAIN="B4_VPN_CLIENTS"
CONTAINER_FORWARD_CHAIN="B4_VPN_FORWARD"
CONTAINER_NAT_CHAIN="B4_VPN_NAT"

log() {
    printf '%s\n' "[INFO] $*"
}

fail() {
    printf '%s\n' "[FEHLER] $*" >&2
    exit 1
}

validate_client() {
    client_name="$1"
    client_cidr="$2"

    [ -n "$client_name" ] || fail "Ein Gerätename fehlt."

    case "$client_cidr" in
        192.168.100.[0-9]/32|\
        192.168.100.[0-9][0-9]/32|\
        192.168.100.[0-9][0-9][0-9]/32)
            ;;
        *)
            fail "Ungültiger Client-Eintrag: $client_name|$client_cidr"
            ;;
    esac

    client_ip="${client_cidr%/32}"
    client_last_octet="${client_ip##*.}"

    [ "$client_last_octet" -ge 1 ] 2>/dev/null \
        && [ "$client_last_octet" -le 254 ] 2>/dev/null \
        || fail "Ungültige Hostadresse: $client_cidr"

    case "$client_ip" in
        192.168.100.1|\
        192.168.100.5|\
        192.168.100.10|\
        192.168.100.20|\
        192.168.100.250)
            fail "Gesperrte Infrastrukturadresse in der Clientliste: $client_ip"
            ;;
    esac
}

[ "$(id -u)" -eq 0 ] || fail "Das Skript muss mit sudo oder als root laufen."
[ -r "$CLIENTS_FILE" ] || fail "Clientliste fehlt oder ist nicht lesbar: $CLIENTS_FILE"

docker inspect "$VPN_CONTAINER" >/dev/null 2>&1 \
    || fail "VPN-Container wurde nicht gefunden: $VPN_CONTAINER"

[ "$(docker inspect -f '{{.State.Running}}' "$VPN_CONTAINER")" = "true" ] \
    || fail "VPN-Container läuft nicht: $VPN_CONTAINER"

CLIENT_COUNT=0
while IFS='|' read -r CLIENT_NAME CLIENT_CIDR CLIENT_DESCRIPTION
do
    case "$CLIENT_NAME" in
        ""|\#*)
            continue
            ;;
    esac

    validate_client "$CLIENT_NAME" "$CLIENT_CIDR"
    CLIENT_COUNT=$((CLIENT_COUNT + 1))
done < "$CLIENTS_FILE"

[ "$CLIENT_COUNT" -gt 0 ] || fail "Die Clientliste enthält keine aktiven Geräte."

VPN_IP="$(docker inspect \
    --format "{{with index .NetworkSettings.Networks \"$VPN_NETWORK\"}}{{.IPAddress}}{{end}}" \
    "$VPN_CONTAINER")"

VPN_SUBNET="$(docker network inspect \
    --format '{{(index .IPAM.Config 0).Subnet}}' \
    "$VPN_NETWORK")"

VPN_DOCKER_GATEWAY="$(docker network inspect \
    --format '{{(index .IPAM.Config 0).Gateway}}' \
    "$VPN_NETWORK")"

VPN_DOCKER_BRIDGE="$(docker network inspect \
    --format '{{index .Options "com.docker.network.bridge.name"}}' \
    "$VPN_NETWORK" 2>/dev/null || true)"

if [ -z "$VPN_DOCKER_BRIDGE" ] || [ "$VPN_DOCKER_BRIDGE" = "<no value>" ]; then
    VPN_NETWORK_ID="$(docker network inspect --format '{{.Id}}' "$VPN_NETWORK")"
    VPN_DOCKER_BRIDGE="docker-$(printf '%.8s' "$VPN_NETWORK_ID")"
fi

[ -n "$VPN_IP" ] || fail "VPN-Container-IP konnte nicht ermittelt werden."
[ -n "$VPN_SUBNET" ] || fail "Docker-Subnetz konnte nicht ermittelt werden."
[ -n "$VPN_DOCKER_GATEWAY" ] || fail "Docker-Gateway konnte nicht ermittelt werden."
ip link show "$VPN_DOCKER_BRIDGE" >/dev/null 2>&1 \
    || fail "Docker-Bridge wurde nicht gefunden: $VPN_DOCKER_BRIDGE"

log "VPN-Container-IP: $VPN_IP"
log "VPN-Netzwerk: $VPN_NETWORK"
log "VPN-Subnetz: $VPN_SUBNET"
log "VPN-Docker-Gateway: $VPN_DOCKER_GATEWAY"
log "VPN-Docker-Bridge: $VPN_DOCKER_BRIDGE"
log "Aktive VPN-Geräte: $CLIENT_COUNT"

sysctl -w net.ipv4.ip_forward=1 >/dev/null

ip route flush table "$ROUTING_TABLE" 2>/dev/null || true

ip route add table "$ROUTING_TABLE" \
    "$LAN1_NETWORK" \
    dev "$LAN1_INTERFACE" \
    scope link \
    src "$LAN1_ADDRESS"

ip route add table "$ROUTING_TABLE" \
    "$LAN2_NETWORK" \
    dev "$LAN2_INTERFACE" \
    scope link \
    src "$LAN2_ADDRESS"

ip route add table "$ROUTING_TABLE" \
    "$VPN_SUBNET" \
    dev "$VPN_DOCKER_BRIDGE" \
    scope link \
    src "$VPN_DOCKER_GATEWAY"

ip route add table "$ROUTING_TABLE" \
    default via "$VPN_IP" \
    dev "$VPN_DOCKER_BRIDGE"

# Tabelle 201 ist ausschließlich für die verwalteten VPN-Geräte reserviert.
while ip rule del table "$ROUTING_TABLE" 2>/dev/null
do
    :
done

iptables -N "$HOST_CHAIN" 2>/dev/null || true
iptables -F "$HOST_CHAIN"
iptables -C DOCKER-USER -j "$HOST_CHAIN" 2>/dev/null \
    || iptables -I DOCKER-USER 1 -j "$HOST_CHAIN"

docker exec "$VPN_CONTAINER" \
    iptables-legacy -N "$CONTAINER_FORWARD_CHAIN" 2>/dev/null || true
docker exec "$VPN_CONTAINER" \
    iptables-legacy -F "$CONTAINER_FORWARD_CHAIN"
docker exec "$VPN_CONTAINER" \
    iptables-legacy -C FORWARD -j "$CONTAINER_FORWARD_CHAIN" 2>/dev/null \
    || docker exec "$VPN_CONTAINER" \
        iptables-legacy -I FORWARD 1 -j "$CONTAINER_FORWARD_CHAIN"

docker exec "$VPN_CONTAINER" \
    iptables-legacy -t nat -N "$CONTAINER_NAT_CHAIN" 2>/dev/null || true
docker exec "$VPN_CONTAINER" \
    iptables-legacy -t nat -F "$CONTAINER_NAT_CHAIN"
docker exec "$VPN_CONTAINER" \
    iptables-legacy -t nat -C POSTROUTING -j "$CONTAINER_NAT_CHAIN" 2>/dev/null \
    || docker exec "$VPN_CONTAINER" \
        iptables-legacy -t nat -I POSTROUTING 1 -j "$CONTAINER_NAT_CHAIN"

RULE_PRIORITY="$RULE_PRIORITY_START"

while IFS='|' read -r CLIENT_NAME CLIENT_CIDR CLIENT_DESCRIPTION
do
    case "$CLIENT_NAME" in
        ""|\#*)
            continue
            ;;
    esac

    validate_client "$CLIENT_NAME" "$CLIENT_CIDR"

    # Alte Einzelgeräteregeln aus der bisherigen Einzellösung entfernen.
    while iptables -D DOCKER-USER \
        -s "$CLIENT_CIDR" \
        -i "$LAN1_INTERFACE" \
        -o "$VPN_DOCKER_BRIDGE" \
        -j ACCEPT 2>/dev/null
    do
        :
    done

    while iptables -D DOCKER-USER \
        -d "$CLIENT_CIDR" \
        -i "$VPN_DOCKER_BRIDGE" \
        -o "$LAN1_INTERFACE" \
        -m conntrack \
        --ctstate RELATED,ESTABLISHED \
        -j ACCEPT 2>/dev/null
    do
        :
    done

    while docker exec "$VPN_CONTAINER" \
        iptables-legacy -D FORWARD \
        -s "$CLIENT_CIDR" \
        -i "$CONTAINER_LAN_INTERFACE" \
        -o "$TUN_INTERFACE" \
        -j ACCEPT 2>/dev/null
    do
        :
    done

    while docker exec "$VPN_CONTAINER" \
        iptables-legacy -D FORWARD \
        -d "$CLIENT_CIDR" \
        -i "$TUN_INTERFACE" \
        -o "$CONTAINER_LAN_INTERFACE" \
        -m conntrack \
        --ctstate RELATED,ESTABLISHED \
        -j ACCEPT 2>/dev/null
    do
        :
    done

    while docker exec "$VPN_CONTAINER" \
        iptables-legacy -t nat -D POSTROUTING \
        -s "$CLIENT_CIDR" \
        -o "$TUN_INTERFACE" \
        -j MASQUERADE 2>/dev/null
    do
        :
    done

    ip rule add \
        priority "$RULE_PRIORITY" \
        from "$CLIENT_CIDR" \
        lookup "$ROUTING_TABLE"

    iptables -A "$HOST_CHAIN" \
        -s "$CLIENT_CIDR" \
        -i "$LAN1_INTERFACE" \
        -o "$VPN_DOCKER_BRIDGE" \
        -j ACCEPT

    iptables -A "$HOST_CHAIN" \
        -d "$CLIENT_CIDR" \
        -i "$VPN_DOCKER_BRIDGE" \
        -o "$LAN1_INTERFACE" \
        -m conntrack \
        --ctstate RELATED,ESTABLISHED \
        -j ACCEPT

    docker exec "$VPN_CONTAINER" \
        iptables-legacy -A "$CONTAINER_FORWARD_CHAIN" \
        -s "$CLIENT_CIDR" \
        -i "$CONTAINER_LAN_INTERFACE" \
        -o "$TUN_INTERFACE" \
        -j ACCEPT

    docker exec "$VPN_CONTAINER" \
        iptables-legacy -A "$CONTAINER_FORWARD_CHAIN" \
        -d "$CLIENT_CIDR" \
        -i "$TUN_INTERFACE" \
        -o "$CONTAINER_LAN_INTERFACE" \
        -m conntrack \
        --ctstate RELATED,ESTABLISHED \
        -j ACCEPT

    docker exec "$VPN_CONTAINER" \
        iptables-legacy -t nat -A "$CONTAINER_NAT_CHAIN" \
        -s "$CLIENT_CIDR" \
        -o "$TUN_INTERFACE" \
        -j MASQUERADE

    log "$CLIENT_NAME ($CLIENT_CIDR) wurde mit Priorität $RULE_PRIORITY eingebunden."
    RULE_PRIORITY=$((RULE_PRIORITY + 1))
done < "$CLIENTS_FILE"

ip route flush cache 2>/dev/null || true

log "Mehrgeräte-Routing auf dem VPN-Gateway wurde erfolgreich gesetzt."