#!/bin/bash
# etcnet firewall handler

# Known issues:
# 1. When interface is 'all' we can't guess correct interfaces processing order
#    like etcnet does so process it by bash sort order
# 2. Guessing may not work correct

# default values
NAME=default
TABLE=filter
CHAIN=
MYIFACEDIR=
PROCESS_IPTABLES=yes
PROCESS_IP6TABLES=yes
PROCESS_EBTABLES=yes
TABLES=
IPTABLES_TABLES="mangle filter nat"
IP6TABLES_TABLES="mangle filter"
EBTABLES_TABLES="broute filter nat"
ACTIONS="start stop restart load unload reload flush show list count counters rule new create remove delete zero policy rename"

usage()
{
    echo '/etc/net FireWall handler'
    echo "Usage: $0 [--ipt[ables]|--ip6t[ables]|--ebt[ables]|--no-ipt[ables]|--no-ip6t[ables]|--no-ebt[ables]] [iface] [table] [chain] <action> [firewall rule or action options]" >&2
    echo ""
    echo " --iptables     - process only with iptables firewall"
    echo " --ip6tables    - process only with ip6tables firewall"
    echo " --ebtables     - process only with ebtables firewall"
    echo " --no-iptables  - process with all types but without iptables"
    echo " --no-ip6tables - process with all types but without ip6tables"
    echo " --no-ebtables  - process with all types but without ebtables"
    echo " By default all enabled types will be processed"
    echo ""
    echo " iface    - 'default' (by default), real interface name or 'all' for all interfaces"
    echo " table    - 'mangle' (iptables and ip6tables only), 'broute' (ebtables only), 'filter' (by default), 'nat' or 'all' for all tables"
    echo " chain    -  system or user defined chain name (case sensitive!) or 'all' for all chains"
    echo " action   - 'start','stop','restart','load','unload','reload','flush','show|list','count|counters','rule','new|create','remove|delete','zero','policy','rename'"
    echo ""
    echo " start    - process all tables and chains for given interface (even when chain or table is defined)"
    echo " stop     - process all tables and chains for given interface (even when chain or table is defined)"
    echo " restart  - equivalent to 'stop' then 'start'"
    echo " load     - load rules for given interface, table and chain"
    echo " unload   - unload rules for given interface, table and chain"
    echo " reload   - equivalent to 'unload' then 'load'"
    echo " flush    - flush rules for given interface, table and chain"
    echo " show     - list rules for given interface, table and chain"
    echo " list     - same as 'show'"
    echo " count    - show counters for given table and chains"
    echo " counters - same as 'count'"
    echo " rule     - parse rule and pass it to iptables and/or ip6tables and/or ebtables"
    echo " new      - create new chain"
    echo " create   - same as 'new'"
    echo " remove   - remove chain"
    echo " delete   - same as 'remove'"
    echo " zero     - zero packet and byte counters in chain"
    echo " policy   - set default policy for chain"
    echo " rename   - rename chain"
    echo ""
    echo "Options for action 'show' or 'list':"
    echo " -n or numeric - numeric output for IP addresses, ports and services"
    echo " -v or verbose - verbose output of rules"
    echo " -x or exact   - expand numbers instead of rounded numbers"
    echo " --line-numbers or lines - display line number for each rule" 
    echo ""
    exit 1
}

guess_options()
{
    # Four options. Get interface name and shift
    [ $# -ge 4 ] && 
	{
	    NAME="$1"
	    [ "$NAME" != "default" ] && [ "$NAME" != "all" ] && 
		{
		    [ -d $IFACEDIR/$NAME@$NETHOST ] && MYIFACEDIR=$IFACEDIR/$NAME@$NETHOST || MYIFACEDIR=$IFACEDIR/$NAME
		    [ -d "$MYIFACEDIR" ] ||
			{
			    print_error "interface configuration directory \"$MYIFACEDIR\" is not found"
			    exit 1
			}
		}
	    # FIXME variable TABLES must be reworked 
	    [ "$2" = "all" ] || egrep -q "([^-]\b|^)$2(\b[^-]|$)" < <(echo "$TABLES") && TABLE="$2" ||
		{
		    print_error "Unknown firewall table \"$2\""
		    exit 1
		}
	    CHAIN="$3"
	    egrep -q "([^-]\b|^)$4(\b[^-]|$)" < <(echo "$ACTIONS") && ACTION="$4" ||
		{
		    print_error "Unknown firewall action \"$4\""
		    exit 1
		}
	    return
	}

    # Three options. Guess first and shift
    [ $# -eq 3 ] && 
	{
	    [ "$1" != "default" ] && [ "$1" != "all" ]  && 
		{
		    # May be it's an interface name
		    [ -d $IFACEDIR/$1@$NETHOST ] && MYIFACEDIR=$IFACEDIR/$1@$NETHOST || MYIFACEDIR=$IFACEDIR/$1
		    [ -d "$MYIFACEDIR" ] &&
			{
			    # Yes, it's an interface name
			    NAME="$1"
			    {
				# FIXME variable TABLES must be reworked 
				[ "$2" = "all" ] || egrep -q "([^-]\b|^)$2(\b[^-]|$)" < <(echo "$TABLES") && TABLE="$2"
			    } || CHAIN="$2"
			} ||
			{
			    # No, it's not an interface name
			    MYIFACEDIR=
			    # FIXME variable TABLES must be reworked 
			    [ "$1" = "all" ] || egrep -q "([^-]\b|^)$1(\b[^-]|$)" < <(echo "$TABLES") && 
				{
				    TABLE="$1"
				    CHAIN="$2"
				} || 
				{
				    print_error "Unknown firewall table \"$1\""
				    exit 1
				}
			}
	    } ||
	    {
		# Yes, it's an interface name or 'all'
		NAME="$1"
		{
		    # FIXME variable TABLES must be reworked 
		    [ "$2" = "all" ] || egrep -q "([^-]\b|^)$2(\b[^-]|$)" < <(echo "$TABLES") && TABLE="$2"
		} || CHAIN="$2"
	    }
	    shift 2
	}

    # Two options. Guest first and shift
    [ $# -eq 2 ] && 
	{
	    [ "$1" != "default" ] && [ "$1" != "all" ] && 
		{
		    # May be it's and interface name
		    [ -d $IFACEDIR/$1@$NETHOST ] && MYIFACEDIR=$IFACEDIR/$1@$NETHOST || MYIFACEDIR=$IFACEDIR/$1
		    [ -d "$MYIFACEDIR" ] &&
			{
			    # Yes, it's an interface name
			    NAME="$1"
			} ||
			{
			    # No
			    MYIFACEDIR=
				{
				# FIXME variable TABLES must be reworked 
				    [ "$1" = "all" ] || egrep -q "([^-]\b|^)$1(\b[^-]|$)" < <(echo "$TABLES") && TABLE="$1"
				} || CHAIN="$1"
			}
		} ||
		{
		    # Yes, it's an interface name or 'all'
		    NAME="$1"
		}
		shift
	
	}

    # One option
    [ $# -eq 1 ] &&
	{
	    egrep -q "([^-]\b|^)$1(\b[^-]|$)" < <(echo "$ACTIONS") && ACTION=$1 ||
		{
		    print_error "Unknown firewall action \"$1\""
		    exit 1
		}
	}
}

# iptables suff
iptables_local_rules_from_file()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    [ "$TABLE" = "all" ] && TABLE="*"
    [ "$CHAIN" = "all" ] && CHAIN="*"
    for CTABLE in $TABLE; do
	[ -d "$CTABLE" ] || continue
	for CCHAIN in "$CTABLE"/$CHAIN; do
	    CCHAIN=$(basename $CCHAIN)
	    [ "$CTABLE/$CCHAIN" != "$CTABLE/loadorder" -a  -f "$CTABLE/$CCHAIN" -a \
	    "$CTABLE/${CCHAIN%.rpm*}" = "$CTABLE/$CCHAIN" -a "$CTABLE/${CCHAIN%\~}" = "$CTABLE/$CCHAIN" ] || continue
	    [ "$1" = "unload" ] && iptables_flush_rules_from_file "$CTABLE" "$CCHAIN" "$2" ||
		{
		    [ -s "$CTABLE/$CCHAIN" ] && iptables_load_rules_from_file "$CTABLE" "$CCHAIN"
		}
	done
    done
}

iptables_local_rule()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    local RULE
    [ "$TABLE" = "all" ] && TABLE="*"
    [ "$CHAIN" = "all" ] && CHAIN="*"
    RULE=$(iptables_expand_string $@)
    [ -z "$RULE" ] && return
    for CTABLE in $TABLE; do
	[ -d "$CTABLE" ] || continue
	for CCHAIN in "$CTABLE"/$CHAIN; do
	    CCHAIN=$(basename $CCHAIN)
	    [ "$CTABLE/$CCHAIN" != "$CTABLE/loadorder" -a  -f "$CTABLE/$CCHAIN" -a \
	    "$CTABLE/${CCHAIN%.rpm*}" = "$CTABLE/$CCHAIN" -a "$CTABLE/${CCHAIN%\~}" = "$CTABLE/$CCHAIN" ] || continue
	    read FIRST SECOND REST < <(echo $RULE)
	    case $FIRST in
		"-A"|"-D"|"-I")
			    iptables_push_rule $CTABLE $FIRST $CCHAIN $SECOND $REST
			    ;;
		"-P")
			    iptables_chain_policy $CTABLE $(echo $CCHAIN $SECOND $REST|sed -e 's/-j//g')
			    ;;
		*)
			    iptables_push_rule $CTABLE $CCHAIN $FIRST $SECOND $REST
			    ;;
	    esac
	done
    done
}

iptables_local_create_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    [ "$TABLE" = "all" ] && TABLE=$IPTABLES_TABLES
    for CTABLE in $TABLE; do
	iptables_create_chain $CTABLE $CHAIN
    done
}

iptables_local_delete_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=$2
    shift $#
    [ "$TABLE" = "all" ] && TABLE=$IPTABLES_TABLES
    for CTABLE in $TABLE; do
	# We can delete only user-defined chains so just don't pass chain name
	# in case of 'all' chain
	[ "$CHAIN" = "all" ] && iptables_delete_chain $CTABLE || iptables_delete_chain $CTABLE $CHAIN
    done
}

iptables_local_list_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=$2
    [ -z "$CHAIN" ] && shift || shift 2
    local LIST_ARGS
    [ $# -eq 0 ] || 
	{
	    iptables_preload
	    LIST_ARGS=$(iptables_expand_string $@)
	}
    [ "$TABLE" = "all" ] && TABLE=$IPTABLES_TABLES
    for CTABLE in $TABLE; do
	printf "\nTable $CTABLE\n\n"
	[ "$CHAIN" = "all" ] && iptables_list_chain $CTABLE $LIST_ARGS || iptables_list_chain $CTABLE $CHAIN $LIST_ARGS
    done
}

iptables_local_zero_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=$2
    shift $#
    [ "$TABLE" = "all" ] && TABLE=$IPTABLES_TABLES
    for CTABLE in $TABLE; do
	[ "$CHAIN" = "all" ] && iptables_zero_chain $CTABLE || iptables_zero_chain $CTABLE $CHAIN
    done
}

iptables_local_rename_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    [ "$TABLE" = "all" ] && TABLE=$IPTABLES_TABLES
    for CTABLE in $TABLE; do
	iptables_rename_chain $CTABLE $CHAIN $@
    done
}

# ip6tables suff
ip6tables_local_rules_from_file()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    [ "$TABLE" = "all" ] && TABLE="*"
    [ "$CHAIN" = "all" ] && CHAIN="*"
    for CTABLE in $TABLE; do
	[ -d "$CTABLE" ] || continue
	for CCHAIN in "$CTABLE"/$CHAIN; do
	    CCHAIN=$(basename $CCHAIN)
	    [ "$CTABLE/$CCHAIN" != "$CTABLE/loadorder" -a  -f "$CTABLE/$CCHAIN" -a \
	    "$CTABLE/${CCHAIN%.rpm*}" = "$CTABLE/$CCHAIN" -a "$CTABLE/${CCHAIN%\~}" = "$CTABLE/$CCHAIN" ] || continue
	    [ "$1" = "unload" ] && ip6tables_flush_rules_from_file "$CTABLE" "$CCHAIN" "$2" ||
		{
		    [ -s "$CTABLE/$CCHAIN" ] && ip6tables_load_rules_from_file "$CTABLE" "$CCHAIN"
		}
	done
    done
}

ip6tables_local_rule()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    local RULE
    [ "$TABLE" = "all" ] && TABLE="*"
    [ "$CHAIN" = "all" ] && CHAIN="*"
    RULE=$(ip6tables_expand_string $@)
    [ -z "$RULE" ] && return
    for CTABLE in $TABLE; do
	[ -d "$CTABLE" ] || continue
	for CCHAIN in "$CTABLE"/$CHAIN; do
	    CCHAIN=$(basename $CCHAIN)
	    [ "$CTABLE/$CCHAIN" != "$CTABLE/loadorder" -a  -f "$CTABLE/$CCHAIN" -a \
	    "$CTABLE/${CCHAIN%.rpm*}" = "$CTABLE/$CCHAIN" -a "$CTABLE/${CCHAIN%\~}" = "$CTABLE/$CCHAIN" ] || continue
	    read FIRST SECOND REST < <(echo $RULE)
	    case $FIRST in
		"-A"|"-D"|"-I")
			    ip6tables_push_rule $CTABLE $FIRST $CCHAIN $SECOND $REST
			    ;;
		"-P")
			    ip6tables_chain_policy $CTABLE $(echo $CCHAIN $SECOND $REST|sed -e 's/-j//g')
			    ;;
		*)
			    ip6tables_push_rule $CTABLE $CCHAIN $FIRST $SECOND $REST
			    ;;
	    esac
	done
    done
}

ip6tables_local_create_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    [ "$TABLE" = "all" ] && TABLE=$IP6TABLES_TABLES
    for CTABLE in $TABLE; do
	ip6tables_create_chain $CTABLE $CHAIN
    done
}

ip6tables_local_delete_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=$2
    shift $#
    [ "$TABLE" = "all" ] && TABLE=$IP6TABLES_TABLES
    for CTABLE in $TABLE; do
	# We can delete only user-defined chains so just don't pass chain name
	# in case of 'all' chain
	[ "$CHAIN" = "all" ] && ip6tables_delete_chain $CTABLE || ip6tables_delete_chain $CTABLE $CHAIN
    done
}

ip6tables_local_list_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=$2
    [ -z "$CHAIN" ] && shift || shift 2
    local LIST_ARGS
    [ $# -eq 0 ] || 
	{
	    ip6tables_preload
	    LIST_ARGS=$(ip6tables_expand_string $@)
	}
    [ "$TABLE" = "all" ] && TABLE=$IP6TABLES_TABLES
    for CTABLE in $TABLE; do
	printf "\nTable $CTABLE\n\n"
	[ "$CHAIN" = "all" ] && ip6tables_list_chain $CTABLE $LIST_ARGS || ip6tables_list_chain $CTABLE $CHAIN $LIST_ARGS
    done
}

ip6tables_local_zero_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=$2
    shift $#
    [ "$TABLE" = "all" ] && TABLE=$IP6TABLES_TABLES
    for CTABLE in $TABLE; do
	[ "$CHAIN" = "all" ] && ip6tables_zero_chain $CTABLE || ip6tables_zero_chain $CTABLE $CHAIN
    done
}

ip6tables_local_rename_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    [ "$TABLE" = "all" ] && TABLE=$IP6TABLES_TABLES
    for CTABLE in $TABLE; do
	ip6tables_rename_chain $CTABLE $CHAIN $@
    done
}

# ebtables suff
ebtables_local_rules_from_file()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2
    [ "$TABLE" = "all" ] && TABLE="*"
    [ "$CHAIN" = "all" ] && CHAIN="*"
    for CTABLE in $TABLE; do
	[ -d "$CTABLE" ] || continue
	for CCHAIN in "$CTABLE"/$CHAIN; do
	    CCHAIN=$(basename $CCHAIN)
	    [ "$CTABLE/$CCHAIN" != "$CTABLE/loadorder" -a  -f "$CTABLE/$CCHAIN" -a \
	    "$CTABLE/${CCHAIN%.rpm*}" = "$CTABLE/$CCHAIN" -a "$CTABLE/${CCHAIN%\~}" = "$CTABLE/$CCHAIN" ] || continue
	    [ "$1" = "unload" ] && ebtables_flush_rules_from_file "$CTABLE" "$CCHAIN" "$2" ||
		{
		    [ -s "$CTABLE/$CCHAIN" ] && ebtables_load_rules_from_file "$CTABLE" "$CCHAIN"
		}
	done
    done
}

ebtables_local_rule()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    local RULE
    [ "$TABLE" = "all" ] && TABLE="*"
    [ "$CHAIN" = "all" ] && CHAIN="*"
    RULE=$(ebtables_expand_string $@)
    [ -z "$RULE" ] && return
    for CTABLE in $TABLE; do
	[ -d "$CTABLE" ] || continue
        for CCHAIN in "$CTABLE"/$CHAIN; do
	CCHAIN=$(basename $CCHAIN)
	    [ "$CTABLE/$CCHAIN" != "$CTABLE/loadorder" -a  -f "$CTABLE/$CCHAIN" -a \
	    "$CTABLE/${CCHAIN%.rpm*}" = "$CTABLE/$CCHAIN" -a "$CTABLE/${CCHAIN%\~}" = "$CTABLE/$CCHAIN" ] || continue
	    read FIRST SECOND REST < <(echo $RULE)
	    case $FIRST in
		"-A"|"-D"|"-I")
			    ebtables_push_rule $CTABLE $FIRST $CCHAIN $SECOND $REST
			    ;;
		"-P")
			    ebtables_chain_policy $CTABLE $(echo $CCHAIN $SECOND $REST|sed -e 's/-j//g')
			    ;;
		*)
			    ebtables_push_rule $CTABLE $CCHAIN $FIRST $SECOND $REST
			    ;;
	    esac
	done
    done
}

ebtables_local_create_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    [ "$TABLE" = "all" ] && TABLE=$EBTABLES_TABLES
    for CTABLE in $TABLE; do
	ebtables_create_chain $CTABLE $CHAIN
    done
}

ebtables_local_delete_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=$2
    shift $#
    [ "$TABLE" = "all" ] && TABLE=$EBTABLES_TABLES
    for CTABLE in $TABLE; do
	# We can delete only user-defined chains so just don't pass chain name
	# in case of 'all' chain
	[ "$CHAIN" = "all" ] && ebtables_delete_chain $CTABLE || ebtables_delete_chain $CTABLE $CHAIN
    done
}

ebtables_local_list_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=$2
    [ -z "$CHAIN" ] && shift || shift 2
    local LIST_ARGS
    [ $# -eq 0 ] || 
	{
	    LIST_ARGS=$(ebtables_expand_string $@)
	}
    [ "$TABLE" = "all" ] && TABLE=$EBTABLES_TABLES
    for CTABLE in $TABLE; do
	printf "\nTable $CTABLE\n\n"
	[ "$CHAIN" = "all" ] && ebtables_list_chain $CTABLE $LIST_ARGS || ebtables_list_chain $CTABLE $CHAIN $LIST_ARGS
    done
}

ebtables_local_zero_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=$2
    shift $#
    [ "$TABLE" = "all" ] && TABLE=$EBTABLES_TABLES
    for CTABLE in $TABLE; do
	[ "$CHAIN" = "all" ] && ebtables_zero_chain $CTABLE || ebtables_zero_chain $CTABLE $CHAIN
    done
}

ebtables_local_rename_chain()
{
    local TABLE=${1:?missing 1st arg to $FUNCNAME}
    local CHAIN=${2:?missing 2nd arg to $FUNCNAME}
    shift 2 
    [ "$TABLE" = "all" ] && TABLE=$EBTABLES_TABLES
    for CTABLE in $TABLE; do
	ebtables_rename_chain $CTABLE $CHAIN $@
    done
}

# Be verbose in any way
export VERBOSE=on
export PROGRESS=on

[ -s ${SCRIPTDIR:=/etc/net/scripts}/functions ] ||
{
    echo "ERROR: $SCRIPTDIR/functions not found!"
    exit 1
}

. ${SCRIPTDIR:=/etc/net/scripts}/functions
pickup_defaults

[ -s ${SCRIPTDIR:=/etc/net/scripts}/functions-fw ] ||
{
    print_error "$SCRIPTDIR/functions-fw not found!"
    exit 1
}

. ${SCRIPTDIR:=/etc/net/scripts}/functions-fw

[ -z "$NETPROFILE" ] && init_netprofile

! profiled_filename opts /etc/net/ifaces/default/options || . "$opts"

is_yes "$CONFIG_FW" ||
{
    print_message "Firewall is disabled"
    exit 1
}

! profiled_filename fwopts /etc/net/ifaces/default/fw/options || . "$fwopts"

# FIXME variable TABLES must be reworked 
case "$1" in
    --ipt*)
			PROCESS_IPTABLES=yes
			PROCESS_IP6TABLES=no
			PROCESS_EBTABLES=no
			TABLES=$IPTABLES_TABLES
			shift
			;;
    --ip6t*)
			PROCESS_IPTABLES=no
			PROCESS_IP6TABLES=yes
			PROCESS_EBTABLES=no
			TABLES=$IP6TABLES_TABLES
			shift
			;;
    --ebt*)
			PROCESS_IPTABLES=no
			PROCESS_IP6TABLES=no
			PROCESS_EBTABLES=yes
			TABLES=$EBTABLES_TABLES
			shift
			;;
    --no-ipt*)
			PROCESS_IPTABLES=no
			TABLES="$IP6TABLES_TABLES $EBTABLES_TABLES"
			shift
			;;
    --no-ip6t*)
			PROCESS_IP6TABLES=no
			TABLES="$IPTABLES_TABLES $EBTABLES_TABLES"
			shift
			;;
    --no-ebt*)
			PROCESS_EBTABLES=no
			TABLES="$IPTABLES_TABLES $IP6TABLES_TABLES"
			shift
			;;
    *)
			# FIXME we must look at the FW_TYPE
			TABLES="$IPTABLES_TABLES $IP6TABLES_TABLES $EBTABLES_TABLES"
			;;
esac

[ $# -lt 1 ] && usage || guess_options $@


print_message "Interface is \"$NAME\""
print_message "Table is \"$TABLE\""
print_message "Chain is \"$CHAIN\""
print_message "Action is \"$ACTION\""
print_message

[ "$NAME" = "all" ] && NAME="*"

for IFACE in "$IFACEDIR"/$NAME; do
    [ -d "$IFACE" ] || continue
    NAME=$(basename $IFACE)
    [ "$NAME" = "default" -o "$NAME" = "unknown" ] || pickup_options
    [ -z "$MYIFACEDIR" ] &&
	{
	    [ -d $IFACEDIR/$NAME@$NETHOST ] && MYIFACEDIR=$IFACEDIR/$NAME@$NETHOST || MYIFACEDIR=$IFACEDIR/$NAME
	}
    ! profiled_filename fwopts "$MYIFACEDIR/fw/options" || . "$fwopts"
    is_yes "$CONFIG_IPV4" && SourceIfNotEmpty $SCRIPTDIR/functions-ipv4 && export IPV4ADDRESS=( $(get_ipv4_addresses $NAME) )
    is_yes "$CONFIG_IPV6" && SourceIfNotEmpty $SCRIPTDIR/functions-ipv6 && export IPV6ADDRESS=( $(get_ipv6_addresses $NAME) )
    for CFW_TYPE in $FW_TYPE; do
	case "$CFW_TYPE" in
	    "iptables")
			is_yes "$PROCESS_IPTABLES" || continue
			# FIXME Does iptables support only IPv4?
			is_yes "$CONFIG_IPV4" || continue
			profiled_filename_dir cfwdir "$MYIFACEDIR/fw/$CFW_TYPE" ||
			    {
				 print_message "No \"$CFW_TYPE\" firewall is configured for interface \"$NAME\""
				 print_message
				 continue
			    }
			[ -x "$IPTABLES" ] || 
			    {
				print_error "$IPTABLES not found. Please, install iptables package"
				print_message
				continue
			    }
			print_message "Firewall type is \"$CFW_TYPE\""
			[ "$NAME" != "default" ] && pickup_options
			# Load own interface syntax if exists
			[ "$NAME" != "default" ] && is_yes "$IPTABLES_HUMAN_SYNTAX" &&
			    {
				[ -s "$cfwdir/syntax" ] &&
				    {
					export IPTABLES_SYNTAX_DIR="$cfwdir"
					unset IPTABLES_SYNTAX IPTABLES_SED_RULES
				    }
			    }

			case "$ACTION" in
			    start)
					iptables_preload
					iptables_start "$NAME"
					;;
			    stop)
					iptables_preload
					iptables_stop "$NAME"
					;;
			    restart)
					iptables_preload
					iptables_stop "$NAME"
					iptables_start "$NAME"
					;;
			    load)
					iptables_preload
					cd "$cfwdir"
					iptables_local_rules_from_file $TABLE $CHAIN
					;;
			    unload)
					iptables_preload
					cd "$cfwdir"
					iptables_local_rules_from_file $TABLE $CHAIN unload
					;;
			    reload)
					iptables_preload
					cd "$cfwdir"
					iptables_local_rules_from_file $TABLE $CHAIN unload
					iptables_local_rules_from_file $TABLE $CHAIN
					;;
			    flush)
					iptables_preload
					cd "$cfwdir"
					iptables_local_rules_from_file $TABLE $CHAIN unload flush
					;;
			    show|list)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
						;;
					    3)
					    	shift 3
					    	;;
					    *)
						shift 4
						;;
					esac
					iptables_local_list_chain $TABLE $CHAIN $@
					;;
			    count|counters)
					print_error "Not implemented yet"
					exit 1
					;;
			    rule)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
					    	;;
					    3)
						shift 3
						;;
					    *)
						shift 4
						;;
					esac
					iptables_preload
					cd "$cfwdir"
					iptables_local_rule $TABLE $CHAIN $@
					;;
			    new|create)
					iptables_local_create_chain $TABLE $CHAIN
					;;
			    remove|delete)
					iptables_local_delete_chain $TABLE $CHAIN
					;;
			    zero)
					iptables_local_zero_chain $TABLE $CHAIN
					;;
			    policy)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
						;;
					    3)
						shift 3
						;;
					    *)
						shift 4
						;;
					esac
					iptables_preload
					cd "$cfwdir"
					iptables_local_rule $TABLE $CHAIN -P $@
					;;
			    rename)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
						;;
					    3)
						shift 3
						;;
					    *)
						shift 4
						;;
					esac
					iptables_local_rename_chain $TABLE $CHAIN $@
					;;
			    *)
					usage
					;;
			esac
			print_message
			;;
	    "ip6tables")
			is_yes "$PROCESS_IP6TABLES" || continue
			# FIXME Does ip6tables support only IPv6?
			is_yes "$CONFIG_IPV6" || continue
			profiled_filename_dir cfwdir "$MYIFACEDIR/fw/$CFW_TYPE" ||
			    {
				 print_message "No \"$CFW_TYPE\" firewall is configured for interface \"$NAME\""
				 print_message
				 continue
			    }
			[ -x "$IP6TABLES" ] || 
			    {
				print_error "$IP6TABLES not found. Please, install iptables-ipv6 package"
				print_message
				continue
			    }
			print_message "Firewall type is \"$CFW_TYPE\""
			[ "$NAME" != "default" ] && pickup_options
			# Load own interface syntax if exists
			[ "$NAME" != "default" ] && is_yes "$IP6TABLES_HUMAN_SYNTAX" &&
			    {
				[ -s "$cfwdir/syntax" ] &&
				    {
					export IP6TABLES_SYNTAX_DIR="$cfwdir"
					unset IP6TABLES_SYNTAX IP6TABLES_SED_RULES
				    }
			    }

			case "$ACTION" in
			    start)
					ip6tables_preload
					ip6tables_start "$NAME"
					;;
			    stop)
					ip6tables_preload
					ip6tables_stop "$NAME"
					;;
			    restart)
					ip6tables_preload
					ip6tables_stop "$NAME"
					ip6tables_start "$NAME"
					;;
			    load)
					ip6tables_preload
					cd "$cfwdir"
					ip6tables_local_rules_from_file $TABLE $CHAIN
					;;
			    unload)
					ip6tables_preload
					cd "$cfwdir"
					ip6tables_local_rules_from_file $TABLE $CHAIN unload
					;;
			    reload)
					ip6tables_preload
					cd "$cfwdir"
					ip6tables_local_rules_from_file $TABLE $CHAIN unload
					ip6tables_local_rules_from_file $TABLE $CHAIN
					;;
			    flush)
					ip6tables_preload
					cd "$cfwdir"
					ip6tables_local_rules_from_file $TABLE $CHAIN unload flush
					;;
			    show|list)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
						;;
					    3)
					    	shift 3
					    	;;
					    *)
						shift 4
						;;
					esac
					ip6tables_local_list_chain $TABLE $CHAIN $@
					;;
			    count|counters)
					print_error "Not implemented yet"
					exit 1
					;;
			    rule)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
					    	;;
					    3)
						shift 3
						;;
					    *)
						shift 4
						;;
					esac
					ip6tables_preload
					cd "$cfwdir"
					ip6tables_local_rule $TABLE $CHAIN $@
					;;
			    new|create)
					ip6tables_local_create_chain $TABLE $CHAIN
					;;
			    remove|delete)
					ip6tables_local_delete_chain $TABLE $CHAIN
					;;
			    zero)
					ip6tables_local_zero_chain $TABLE $CHAIN
					;;
			    policy)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
						;;
					    3)
						shift 3
						;;
					    *)
						shift 4
						;;
					esac
					ip6tables_preload
					cd "$cfwdir"
					ip6tables_local_rule $TABLE $CHAIN -P $@
					;;
			    rename)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
						;;
					    3)
						shift 3
						;;
					    *)
						shift 4
						;;
					esac
					ip6tables_local_rename_chain $TABLE $CHAIN $@
					;;
			    *)
					usage
					;;
			esac
			print_message
			;;
	    "ebtables")
			is_yes "$PROCESS_EBTABLES" || continue
			profiled_filename_dir cfwdir "$MYIFACEDIR/fw/$CFW_TYPE" ||
			    {
				 print_message "No \"$CFW_TYPE\" firewall is configured for interface \"$NAME\""
				 print_message
				 continue
			    }
			[ -x "$EBTABLES" ] || 
			    {
				print_error "$EBTABLES not found. Please, install ebtables package"
				print_message
				continue
			    }
			print_message "Firewall type is \"$CFW_TYPE\""
			[ "$NAME" != "default" ] && pickup_options
			case "$ACTION" in
			    start)
					ebtables_start "$NAME"
					;;
			    stop)
					ebtables_stop "$NAME"
					;;
			    restart)
					ebtables_stop "$NAME"
					ebtables_start "$NAME"
					;;
			    load)
					cd "$cfwdir"
					ebtables_local_rules_from_file $TABLE $CHAIN
					;;
			    unload)
					cd "$cfwdir"
					ebtables_local_rules_from_file $TABLE $CHAIN unload
					;;
			    reload)
					cd "$cfwdir"
					ebtables_local_rules_from_file $TABLE $CHAIN unload
					ebtables_local_rules_from_file $TABLE $CHAIN
					;;
			    flush)
					cd "$cfwdir"
					ebtables_local_rules_from_file $TABLE $CHAIN unload flush
					;;
			    show|list)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
						;;
					    3)
					    	shift 3
					    	;;
					    *)
						shift 4
						;;
					esac
					ebtables_local_list_chain $TABLE $CHAIN $@
					;;
			    count|counters)
					print_error "Not implemented yet"
					exit 1
					;;
			    rule)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
					    	;;
					    3)
						shift 3
						;;
					    *)
						shift 4
						;;
					esac
					cd "$cfwdir"
					ebtables_local_rule $TABLE $CHAIN $@
					;;
			    new|create)
					ebtables_local_create_chain $TABLE $CHAIN
					;;
			    remove|delete)
					ebtables_local_delete_chain $TABLE $CHAIN
					;;
			    zero)
					ebtables_local_zero_chain $TABLE $CHAIN
					;;
			    policy)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
						;;
					    3)
						shift 3
						;;
					    *)
						shift 4
						;;
					esac
					cd "$cfwdir"
					ebtables_local_rule $TABLE $CHAIN -P $@
					;;
			    rename)
					# FIXME BROKEN Double shift after each
					# iteration
					case $# in
					    1)
						shift 1
						;;
					    2)
						shift 2
						;;
					    3)
						shift 3
						;;
					    *)
						shift 4
						;;
					esac
					ebtables_local_rename_chain $TABLE $CHAIN $@
					;;
			    *)
					usage
					;;
			esac
			print_message
			;;
		*)
			print_error "Firewall type \"$CFW_TYPE\" isn't supported"
			;;
	esac
    done
    MYIFACEDIR=""
done
