#!/bin/sh -efu

# Help message
help()
{
    cat <<EOF
${0##*/} -- create network namespace for runns.

Usage: ${0##*/} [options]

Options:
  -h | --help     print this help message
  -n | --name     namespace name (default is "vpnX", where X is a number)
  -s | --section  specify section to load from /etc/runns.conf
  -i | --int      interface name (default is "eth0")
  -o | --out      interface name for veth in default network namespace
                  (default is "vpnX", where X is a number)
  -r | --resolve  path to resolv.conf file for this network namespace
EOF
    exit 0
}

# Parse command line arguments
TMPARGS="$(getopt -n "$0" -o n:,i:,o:,s:,r:,h -l name:,int:,out:,section:,resolve:,help -- "$@")" ||
	  help
eval set -- "$TMPARGS"

NS=
INT=
OUT=
CONFIG=/etc/runns.conf
SECTION=
RESOLVE=
while :
do
    case "$1" in
        --)
            shift; break ;;
        -n|--name)
            shift; NS="$1" ;;
        -i|--int)
            shift; INT="$1" ;;
        -o|--out)
            shift; OUT="$1" ;;
        -s|--section)
            shift; SECTION="$1" ;;
        -r|--resolve)
            shift; RESOLVE="$1" ;;
        *)
            help ;;
    esac
    shift
done

# Load configuration file if it was specified
if [ -n "$SECTION" ]; then
    # Read network namespace if it is not set
    [ -n "$NS" ] || NS="$(awk -F '=' -v section="[$SECTION]" '
BEGIN{ IGNORECASE = 1}
$0==section { flag=1; next }
/\[/{ flag=0; next }
flag && $1=="NetworkNamespace"{ print $2; exit }
' $CONFIG)"
    # Read interfaces name if it is not set
    [ -n "$INT" ] || INT="$(awk -F '=' -v section="[$SECTION]" '
BEGIN{ IGNORECASE = 1}
$0==section { flag=1; next }
/\[/{ flag=0; next }
flag && $1=="InterfaceIn"{ print $2; exit }
' $CONFIG)"
    [ -n "$OUT" ] || OUT="$(awk -F '=' -v section="[$SECTION]" '
BEGIN{ IGNORECASE = 1}
$0==section { flag=1; next }
/\[/{ flag=0; next }
flag && $1=="InterfaceOut"{ print $2; exit }
' $CONFIG)"
    # Read resolve.conf
    [ -n "$RESOLVE" ] || RESOLVE="$(awk -F '=' -v section="[$SECTION]" '
BEGIN{ IGNORECASE = 1}
$0==section { flag=1; next }
/\[/{ flag=0; next }
flag && $1=="Resolve"{ print $2; exit }
' $CONFIG)"
fi

# If NS is empty set the default value "vpn$MAXNS"
if [ -z "$NS" ]; then
    MAXNS=$(find /var/run/netns/ -maxdepth 1 -type f -regex '.*/vpn[0-9]+' -printf '%f\n' |
                awk 'BEGIN{max=0} match($0, /[0-9]+/){n=substr($0, RSTART, RLENGTH); if (max<n) {max=n}} END{print ++n}')
    [ -n "$MAXNS" ] || MAXNS=1
    NS="vpn$MAXNS"
fi
# Set IPv4 third octet
IP4C="${MAXNS:-0}"

# Set default name of interfaces in the case if they did not set yet
[ -n "$INT" ] || INT="eth0"
[ -n "$OUT" ] || OUT="${NS}d"

# Output setup information
cat <<EOF
Using following options:
- Network namespace: $NS
- Network interface: $OUT
- IPv4 address for ${NS}d: 172.24.${IP4C}.1/24
- resolv.conf: ${RESOLVE:-using default}
EOF

# Add network namespace and interfaces
ip netns add "$NS"
ip link add "$OUT" type veth peer name "${NS}r"
ip link set "${NS}r" netns "$NS"
echo "Network namespace created"
# Set IP address and up interface in the default network namespace
ip addr add "172.24.${IP4C}.1/24" dev "$OUT"
ip link set "$OUT" up
echo "Interface  $OUT is up"
# Setup interface in another network namespace
ip netns exec "$NS" ip link set "${NS}r" name eth0
ip netns exec "$NS" ip addr add "172.24.${IP4C}.2/24" dev eth0
ip netns exec "$NS" ip link set eth0 up
ip netns exec "$NS" ip route add default via "172.24.${IP4C}.1"
echo "Interface ${NS}r in ${NS} is up and ready"
# Enable IPv4 forward
echo "1" > /proc/sys/net/ipv4/ip_forward
# Add NAT rule
iptables -t nat -A POSTROUTING -s 172.24.${IP4C}.0/24 -o "$INT" -j MASQUERADE
echo "NAT rule is ready"
# Setup resolv.conf for this network namespace
if [ -n "${RESOLVE-}" ]
then
    [ -d "/etc/netns/$NS" ] || mkdir -p "/etc/netns/$NS"
    [ ! -f "$RESOLVE" ] || cp "$RESOLVE" "/etc/netns/$NS"
fi

# ACCEPT FORWARD
iptables -A FORWARD -i $INT -o $OUT -j ACCEPT
iptables -A FORWARD -o $INT -i $OUT -j ACCEPT

# Launch commands in the new network namespace
awk -F '=' -v section="[$SECTION]" '
BEGIN{ IGNORECASE = 1}
$0==section { flag=1; next }
/\[/{ flag=0; next }
flag && $1=="vpn"{ print $2 }
' $CONFIG | (while read arg; do ip netns exec "$NS" openvpn --daemon "openvpn-$NS" --config "$arg"; done)
