#!/bin/bash

mark=$1
def_gateway=$2
def_interface=$3
vpn_gateway=$4
vpn_interface=$5
remote_ip=$6
netclass=$7
allow_lan=$8
mode=$9
# v6 default-gateway, v6 VPN-gateway, physical v6 default interface, and physical v6
# default address. Empty when no IPv6 is configured. def_ip_v6 is the authoritative
# "host has physical v6" signal: def_interface_v6 falls back to the v4 interface name on
# the engine side, so its presence alone does not mean the host has a v6 default.
def_gateway_v6=${10}
vpn_gateway_v6=${11}
def_interface_v6=${12}
def_ip_v6=${13}
# WireGuard fwmark in hex (e.g. "0xca6c"), matching iproute2's `ip rule show` output. Used to
# locate the WG `not fwmark` rule independently of its routing-table id, which may vary. Must
# track marks::kWireGuardFwMark in the helper (passed in by cgroups.cpp).
wg_fwmark=${14}

net_cls_root="`mount -l -t cgroup | grep "net_cls on" | cut -d ' ' -f 3 | head -n 1`"
if [ ! -f "$net_cls_root/windscribe/net_cls.classid" ]; then
    modprobe cls_cgroup
    if [ $? -ne 0 ]; then
        echo "Could not load cls_cgroup module"
        exit 1
    fi

    net_cls_root="`mount -l -t cgroup | grep "net_cls on" | cut -d ' ' -f 3 | head -n 1`"
    if [ -z "$net_cls_root" ]; then
        if [ -d /sys/fs/cgroup/net_cls ]; then
            # on some distros, cgroups v2 net_cls is mounted and it may be a symlink.  If so, unmount it and mount v1
            mount -o remount,rw /sys/fs/cgroup

            link="`readlink /sys/fs/cgroup/net_cls`"
            if [ -n "$link" ]; then
                umount /sys/fs/cgroup/${link}
            fi
            rm -f /sys/fs/cgroup/net_cls
        fi
        mkdir -p /sys/fs/cgroup/net_cls
        mount -t cgroup -onet_cls net_cls /sys/fs/cgroup/net_cls

        net_cls_root="`mount -l -t cgroup | grep "net_cls on" | cut -d ' ' -f 3 | head -n 1`"
        if [ -z "$net_cls_root" ]; then
            echo "Could not find cgroup root"
            exit 1
        fi
    fi

    mkdir -p /etc/iproute2 # create dir if it doesn't exist
    touch /etc/iproute2/rt_tables # create file if it doesn't exist

    # Add "windscribe" table definition if it doesn't exist
    if [ "`grep -c "69 windscribe" /etc/iproute2/rt_tables`" -eq 0 ]; then
        echo "69 windscribe" >> /etc/iproute2/rt_tables
    fi
    if [ "`grep -c "70 windscribe_include" /etc/iproute2/rt_tables`" -eq 0 ]; then
        echo "70 windscribe_include" >> /etc/iproute2/rt_tables
    fi

    # Create separate routing table for packets with our mark
    ip rule add fwmark "$mark" priority 16384 table windscribe
    ip route add default via "$def_gateway" dev "$def_interface" table windscribe
    ip route add 10.255.255.0/24 dev "$vpn_interface" table windscribe

    # Create separate routing table for packets that should always go into tunnel
    ip route add default via "$vpn_gateway" dev "$vpn_interface" table windscribe_include
    ip route add "$remote_ip" dev "$def_interface" table windscribe_include

    # IPv6 sibling of the rules above. 10.255.255.0/24 and $remote_ip are v4-only
    # and have no v6 counterpart. Use the v6 default interface for the physical route;
    # the engine already falls it back to the v4 interface name when no distinct v6
    # interface exists, so it is non-empty whenever a v6 default was found.
    v6_iface="$def_interface_v6"
    if [ -n "$def_gateway_v6" ] || [ -n "$def_ip_v6" ] || [ -n "$vpn_gateway_v6" ]; then
        ip -6 rule add fwmark "$mark" priority 16384 table windscribe
    fi
    if [ -n "$def_gateway_v6" ]; then
        ip -6 route add default via "$def_gateway_v6" dev "$v6_iface" table windscribe
    elif [ -n "$def_ip_v6" ]; then
        # Dual-stack host whose v6 default route is on-link / point-to-point (no nexthop):
        # route marked v6 out the physical v6 interface directly. Gated on a real physical
        # v6 address, not the interface name (which falls back to the v4 NIC), so a v4-only
        # host or a ::/0 reject route does not install a bogus default here.
        ip -6 route add default dev "$v6_iface" table windscribe
    elif [ -n "$vpn_gateway_v6" ]; then
        # Genuinely v4-only host with a dual-stack VPN: terminate marked v6 packets here so
        # they don't fall through to the WG 'not fwmark' rule and leak into the tunnel.
        ip -6 route add unreachable default table windscribe
    fi
    if [ -n "$vpn_gateway_v6" ]; then
        ip -6 route add default via "$vpn_gateway_v6" dev "$vpn_interface" table windscribe_include
    fi

    # Create net_cls id
    mkdir "$net_cls_root/windscribe"
    echo "$netclass" > "$net_cls_root/windscribe/net_cls.classid"
fi

# Allow IP rules to consult main routing table first, ignoring /0 or /1 routes
ip rule add priority 16383 table main suppress_prefixlength 1
# head -n1: the fwmark is a fixed constant, so a stale WG rule surviving an unclean disconnect
# would make grep return multiple lines and break the $((priority - 1)) arithmetic below.
priority="`ip rule show | grep -i "$wg_fwmark" | cut -d ":" -f1 | head -n1`" # priority of WireGuard rule (matched by fwmark), if it exists
if [ -n "$priority" ]; then
    # WG uses the same ip rule mechanism, just adjust the WG rule
    ip rule add priority $((priority - 1)) table main suppress_prefixlength 0
else
    # For non-WG protocols, remove rule forcing other traffic into the tunnel
    ip rule del priority 16385 table windscribe_include
fi

# IPv6 sibling of the priority adjust above. Same gate as the windscribe-table v6 rule above.
if [ -n "$def_gateway_v6" ] || [ -n "$def_ip_v6" ] || [ -n "$vpn_gateway_v6" ]; then
    ip -6 rule add priority 16383 table main suppress_prefixlength 1
    priority_v6="`ip -6 rule show | grep -i "$wg_fwmark" | cut -d ":" -f1 | head -n1`"
    if [ -n "$priority_v6" ]; then
        ip -6 rule add priority $((priority_v6 - 1)) table main suppress_prefixlength 0
    else
        ip -6 rule del priority 16385 table windscribe_include
    fi
fi

# Inclusive mode with a v4-only tunnel on a host with physical v6: tunneled (unmarked) apps have no
# v6 tunnel route, so their v6 would fall through to the physical default in main and leak around
# the VPN. Terminate it before main; marked bypass traffic already escaped at 16384, LAN at 16383.
# Must stay below the block above, whose non-WG branch deletes the 16385 rule.
if [ "$mode" = "inclusive" ] && [ -z "$vpn_gateway_v6" ] && { [ -n "$def_gateway_v6" ] || [ -n "$def_ip_v6" ]; }; then
    ip -6 route add unreachable default table windscribe_include
    ip -6 rule add priority 16385 table windscribe_include
else
    ip -6 rule del priority 16385 table windscribe_include
fi

# make sure to exit with code 0 since an above command may fail if we are adding a duplicate rule or
# deleting a non-existent rule; these are not errors
exit 0
