#!/bin/bash
# **** License ****
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# This code was originally developed by Vyatta, Inc.
# Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
# All Rights Reserved.
#
# Author: Tom Grennan
# Date: 2007
#
# **** End License ****

shopt -s extglob
shopt -s nullglob

declare progname=${0##*/}
declare -a full_itfs=( /sys/class/net/+(eth|vmnet|lo|tun|wan|pppoe|pppoa|adsl)* )
declare -a itfs
declare _do_show=_show_itf_stats

_error ()
{
    ecode=$1
    shift
    echo $@
    if [ $ecode -eq 1 ] ; then
        echo
        _usage
    fi
    exit $ecode
}

_show_itf_stats ()
{
    local -i rx_bytes rx_packets rx_errors rx_dropped rx_over_errors multicast
    local -i tx_bytes tx_packets tx_errors tx_dropped tx_carrier_errors \
	collisions
    local -i rx_missed_errors rx_fifo_errors

    for itf ; do
	test -d /sys/class/net/$itf || \
	    _error 2 $itf: no such interface\!
	for stat in \
	    rx_bytes rx_packets rx_errors rx_dropped rx_over_errors multicast \
	    tx_bytes tx_packets tx_errors tx_dropped tx_carrier_errors \
	    collisions
	do
	    full_stat=/sys/class/net/${itf}/statistics/${stat}
	    if  [ -r $full_stat ] ; then
		eval $stat=$(cat $full_stat)
	    else
		eval $stat=0
	    fi
	done
	for stat in rx_missed_errors ; do
	    full_stat=/sys/class/net/${itf}/statistics/${stat}
	    if  [ -r $full_stat ] ; then
		let $(( rx_dropped_errors += $(cat $full_stat) ))
	    fi
	done
	for stat in rx_fifo_errors ; do
	    full_stat=/sys/class/net/${itf}/statistics/${stat}
	    if  [ -r $full_stat ] ; then
		let $(( rx_over_errors += $(cat $full_stat) ))
	    fi
	done

	printf -v rx_stats \
	    '%10d %10d %10d %10d %10d %10d' \
	    $rx_bytes  \
	    $rx_packets \
	    $rx_errors \
	    $rx_dropped \
	    $rx_over_errors \
	    $multicast

	printf -v tx_stats \
	    '%10d %10d %10d %10d %10d %10d' \
	    $tx_bytes  \
	    $tx_packets \
	    $tx_errors \
	    $tx_dropped \
	    $tx_carrier_errors \
	    $collisions

	ip -s addr show ${itf} | sed 's/^[0-9]*: //'
	cat <<-EOF

    RX:  bytes    packets     errors    dropped    overrun      mcast
    $rx_stats
    TX:  bytes    packets     errors    dropped    carrier collisions
    $tx_stats

	EOF
    done
}

_show_itf_physical ()
{
    for eth ; do
        sudo /usr/sbin/ethtool $eth
        echo
    done
}

_usage ()
{
    cat <<-EOF
	$progname [ INTERFACE ]
	$progname loopback [ INTERFACE ]
	$progname tunnel [ INTERFACE ]
	$progname serial [ INTERFACE ]
	$progname pppoe [ INTERFACE ]
	$progname pppoa [ INTERFACE ]
	$progname adsl [ INTERFACE ]
	$progname system [ enabled ]
	EOF
}

_add_itfs ()
{
    for itf ; do
	if [[ $itf != +(eth|vmnet|lo|tun|wan|pppoe|pppoa|adsl)* ]] ; then
	    _error 2 \""$itf"\" is not an interface name\!
	elif [ ! -d /sys/class/net/$itf ] ; then
	    _error 2 $itf: no such interface\!
	else
	    itfs+=( $itf )
	fi
    done
}

if [ $# -gt 0 ] ; then
    if [[ $1 == --+(usage|help) ]] ; then
	_usage
	exit 0
    elif [[ $1 == +(ethernet|loopback|system|tunnel|serial|pppoe|pppoa|adsl) ]] ; then
	if [ $# -eq 1 ] ; then
	    case $1 in
		ethernet )
		    full_itfs=( /sys/class/net/+(eth|vmnet)* ) ;;
		loopback )
		    full_itfs=( /sys/class/net/lo* ) ;;
		tunnel )
		    full_itfs=( /sys/class/net/tun* ) ;;
		serial )
		    full_itfs=( /sys/class/net/wan* ) ;;
		pppoe )
		    full_itfs=( /sys/class/net/pppoe* ) ;;
		pppoa )
		    full_itfs=( /sys/class/net/pppoa* ) ;;
		adsl )
		    full_itfs=( /sys/class/net/adsl* ) ;;
	    esac
	    itfs=( ${full_itfs[@]##*/} )
	else
	    if [[ $1 == system ]] ; then
		if [[ $2 == enabled ]] ; then
		    for full_itf in ${full_itfs[@]} ; do
		        let -i flags="$(cat $full_itf/flags) & 1"
			[[ $flags -eq 1 ]] && _add_itfs ${full_itf##*/}
		    done
		fi
	    else
		_add_itfs $2
		if [ $# -gt 2 ] ; then
		    if [[ $3 == physical ]] ; then
			_do_show=_show_itf_physical
		    elif [[ $3 == vif ]] ; then
			if [ $# -gt 3 ] ; then
			    itfs+=.$4
			    if [[ $# -gt 4 && $4 == physical ]] ; then
				_do_show=_show_itf_physical
			    fi
			else
			    _error 2 missing VIF number\!
			fi
		    fi
		fi
	    fi
	fi
    else
	_add_itfs $*
    fi
else
    itfs=( ${full_itfs[@]##*/} )
fi

eval $_do_show ${itfs[@]}

# Local Variables:
# mode: shell-script
# sh-indentation: 4
# End: