#!/bin/sh -E
#
# ipobfs        IP obfuscator
# chkconfig:    - 91 19
# processname:  ipobfs
WITHOUT_RC_COMPAT=1
. /etc/init.d/functions

BASE=ipobfs
CONFIGDIR="/etc/$BASE"
PIDFILEDIR="/run"

SourceIfNotEmpty "$CONFIGDIR/default"

get_iface() {
  case "$ROUTE" in
    *.*.*.*) ip route get $ROUTE | \
             sed -En "/$ROUTE via/s/.* dev ([^ ]+).*/\1/p";;
          *) echo "$ROUITE";;
  esac
}

start_fw() { # channel client/server
  . "$CONFIGDIR/$1"
  XPROTO=$((PROTO^PROTOXOR))
  IFACE=`get_iface`
  case $FIREWALL in
    iptables)
      IPTI="-t mangle -I"; IPTJ="-j NFQUEUE --queue-num $QUEUE --queue-bypass"
      case "$2" in
        client) PORTLOC="0>>22&0x3C@0>>16&0xFFFF"; PORTDIR="--dport";;
        server) PORTLOC="0>>22&0x3C@0&0xFFFF"; PORTDIR="--sport";;
      esac
      iptables -t mangle -N ipobfs_$1_in
      iptables -t mangle -N ipobfs_$1_out
      iptables $IPTI ipobfs_$1_in -i $IFACE -p $XPROTO -m u32 --u32 "$PORTLOC=$PORT" $IPTJ
      iptables $IPTI ipobfs_$1_out -o $IFACE -p $PROTO $PORTDIR $PORT $IPTJ
      iptables $IPTI PREROUTING -j ipobfs_$1_in
      iptables $IPTI POSTROUTING -j ipobfs_$1_out
      ;;
    nft)
      case "$PROTOXOR:$2" in
        0:client) PORTDIR="th dport"; PORTLOC="@th,0,16";;
        0:server) PORTDIR="th sport"; PORTLOC="@th,16,16";;
        *:client) PORTDIR="@nh,176,16"; PORTLOC="@nh,160,16";;
        *:server) PORTDIR="@nh,160,16"; PORTLOC="@nh,176,16";;
        # TODO IPv4 options may be non-null, so not 160/176 here
      esac
      NFTJ="counter queue flags bypass to $QUEUE"
      nft add table ip ipobfs_$1
      nft add chain ip ipobfs_$1 ipobfs_$1_in "{ type filter hook prerouting priority filter; }"
      nft add chain ip ipobfs_$1 ipobfs_$1_out "{ type filter hook postrouting priority filter; }"
      nft add rule ip ipobfs_$1 ipobfs_$1_in "iif $IFACE @nh,72,8 $XPROTO $PORTLOC $PORT $NFTJ"
      nft add rule ip ipobfs_$1 ipobfs_$1_out "oif $IFACE ip protocol $PROTO $PORTDIR $PORT $NFTJ"
      ;;
    *)
      ;;
   esac
}   

stop_fw() { # channel
  . "$CONFIGDIR/$1"
  XPROTO=$((PROTO^PROTOXOR))
  case $FIREWALL in
    iptables)
      iptables -t mangle -D PREROUTING -j ipobfs_$1_in
      iptables -t mangle -D POSTROUTING -j ipobfs_$1_out
      iptables -t mangle -F ipobfs_$1_in
      iptables -t mangle -F ipobfs_$1_out
      iptables -t mangle -X ipobfs_$1_in
      iptables -t mangle -X ipobfs_$1_out
      ;;
    nft)
      nft delete table ipobfs_$1
      ;;
    *)
      ;;
   esac
}   

status_fw() { # channel
  . "$CONFIGDIR/$1"
  XPROTO=$((PROTO^PROTOXOR))
  case $FIREWALL in
    iptables)
      iptables-save -c | grep --color "queue-num $QUEUE"
    ;;
    nft)
      nft list table ipobfs_$1  
    ;;
  esac
}

start_channel() { # channel [foreground channel [addopts]]
  . "$CONFIGDIR/$1"
  COMMON="--qnum=$QUEUE --ipproto-xor=$PROTOXOR --data-xor=$DATAXOR --data-xor-offset=$XOROFFSET --data-xor-len=$XORLEN"
  if [ -z "$2" ]; then
    ipobfs $COMMON $IPOBFSFLAGS --pidfile "$PIDFILEDIR/ipobfs.$1.pid" --daemon
  else
    shift 3
    exec ipobfs $COMMON $IPOBFSFLAGS "$@" # foreground execution!
  fi
}

stop_channel() { # channel
  . "$CONFIGDIR/$1"
  kill `cat "$PIDFILEDIR/ipobfs.$1.pid"`
}


status_channel() { # channel
  . "$CONFIGDIR/$1"
  ps  --no-headers -efwwq `cat "$PIDFILEDIR/ipobfs.$1.pid"`
}

start() { # client/server
  for CHANNEL in $CHANNELS; do
    start_fw $CHANNEL $1
    start_channel $CHANNEL
  done
}

stop() {
  for CHANNEL in $CHANNELS; do
    stop_fw $CHANNEL
    stop_channel $CHANNEL
  done
}

status() {
  for CHANNEL in $CHANNELS; do
    status_fw $CHANNEL
    status_channel $CHANNEL
  done
}

case $0/$1/$2/$3 in
  *server/start//)      start server;;
  *server/restart//)    stop; start server;;
  */start//)            start client;;
  */stop//)             stop;;
  */restart//)          stop; start client;;
  */status//)           status;;
  # single services: mode/action/channel/part
  *server/start/*/firewall)     start_fw $2 server;;
  */start/*/firewall)           start_fw $2 client;;
  */stop/*/firewall)            stop_fw $2;;
  */start/*/*)                  shift; start_channel $1 foreground "$@";;
  */status/*/*)         status_fw $2;;
esac
