#!/bin/bash
#
#  Copyright (C) 2014-2015 STROMASYS.
#  All rights reserved.
#
# Requires 
# root id
# packadges:
#     bridge-utils (optional)
#     tunctl (optional, need only if command 'ip tuntap' not worked)
#     ethtool
#     vconfig (optional)

#set -xv
if [ -x ./chnprep.sh ] ; then
    . ./chnprep.sh library
elif [ -x /sbin/chnprep.sh ] ; then
    . /sbin/chnprep.sh library
else
    echo "No file chnprep.sh"
    exit
fi

echo "CHARON Network Configuration Utility, STROMASYS (c) 2016 Version $VER_NO"
echo ""
ifacestatusformat="%-20s%-20s%-35s\n"

id=`id -u`
if [ x$id != x0 ] ; then
    echo "Please run under root!";
    exit;
fi

if [[ ! -f $NCU_CFG ]]; then
    touch  $NCU_CFG
fi

if [[ ! -f $NCU_BR_CFG ]]; then
    touch  $NCU_BR_CFG
fi

if [[ ! -f $NCU_VLAN_CFG ]]; then
    touch  $NCU_VLAN_CFG
fi

SAVELCALL=$LC_ALL
export LC_ALL=C

p_bridge()
{
    dname=$1
    tap_num=$2
    
    inf=$dname
    
    
    if [[ ! -f /usr/sbin/brctl ]]; then
	echo Install 'bridge-utils' packadges please...
	exit 5
    fi
    if [ $iptuntap != 0 ] ; then
	if [[ ! -f /usr/sbin/tunctl ]]; then
	    echo Install 'tunctl' packadge please...
	    return
	fi
    fi

    if grep -w "^$inf\$" $NCU_CFG 1>/dev/null ; then
	echo ERROR: $inf is dedicated for CHARON - Release it to host please
	return
    fi
    ethx=`$IFCONFIG -a | grep "^${dname}:" | sed 's/:\s.*//'`
    br_name=br0_$inf
    if [ x$ethx == x ] ; then
	echo "Interface '$dname' does not exist";
	return;
    fi
    eth=$dname

    br_n=`$IFCONFIG -a | grep "^${br_name}:" | sed 's/:\s.*//'`
    if [ x$br_name == x$br_n ] ; then
	echo ERROR: $eth already used in bridge $br_n
	return
    fi

    if grep "^${eth}\s" $NCU_BR_CFG 1>/dev/null ; then
	echo ERROR: $eth already used in bridge
	return
    fi
    echo $eth $tap_num >> $NCU_BR_CFG
    while true; do
	ethtool -K $eth rx off
	ethtool -K $eth tx off
	ethtool -K $eth sg off
	ethtool -K $eth gso off
	ethtool -K $eth gro off
	ethtool -K $eth txvlan off
	ethtool -K $eth rxvlan off
	break;
    done  >> $NCU_LOG

    echo "Binding bridge to $eth..."

#
# The TCP/IP configuration over eth
#

   # ethip=`$IFCONFIG $eth | grep netmask | sed 's/.*inet\s*//g'`
    ethip=`$IFCONFIG $eth | grep Mask | sed 's/.*inet addr:\s*//g' | awk '{print $1}' `
#    route=`route | grep default | sed 's/default\s*//g;s/ \s*.*//g'`
#    route_through=`route  | grep default | sed 's/.*\s//g'`
    route=`ip route | grep default |  awk '{print $3}'`
    route_through=`ip route | grep default |   sed 's/.*dev //g' |  awk '{print $1}'`
    if [ "x$route_through" != "x$eth" ] ; then
	route=''
    fi

  
    echo $br_name $ethip Route[$route] $route_through

    echo -n "Forming the bridge: ..1"
   
    echo -n "..2"
    $IFCONFIG $eth 0.0.0.0 promisc up
    
    echo -n "..3"
    /usr/sbin/brctl addbr $br_name
    echo -n "..4"
    /usr/sbin/brctl addif $br_name $eth
    echo -n "..5"
    for (( td=0 ; td < tap_num; td ++))
    do
	echo -n "."
	if [ $iptuntap != 0 ] ; then
	    tdev=`/usr/sbin/tunctl -b`
	else
	    tdevn=`ip tuntap | wc -l`
	    tdev=tap$tdevn
	    ip tuntap add $tdev mode tap 
	fi
	echo -n ". addif $tdev "
	/usr/sbin/brctl addif $br_name $tdev
	/sbin/ifconfig $tdev promisc up
    done
    echo -n "..7"
     /usr/sbin/brctl stp $br_name off
    echo -n "..8"

    if [ "x$ethip" != x ] ; then
	/sbin/ifconfig $br_name $ethip up
	if [ "x$route" != "x" ] ; then
	    echo -n "..9"
	    /sbin/route add default gw $route
	fi
    else
	/sbin/ifconfig $br_name up
    fi
    echo -n " "
    echo  " done!"
    echo "Formed bridge $br_name  attached over $eth...";

}  2>>$NCU_LOG

p_help()
{
    echo "Usage:"
    echo "ncu [options] [interfaces] ..[options] [interfaces]"
    echo 'Options:'
    echo '--help|-h                                     Display help'
    echo '--list|-l                                     List available interfaces'
    echo '--dedicate|-d <interface1>..<interfaceN>      Dedicate interfaces to CHARON'
    echo '--release|-r <interface1>..<interfaceN>       Release interfaces to host'
    if [[ n$BRTUN = nyes ]] ; then
	echo '--bridge|-b  <interface> <num_taps>       Create BRIDGE and Tap interfaces'
	echo '-c  <interface1>                          Remove BRIDGE'
    fi
    if [[ n$VLANPRESENT = nyes ]] ; then
	echo '--vlan | -v <interface>                   Create VLAN interfaces'
	echo '-w  <interface1>                          Remove VLAN'
    fi
}

p_status() 
{
    printf "$ifacestatusformat" "Interfaces" "Dedicated to" "State"
    printf "$ifacestatusformat" "----------" "------------" "-----"
    breth=""
    if [[ n$BRTUN = nyes ]] ; then
	brs=`/usr/sbin/brctl show | wc -l`;
	if [[ $brs > 1 ]] ; then
	    for i in `/usr/sbin/brctl show br000 2>/dev/null | sed 's/.*\s//g'`; do
		if [ "x$i" == xinterfaces ] ; then
		    echo a >/dev/null
		elif [ "x`echo $i | grep tap`" != x ] ; then
		    echo aa	 >/dev/null
		else
		    breth=$i;
		fi
	    done
	fi
    fi
    for eth in `nmcli dev | awk '{print $1}'` ; do
	if [ $eth != "DEVICE" ] ; then
	    STT=`nmcli dev | grep "^$eth\s" | awk '{print $3}'`
	    if [[ N$STT == Ndisconnected ||  N$STT == Nunmanaged ]] ; then
		TOHOST=" from"
	    else
		TOHOST=" to"
	    fi
	    if [[ $eth == tap* ]] ; then
		STT="connected"
		TOHOST=" to"
	    fi
	    if grep "^$eth\$" $NCU_CFG 1>/dev/null ; then
		printf "$ifacestatusformat" "$eth" "CHARON" "$STT$TOHOST host"
	    else
		if [[ n$BRTUN = nyes ]] ; then
		    if /usr/sbin/brctl show | grep "\s${eth}\$" 1>/dev/null ; then
			printf "$ifacestatusformat" "$eth" "bridge" "$STT$TOHOST bridge"
		    else 
			printf "$ifacestatusformat" "$eth" "host" "$STT$TOHOST host"
		    fi
		else
		    printf "$ifacestatusformat" "$eth" "host" "$STT$TOHOST host"
		fi
	    fi
	fi
    done
    test "$BRTUN" = "yes" && echo "=================================================================" && /usr/sbin/brctl show
    test "$VLANPRESENT" = "yes" && echo "========================== VLAN =================================" && cat /proc/net/vlan/config | tail --lines=+3 | awk '{ print $1;}'
    echo "================================================================="
} 

p_dedicate()
{
    inf=$1
    if grep -w $inf $NCU_CFG 1>/dev/null ; then
	echo ERROR: $inf already  dedicate to CHARON
    else
	echo Turning off offloading for $inf.. Please wait
	printf "$inf\n" >> $NCU_CFG
	if [[ $NMCLI_VER < "0.9.9.0" ]] ; then
	    nmcli dev disconnect iface $inf
	else
	    nmcli dev disconnect $inf
	fi
	while true; do
	    sleep 1
	    ethtool -K $inf rx off
	    ethtool -K $inf tx off
	    ethtool -K $inf sg off
	    ethtool -K $inf gso off
	    ethtool -K $inf gro off
	    ethtool -K $inf txvlan off
	    ethtool -K $inf rxvlan off
	    #sleep 2
	    #ethtool -k $inf
	    break;
	done  >> $NCU_LOG
    fi
}  2>>$NCU_LOG

p_release()
{
    inf=$1
    if grep -w $inf $NCU_CFG 1>/dev/null ; then
	grep -vw $inf $NCU_CFG > $NCU_BAK
	mv $NCU_BAK $NCU_CFG
    else
	echo ERROR: $inf already  dedicate to Host
	read -p "Are you sure to release it anyway [no]:" ans
	if [ x$ans != xyes ] ; then
	    return
	fi
    fi
    if [[ $NMCLI_VER < "0.9.9.0" ]] ; then
	for UUID in `nmcli con | grep $inf | sed -e "s/Auto //"  | sed -e "s/System //" | awk '{print $2}'` ; do
	    #echo UUID $UUID for $inf
	    nmcli -p con up uuid  $UUID
	done
    else
	UUID=`nmcli dev show $inf | fgrep "CONNECTIONS.AVAILABLE-CONNECTIONS[1]:" | awk '{print $2}'`
	if [ -n "$UUID" ] ; then
	    #echo UUID $UUID for $inf
	    nmcli -p con up uuid  $UUID
	else
	    for UUID in `nmcli con | grep $inf | sed -e "s/Auto //"  | sed -e "s/System //" | awk '{print $2}'` ; do
		#echo UUID $UUID for $inf
		nmcli -p con up uuid  $UUID
	    done
	fi
    fi
    while true; do
	sleep 1
	ethtool -K $inf rx on
	ethtool -K $inf tx on
	ethtool -K $inf sg on
	ethtool -K $inf gso on
	ethtool -K $inf gro on
	ethtool -K $inf txvlan on
	ethtool -K $inf rxvlan on
	break;
    done >> $NCU_LOG
}  

p_create_vlan() {
    if $IFCONFIG -a | grep "^${1}:" >/dev/null ; then 
	idlan=$2
	test -z "$idlan" && idlan=0
	if /sbin/vconfig add $1 $idlan ; then
	    echo -n "${1}.${idlan}" >> $NCU_VLAN_CFG
	    if [ -n "$3" ] ; then
		if [ -n "$4" ] ; then
		    $IFCONFIG ${1}.${idlan} $3 netmask $4 && echo -n " $3 $4" >> $NCU_VLAN_CFG
		else
		    $IFCONFIG ${1}.${idlan} $3 && echo -n " $3" >> $NCU_VLAN_CFG
		fi
	    fi
	    $IFCONFIG ${1}.${idlan} up
	    echo "" >> $NCU_VLAN_CFG
	fi
    else
	echo "Interface $1 not present"
    fi
}

2>>$NCU_LOG

prompt() {
    echo " select action:"
    echo "1 - Dedicate to CHARON"
    echo "2 - Release to host"
    if [[ n$BRTUN = nyes ]] ; then
	echo "3 - Create Bridge with TAPs"
	echo "4 - Remove Bridge"
    else
	echo "3 - Create Bridge with TAPs (disabled, because bridge control package is not found)"
	echo "4 - Remove Bridge           (disabled, because bridge control package is not found)"
    fi
    if [[ n$VLANPRESENT = nyes ]] ; then 
	echo "5 - Add VLAN"
	echo "6 - Remove VLAN"
    else
	echo "5 - Add VLAN                (disabled, because vlan control package is not found)"
	echo "6 - Remove VLAN             (disabled, because vlan control package is not found)"
    fi
    echo "7 - Print status"
    echo "8 - Exit"
} 
 
# ============= main ===================
date >> $NCU_LOG
if [[ "x$1" = "x" ]] ; then
    p_status
    HBR=""
    if [[ n$BRTUN = nyes ]] ; then
	brs=`/usr/sbin/brctl show | grep br000 | wc -l`;
	if [[ $brs > 0 ]] ; then
	    HBR="/ Bridge Cleanup (C)"
	else
	    HBR="/ Bridge (B)"
	fi
    fi
    while prompt; read -p ":>" C_H
    do 
	printf "\n"
	if [[  "$C_H" = "1"  ]] ; then
	    read -p "Specify the interface to dedicate to CHARON:" inf
	    p_dedicate $inf
	elif [[ "$C_H" = "2" ]] ; then
	    read -p "Specify the interface to release to HOST:" inf
	    p_release $inf
	elif [[ "$C_H" = "7" ]] ; then
	    p_status
	elif [ "$BRTUN" = "yes" -a "$C_H" = "3" ] ; then
	    read -p "Specify the interface to be used for BRIDGE:" inf
	    tap_nums=0
	    while [[ $tap_nums -le 0 ]] ;
	    do
		read -p "How many tap should be created:" tap_nums
	    done
	    p_bridge $inf $tap_nums
	elif [ "$BRTUN" = "yes" -a "$C_H" = "4" ] ; then
	    read -p "Specify the phys interface used for  BRIDGE:" inf
	    p_bridge_cleanup  $inf
	elif [ "$VLANPRESENT" = "yes" -a "$C_H" = "5" ] ; then
	    read -p "Specify the phys interface used for  VLAN:" inf
	    if $IFCONFIG -a | grep "^${inf}:" >/dev/null ; then
		while true ; do 
		    read -p "Specify the id of VLAN device (<4095):" vlanid
		    if [ $(( $vlanid < 4095  &&  $vlanid > 0 )) = 1 ] ; then break;  fi
		    echo "ERROR: bad id of vlan. Must be greate than 0 and less than 4095"
		done
		read -p "Specify the ip address of VLAN device or empty string for no ip address:" vlanip
		read -p "Specify the netmask address of VLAN device or empty string for no netmask:" vlanmask
		p_create_vlan "$inf" "$vlanid" "$vlanip" "$vlanmask"
	    else
	        echo "Interface $inf not present"
	    fi
	elif [ "$VLANPRESENT" = "yes" -a "$C_H" = "6" ] ; then
	    read -p "Specify the VLAN interface, which be removed:" inf
	    /sbin/vconfig rem $inf
	    sed -i "/^${inf}\s/ d" $NCU_VLAN_CFG 
	else
	    echo Bye
	    exit 0;
	fi
	printf "\n"
	HBR=""
	if [[ n$BRTUN = nyes ]] ; then
	    brs=`/usr/sbin/brctl show | wc -l`;
	    if [[ $brs > 1 ]] ; then
		HBR="/ Bridge Cleanup (C)"
	    else
		HBR="/ Bridge (B)"
	    fi
	fi
    done
else
    while [[ "x$1" != "x" ]]; do
	arg=$1           # shift the found arg away.
	arg2=$2
	shift
	case $arg in
	    --help|-h)
		cmd="x"
		p_help
		;;
	    --list|-l)
		cmd="x"
		p_status
		;;
	    --dedicate|-d|-D)
		cmd="C"
		;;
            --release|-r|-R)
		cmd="H"
		;;
	    -c)
		if [[ n$BRTUN != nyes ]] ; then 
		    echo ERROR: invalid argument  $arg
		    cmd="x"
		else
		    cmd="F"
		fi
		;;
	    --bridge|-b)
		if [[ n$BRTUN != nyes ]] ; then 
		    echo ERROR: invalid argument  $arg
		    cmd="x"
		else
		    cmd="B"
		fi
		;;
	    --vlan|-v)
		if [[ n$VLANPRESENT != nyes ]] ; then 
		    echo ERROR: invalid argument  $arg
		    cmd="x"
		else
		    cmd="V"
		fi
		;;
	    -w)
		if [[ n$VLANPRESENT != nyes ]] ; then 
		    echo ERROR: invalid argument  $arg
		    cmd="x"
		else
		    cmd="W"
		fi
		;;
	    *)
		case $cmd in  
		    C)
			p_dedicate $arg 
			;;
		    H)
			p_release $arg
			;;
		    B)
			if [[ n$BRTUN = nyes ]] ; then
			    p_bridge $arg $arg2
			    shift
			else
			    echo "ERROR: invalid argument --bridge  $arg"
			fi
			;;
		    F)
			p_bridge_cleanup $arg 
			;;
		    V)
		        p_create_vlan $arg
		        ;;
		    W)
			/sbin/vconfig rem $arg
			sed -i "/^${arg}\$/ d" $NCU_VLAN_CFG 
			;;
		    *)
			echo ERROR: wrong argument  $arg
		esac
		;;
	esac
    done
fi
