#!/bin/sh
#
# Shorewall 3.4 -- /usr/share/shorewall/lib.accounting
#
#     This program is under GPL [http://www.gnu.org/copyleft/gpl.htm]
#
#     (c) 1999,2000,2001,2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net)
#
#	Complete documentation is available at http://shorewall.net
#
#	This program is free software; you can redistribute it and/or modify
#	it under the terms of Version 2 of the GNU General Public License
#	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.
#
#	You should have received a copy of the GNU General Public License
#	along with this program; if not, write to the Free Software
#	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# This library is loaded by /usr/share/shorewall/compiler when the accounting file is
# non-empty.
#

#
# Process a record from the accounting file
#
process_accounting_rule() {
    rule=
    rule2=
    jumpchain=
    user1=

    accounting_error() {
	error_message "WARNING: Invalid Accounting rule" $action $chain $source $dest $proto $port $sport $user
    }

    accounting_interface_error() {
	error_message "WARNING: Unknown interface $1 in " $action $chain $source $dest $proto $port $sport $user
    }

    accounting_interface_verify() {
	verify_interface $1 || accounting_interface_error $1
    }

    jump_to_chain() {
	if ! havechain $jumpchain; then
	    if ! createchain2 $jumpchain No; then
		accounting_error
		return 2
	    fi
	fi

	rule="$rule -j $jumpchain"
    }

    do_ipp2p() {
	[ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support"
	case $proto in
	    *:*)
		proto=${proto#*:}
		;;
	    *)
		proto=tcp
		;;
	esac

	rule="$rule -p $proto -m ipp2p --${port:-ipp2p}"
    }

    case $source in
	*:*)
	    accounting_interface_verify ${source%:*}
	    rule="$(source_ip_range ${source#*:}) $(match_source_dev ${source%:*})"
	    ;;
	*.*.*.*|+*|!+*)
	    rule="$(source_ip_range $source)"
	    ;;
	-|all|any)
	    ;;
	*)
	    if [ -n "$source" ]; then
		accounting_interface_verify $source
		rule="$(match_source_dev $source)"
	    fi
	    ;;
    esac

    [ -n "$dest" ] && case $dest in
	*:*)
	    accounting_interface_verify ${dest%:*}
	    rule="$rule $(dest_ip_range ${dest#*:}) $(match_dest_dev ${dest%:*})"
	    ;;
	*.*.*.*|+*|!*)
	    rule="$rule $(dest_ip_range $dest)"
	    ;;
	-|all|any)
	    ;;
	*)
	    accounting_interface_verify $dest
	    rule="$rule $(match_dest_dev $dest)"
	    ;;
    esac

    [ -n "$proto" ] && case $proto in
	-|any|all)
	    ;;
	    ipp2p|IPP2P|ipp2p:*|IPP2P:*)
		do_ipp2p
		;;
	*)
	    rule="$rule -p $proto"
	    ;;
    esac

    multiport=

    [ -n "$port" ] && case $port in
	-|any|all)
	    ;;
	*)
	    if [ -n "$MULTIPORT" ]; then
		rule="$rule -m multiport --dports $port"
		multiport=Yes
	    else
		rule="$rule --dport $port"
	    fi
	    ;;
    esac

    [ -n "$sport" ] && case $sport in
	-|any|all)
	    ;;
	*)
	    if [ -n "$MULTIPORT" ]; then
		[ -n "$multiport" ] && rule="$rule --sports $sport" || rule="$rule -m multiport --sports $sport"
	    else
		rule="$rule --sport $sport"
	    fi
	    ;;
    esac

    [ -n "$user" ] && case $user in
	-|any|all)
	    ;;
	*)
	    [ "$chain" != OUTPUT ] && \
		fatal_error "Invalid use of a user/group: chain is not OUTPUT but $chain"
	    rule="$rule -m owner"
	    user1="$user"

	    case "$user" in
		!*+*)
		    if [ -n "${user#*+}" ]; then
			rule="$rule ! --cmd-owner ${user#*+} "
		    fi
		    user1=${user%+*}
		    ;;
		*+*)
		    if [ -n "${user#*+}" ]; then
			rule="$rule --cmd-owner ${user#*+} "
		    fi
		    user1=${user%+*}
		    ;;
	    esac

	    case "$user1" in
		!*:*)
		    if [ "$user1" != "!:" ]; then
			temp="${user1#!}"
			temp="${temp%:*}"
			[ -n "$temp" ] && rule="$rule ! --uid-owner $temp "
			temp="${user1#*:}"
			[ -n "$temp" ] && rule="$rule ! --gid-owner $temp "
		    fi
		    ;;
		*:*)
		    if [ "$user1" != ":" ]; then
			temp="${user1%:*}"
			[ -n "$temp" ] && rule="$rule --uid-owner $temp "
			temp="${user1#*:}"
			[ -n "$temp" ] && rule="$rule --gid-owner $temp "
		    fi
		    ;;
		!*)
		    [ "$user1" != "!" ] && rule="$rule ! --uid-owner ${user1#!} "
		    ;;
		*)
		    [ -n "$user1" ] && rule="$rule --uid-owner $user1 "
		    ;;
	    esac
	    ;;
    esac

    case $action in
	COUNT)
	    ;;
	DONE)
	    rule="$rule -j RETURN"
	    ;;
	*:COUNT)
	    rule2="$rule"
	    jumpchain=${action%:*}
	    jump_to_chain || return
	    ;;
	JUMP:*)
	    jumpchain=${action#*:}
	    jump_to_chain || return
	    ;;
	*)
	    jumpchain=$action
	    jump_to_chain || return
	    ;;
    esac

    [ "x${chain:=accounting}" = "x-" ] && chain=accounting

    ensurechain1 $chain

    if do_iptables -A $chain $(fix_bang $rule) ; then
	[ -n "$rule2" ] && run_iptables2 -A $jumpchain $rule2
	progress_message "   Accounting rule \"$action $chain $source $dest $proto $port $sport $user\" $DONE"
	save_progress_message_short "   Accounting rule \\\"$action $chain $source $dest $proto $port $sport $user\\\" Added"
    else
	accounting_error
    fi
}

#
# Set up Accounting
#
setup_accounting() # $1 = Name of accounting file
{

    progress_message2 "$DOING Accounting..."

    save_progress_message "Setting up Accounting..."

    while read action chain source dest proto port sport user ; do
	process_accounting_rule
    done < $TMP_DIR/accounting

    if havechain accounting; then
	for chain in INPUT FORWARD OUTPUT; do
	    run_iptables -I $chain -j accounting
	done
    fi

}