Code cleanup and integration of the cleanup expired function.
[vsys-scripts.git] / exec / ipfw-be
index 8694e44..239a923 100755 (executable)
 # Configurable variables are at the beginning
 
 # If HOOK is set the program is called before configuring a rule.
-# A sample hook can be found in the ipfw.rpm package
+# A sample hook can be found in the ipfwroot.rpm package,
+# it can be used to collect statistical information on dummynet usage.
+# To configure an hook, set the HOOK variable as follow:
 # HOOK=/tmp/sample_hook
-# XXX HOOK=""
 
 # You should not touch anything below.
 
@@ -68,27 +69,34 @@ TEST=0              # set to 1 for test mode
 DBFILE=/tmp/ff
 lockfile=/var/lock/ipfw.lock
 
-# There values are the keys used in the database for rules and pipes
-# rule_nr 1..10000 are mapped to rules 10000..49999 (n*4+9996)
-# rule_nr 10001..20000 are mapped to rules 50000..59999 (n+39999)
-# pipe_nr 1..25000 are mapped to pipes 10000-59999 (n*2+9998)
-RULE_BL_MIN=1
-RULE_BL_MAX=10000
-RULE_IN_MIN=10001
-RULE_IN_MAX=20000
+# These values are the keys used in the database for blocks
+# and pipes. For rules we store directly the ipfw number.
+#
+# For each user we allocate a block of $M rules. The rules
+# allocated to users are within the range:
+# ipfw_min_rule = RULE_BASE
+# ipfw_max_rule = RULE_BASE + ((M-1)*block_n) -1
+BLOCK_MIN=1
+BLOCK_MAX=1000
+M=50           # block size
 PIPE_MIN=1
 PIPE_MAX=25000
-# These are the rule numbers used in ipfw
+
+# These are the actual rule numbers used in ipfw
 IPFW_RULE_MIN=10000
-IPFW_RULE_MAX=59999
 IPFW_PIPE_MIN=10000
-IPFW_PIPE_MAX=59999
+
+# The skipto and the generic default rule
+# these values are used to initialize the firewall
+SLICE_TABLE=1          # table number used for slice ids lookup
+S=1000                 # firewall rule number for the skipto rule
+D=2000                 # default rule for reserved section
 
 # set slicename and slice_id
-# there represents the credential of the user
+# these are the credential of the user invoking the backend
 SLICENAME=$1
 SLICE_ID=`id -u $SLICENAME`
-[ $? != 0 ] && abort "Invalid slicename $SLICENAME"
+[ x"$SLICE_ID" = x"" ] && echo "No sliver present." && exit
 
 # programs
 # XXX check consistency for variables {}
@@ -101,10 +109,12 @@ IPFW_CHECK="/sbin/ipfw -n"
 debug() { # $1 message to be displayed
        [ x"${VERBOSE}" != x"0" ] && echo "ipfw-be: $1"
 }
+
 # if the first argument is -v, enable verbose mode
 set_verbose() {
     [ x"$1" = x"-v" -o x"$2" = x"-v" ] && VERBOSE=1
 }
+
 set_test() {
     [ x"$1" = x"-q" -o x"$2" = x"-q" ] || return
     TEST=1
@@ -112,10 +122,9 @@ set_test() {
     IPFW_CHECK="/bin/echo ipfw -n:"
 }
 
-
-abort() { # $1 message to be displayed
+abort() { # $1 message to be displayed in case of error
        release_lock
-       echo "ipfw-be aborting: $1"
+       echo "ipfw-be aborting (netconfig help): $1"
        exit 1
 }
 
@@ -137,29 +146,38 @@ clean_db() {
        # is not supported so for the time being we kill them all
        ${IPFW} -q flush
        ${IPFW} -q pipe flush
-       # ${IPFW} delete ${IPFW_RULE_MIN}-${IPFW_RULE_MAX}
-       # ${IPFW} pipe delete ${IPFW_PIPE_MIN}-${IPFW_PIPE_MAX}
+       ${IPFW} -q table $SLICE_TABLE flush
+       #${IPFW} delete ${IPFW_RULE_MIN}-${IPFW_RULE_MAX}
+       #${IPFW} pipe delete ${IPFW_PIPE_MIN}-${IPFW_PIPE_MAX}
+       # since all rules are now deleted, we should initialize the firewall 
+       ipfw_init
 }
 
+#
 # Add the ipfw rule/pipe and update the database.
 # The pipe-in and pipe_out config are through global variables
 # CONFIG_IN CONFIG_OUT because they may be long.
 # Other arguments are on the command line
-add_rule() { # new_rule slice_id type arg rule pipe_base timeout
-    local new_rule=$1 slice_id=$2 type=$3 arg=$4
-    local rule_base=$5 pipe_base=$6 timeout=$7
-    local pipe_in pipe_out rule_in rule_out check_timeout
+#
+# the new_rule variable is set if the rule to be installed is new
+# we need to know this because we do not want to clean
+# rule counters on pipes reconfiguration
+add_rule() { # slice_id new_rule type arg ipfw_rule pipe_index timeout
+    local slice_id=$1 new_rule=$2 type=$3 arg=$4
+    local ipfw_rule=$5 pipe_index=$6 timeout=$7
+    local ipfw_pipe_in ipfw_pipe_out check_timeout
 
     # If we use a profile file, locate the user directory
     # move in the slice root dir XXX todo
-    [ "$TEST" != "1" ] && cd /vservers/${SLICENAME}/root
+    [ "$TEST" != "1" ] && cd /vservers/${SLICENAME}/root
     #echo ${CONFIG_STRING} | ${SED} -e "s/ profile \(.[^ ]\)/ profile \/vservers\/${SLICENAME}\/\1/g"
 
+    # find actual pipe numbers
+    ipfw_pipe_in=$(($IPFW_PIPE_MIN + $((2 * $(($pipe_index - 1)))) ))
+    ipfw_pipe_out=$(($ipfw_pipe_in + 1))
+    local del                  # used delete incompatible configurations
+
     # first, call ipfw -n to check syntax, if ok move on and do the action
-    pipe_in=$(($pipe_base + $pipe_base + 9998))
-    pipe_out=$(($pipe_in + 1))
-    local del  # anything to delete ?
-    local rule_nr=$(($rule_base + 39999))  # XXX formula for individual rules
     if [ x"$new_rule" != x"0" ] ; then
        case $type in
        server)
@@ -182,78 +200,172 @@ add_rule() { # new_rule slice_id type arg rule pipe_base timeout
            ;;
        esac
 
-       rule_in="pipe ${pipe_in} in jail $slice_id ${rule_in} // $type $arg"
-       rule_out="pipe ${pipe_out} out jail $slice_id ${rule_out} // $type $arg"
-       ${IPFW_CHECK} add ${rule_nr} $rule_in > /dev/null || \
+       rule_in="pipe ${ipfw_pipe_in} in jail $slice_id ${rule_in} // $type $arg"
+       rule_out="pipe ${ipfw_pipe_out} out jail $slice_id ${rule_out} // $type $arg"
+
+       ${IPFW_CHECK} add ${ipfw_rule} $rule_in > /dev/null || \
                abort "ipfw syntax error $rule_in"
-       ${IPFW_CHECK} add ${rule_nr} $rule_out > /dev/null || \
+       ${IPFW_CHECK} add ${ipfw_rule} $rule_out > /dev/null || \
                abort "ipfw syntax error $rule_out"
     fi
 
     # check error reporting
-    ${IPFW_CHECK} pipe ${pipe_in} config ${CONFIG_PIPE_IN} > /dev/null || \
+    ${IPFW_CHECK} pipe ${ipfw_pipe_in} config ${CONFIG_PIPE_IN} > /dev/null || \
                abort "ipfw syntax error pipe_in"
-    ${IPFW_CHECK} pipe ${pipe_out} config ${CONFIG_PIPE_OUT} > /dev/null || \
+    ${IPFW_CHECK} pipe ${ipfw_pipe_out} config ${CONFIG_PIPE_OUT} > /dev/null || \
                abort "ipfw syntax error pipe_out"
 
     # all good, delete and add rules if necessary
-    [ "$del" = "service" ] && do_delete $slice_id service $arg
-    [ "$del" = "cli_ser" ] && do_delete $slice_id client $arg
-    [ "$del" = "cli_ser" ] && do_delete $slice_id server $arg
-    [ "$new_rule" != "0" ] && ${IPFW} add ${rule_nr} $rule_in > /dev/null
-    [ "$new_rule" != "0" ] && ${IPFW} add ${rule_nr} $rule_out > /dev/null
+    [ "$del" = "service" ] && do_delete $slice_id service $arg
+    [ "$del" = "cli_ser" ] && do_delete $slice_id client $arg
+    [ "$del" = "cli_ser" ] && do_delete $slice_id server $arg
+    [ "$new_rule" != "0" ] && ${IPFW} add ${ipfw_rule} $rule_in > /dev/null
+    [ "$new_rule" != "0" ] && ${IPFW} add ${ipfw_rule} $rule_out > /dev/null
     # config pipes
-    ${IPFW} pipe ${pipe_in} config ${CONFIG_PIPE_IN}
-    ${IPFW} pipe ${pipe_out} config ${CONFIG_PIPE_OUT}
+    ${IPFW} pipe ${ipfw_pipe_in} config ${CONFIG_PIPE_IN}
+    ${IPFW} pipe ${ipfw_pipe_out} config ${CONFIG_PIPE_OUT}
 
     # send output to the user
-    ${IPFW} show ${rule_nr}
-    ${IPFW} pipe ${pipe_in} show
-    ${IPFW} pipe ${pipe_out} show
+    ${IPFW} show ${ipfw_rule}
+    ${IPFW} pipe ${ipfw_pipe_in} show
+    ${IPFW} pipe ${ipfw_pipe_out} show
 
+#add test 
     [ "$TEST" = "1" ] && return
-    # add to the database, at least to adjust the timeout
+    # add to the database
     ( grep -v -- "^${slice_id} ${type} ${arg} " $DBFILE;  \
-       echo "${slice_id} ${type} ${arg} ${rule_base} ${pipe_base} ${timeout}" ) > ${DBFILE}.tmp
+       echo "${slice_id} ${type} ${arg} ${ipfw_rule} ${pipe_index} ${timeout}" ) > ${DBFILE}.tmp
     mv ${DBFILE}.tmp ${DBFILE}
 }
 
+#
 # Delete a given configuration
-do_delete() { # slice_id type arg
-    local pipe_in pipe_out pipe_base rule_base rule_nr
-    local slice_id=$1 type=$2 arg=$3
-
-    [ "${arg}" = "" ] && abort "Missing arg on 'delete'"
+# if block_deletion !0 free block resources (if necessary)
+# otherwise leave the block allocated in case
+# we are adding the first rule
+do_delete() { # block_deletion slice_id type arg
+    local ipfw_pipe_in ipfw_pipe_out pipe_index ipfw_rule
+    local block_deletion=$1 slice_id=$2 type=$3 arg=$4
+
+    [ "${type}" = "BLOCK" ] && abort "A Block can not be deleted"
+    [ "${arg}" = "" ] && abort "Missing args on 'delete', expected on of {SERVICE|SERVER|CLIENT} port_number"
     set `find_rule $slice_id $type $arg`
-    rule_base=$1; pipe_base=$2
-    [ "$rule_base" = "0" ] && return           # no rules found
-
-    rule_nr=$(($rule_base + 39999))            # XXX only individual rules
-    pipe_in=$(($pipe_base + $pipe_base + 9998))
-    pipe_out=$(($pipe_in + 1))
-
-    $IPFW delete ${rule_nr}
-    $IPFW pipe delete ${pipe_in}
-    $IPFW pipe delete ${pipe_out}
-    echo "removed configuration ${slice_id} ${type} ${arg}"
-    [ "$TEST" = "1" ] && return
+    ipfw_rule=$1; pipe_index=$2
+    [ "$ipfw_rule" = "0" ] && return           # no rules found
+
+    # find actual pipe numbers XXX do as function
+    ipfw_pipe_in=$(($IPFW_PIPE_MIN + $((2 * $(($pipe_index - 1)))) ))
+    ipfw_pipe_out=$(($ipfw_pipe_in + 1))
+
+    echo "removing configuration ${slice_id} ${type} ${arg}"
+    [ "$TEST" = "1" ] && return 0
+    $IPFW delete ${ipfw_rule}
+    $IPFW pipe delete ${ipfw_pipe_in}
+    $IPFW pipe delete ${ipfw_pipe_out}
     # remove from the database
     grep -v -- "^${slice_id} ${type} ${arg} " $DBFILE > ${DBFILE}.tmp
     mv ${DBFILE}.tmp ${DBFILE}
 
-    # XXX if the use block is empty
+    # if there are no mor rules for the user
     # remove the table entry from ipfw and from the db
+    [ $block_deletion = 0 ] && return 0
+
+    local rule_counter=`grep ^${slice_id} ${DBFILE} | wc -l`
+    [ $rule_counter -gt 1 ] && return 0        # there are still user rules
+    # delete the block and clean the table
+    local block_n=`grep "^${slice_id} BLOCK" ${DBFILE} | cut -d " " -f 3`
+    debug "Deleting BLOCK <${block_n}> entry from ipfw and from the database"
+    table_remove $slice_id $block_n 
+}
+
+#
+# compare the argument with the first two field of
+# the database.
+# On match returns the block number, otherwise returns 0.
+# no echo inside
+find_block() { # $1 slice_id
+    local ret
+    ret=`grep -- "^$1 BLOCK " $DBFILE`
+
+    [ x"$ret" = x ] && echo "0" && return      # nothing found
+    # ignore multiple matches. If the db is corrupt we are
+    # screwed anyways
+    set $ret
+    echo "$3"
 }
 
+#
+# remove the default user rule and
+# the a BLOCK entry from ipfw and update the db
+# no echo inside
+table_remove() { # $slice_id $block_n
+    [ "$TEST" = "1" ] && return 0
+
+    # compute and delete the last user rule
+    local ipfw_rulemax=$(($IPFW_RULE_MIN + $(($M *${block_n})) -1))
+    ${IPFW} table $SLICE_TABLE delete $slice_id
+    ${IPFW} delete ${ipfw_rulemax}
+    ( grep -v -- "^${slice_id} BLOCK ${block_n}" $DBFILE; ) > ${DBFILE}.tmp
+    mv ${DBFILE}.tmp ${DBFILE}
+    return 0
+}
+
+#
+# find a rule and pipes
+# allocate a new block if no rules are found.
+#
+# Return ipfw_rule, index_pipe and new_rule
+# new_rule is a flag set if the rule was not found
+#
+# return 0 as first argument if there are no resource available
+find_allocate() { # slice_id type arg
+    local slice_id=$1 type=$2 arg=$3
+    local ipfw_rule pipe_index new_block=0
+
+    # search for already allocated rule and pipes
+    set `find_rule $slice_id $type $arg`
+    ipfw_rule=$1; pipe_index=$2
+    [ ! ${ipfw_rule} = 0 ] && echo $ipfw_rule $pipe_index "0" && return 0      # rules found, return
+
+    # no rules found, search for an already allocated block
+    # if not found, allocate a new block
+    local block_n=`find_block ${slice_id}`
+    [ ${block_n} = "0" ] && new_block=1 && block_n=`allocate_block`
+    [ ${block_n} = "0" -o ${block_n} -gt $BLOCK_MAX ] && echo 0 && return 0;
+
+    # here we have a valid block id
+    # compute the user block ruleset
+    local ipfw_rulemin=$(($IPFW_RULE_MIN + $(($M *$(($block_n - 1))))))
+    local ipfw_rulemax=$(($(($ipfw_rulemin + $M)) - 1 ))
+
+    # find rule and pipes, we reserve the last rule to the user default rule
+    set `allocate_resources $ipfw_rulemin $(($ipfw_rulemax - 1))`
+    ipfw_rule=$1; pipe_index=$2
+    [ $ipfw_rule = 0 ] && echo 0 && return 0   # no resources
+
+    # configure the table, add the last user rule and update the database
+    if [ "$TEST" = "0" -a $new_block = 1 ] ; then
+       ${IPFW} table $SLICE_TABLE add ${slice_id} ${ipfw_rulemin} > /dev/null
+       ${IPFW} add ${ipfw_rulemax} allow all from any to any > /dev/null
+       ( echo "${slice_id} BLOCK ${block_n}" ) >> ${DBFILE}
+    fi
+
+    echo $ipfw_rule $pipe_index "1"
+    return 0
+
+}
+
+#
 # called with the database file as input
 # compare the tuple <slice_id type arg> with
 # the current firewall configuration. The database contains
-#      slice_id type arg rule_base pipe_base timeout
-# On match returns <rule_base pipe_base timeout>
+#      slice_id type arg ipfw_rule pipe_index timeout
+# On match returns <ipfw_rule pipe_index timeout>
 # On non match returns 0 0 0
-find_rule() { # $1 slice_id $2 type $3 arg
+# no echo inside
+find_rule() { # slice_id type arg
     local ret
-    ret=`grep -- "^$1 $2 $3 " $DBFILE`
+    ret=`grep -- "^$1 $2 $3 " $DBFILE | grep -v BLOCK`
 
     [ x"$ret" = x ] && echo "0 0 0 " && return # nothing found
     # ignore multiple matches. If the db is corrupt we are
@@ -262,7 +374,7 @@ find_rule() { # $1 slice_id $2 type $3 arg
     echo "$4 $5 $6"
 }
 
-
+#
 # Find a hole in a list of numbers within a range (boundaries included)
 # The input is passed as a sorted list of numbers on stdin.
 # Return a "0" rule if there is no rule free
@@ -277,32 +389,108 @@ find_hole() {  # min max
     echo $cand
 }
 
+#
 # returns a free rule and pipe base for client|server|service
+# within a block
 # Returns r=0 if there are no resources available
-allocate_resources() {
+# no echo inside
+allocate_resources() { # ipfw_minrule ipfw_maxrule
     local p r
     # remove comments, extract field, sort
-    p=`grep -v '^#' $DBFILE | awk '{print $5}' | sort -n | \
+    p=`grep -v '^#' $DBFILE | grep -v BLOCK | awk '{print $5}' | sort -n | \
        find_hole $PIPE_MIN $PIPE_MAX`
-    r=`grep -v '^#' $DBFILE | awk '{print $4}' | sort -n | find_hole $1 $2`
+    r=`grep -v '^#' $DBFILE | grep -v BLOCK | awk '{print $4}' | sort -n | \
+       find_hole $1 $2`
     [ $r = 0 -o $p = 0 ] && r=0                # no resources available
     echo $r $p
 }
 
+#
+# allocate a free block
+# Returns 0 if there are no resources available
+# no debug inside
+allocate_block() {
+    b=`grep -v '^#' $DBFILE | grep BLOCK | awk '{print $3}' | sort -n | \
+       find_hole $BLOCK_MIN $BLOCK_MAX`
+    echo $b
+}
+
+#
+# parse the ipfw database and remove expired rules
+#
+# Each timeout value stored in the database is compared against
+# the current time.  If the timeout is older than current,
+# the rules and related pipes will be deleted.
+kill_expired() { # slice_id type arg
+    local match timeout
+
+    # if there is no database file exit
+    [ ! -f ${DBFILE} ] && return 0
+
+    # Get the current time
+    now=`date -u +%s`
+
+    cp ${DBFILE} ${DBFILE}.kill
+    cat ${DBFILE}.kill | grep -v BLOCK |
+    while read line; do
+       match=`echo $line|cut -d " " -f 1-3`
+       timeout=`echo $line|cut -d " " -f 6`
+       [ $now -gt $timeout ] && do_delete 1 $match
+    done
+    rm ${DBFILE}.kill
+}
+
+#
+# execute functions from root context
+# can be used from root context as follow:
+# echo "super $command $args" | /vsys/ipfw-be 0
+do_super() { # $arguments...
+       case $1 in
+       init)
+           ipfw_init; return 0
+           ;;
+       dbcleanup)
+           clean_db; return 0
+           ;;
+       killexpired)
+           kill_expired; return 0
+           ;;
+       *)
+           abort "Invalid super command"
+           ;;
+       esac
+}
+
+#
+# refresh the rule timeout
+do_refresh() { # slice_id type arg timeout
+    local ipfw_pipe_in ipfw_pipe_out pipe_index 
+    local slice_id=$1 type=$2 arg=$3 timeout=$4
+
+    debug "do_refresh type: <$type> arg: <$arg> timeout: <$timeout>"
+    [ "${type}" = "BLOCK" ] && abort "BLOCK rule not valid"
+    [ "${timeout}" = "" ] && abort "Missing args on 'refresh', expected on of {SERVICE|SERVER|CLIENT} port_number"
+    set `find_rule $slice_id $type $arg`
+    ipfw_rule=$1; pipe_index=$2
+    [ "${ipfw_rule}" = "0" ] && debug "no rules found" && return 0             # no rules found
+
+    [ "$TEST" = "1" ] && return
+    # update the database with the new timeout value
+    ( grep -v -- "^${slice_id} ${type} ${arg} " $DBFILE;  \
+       echo "${slice_id} ${type} ${arg} ${ipfw_rule} ${pipe_index} ${timeout}" ) > ${DBFILE}.tmp
+    mv ${DBFILE}.tmp ${DBFILE}
+    echo "refreshed timeout for rule ${type} ${arg}"
+}
+
+#
 # process a request.
 # A request is made by a set of arguments formatted as follow:
 #
 # config {server|client|service} arg [-t timeout] PIPE_IN <pipe_conf> PIPE_OUT <pipe_conf>
 # show {rules|pipes} [args]
 # delete type arg
+# refresh type arg [-t timeout]
 #
-# XXX not implemented yet
-# config {rule|pipe} num <parameters>
-# alloc rules|pipes [-t timeout] # returns a block of NUM_RULES or NUM_PIPES
-# release rules|pipes args     # release the entire block
-# refresh rules|pipes args [-t timeout]
-#
-# where uppercase values are keywords.
 # The timeout value is expressed as:
 # week, day, month or anything else accepted by the date command.
 # The id of the slice issuing the request is in the $SLICE_ID variable,
@@ -310,7 +498,6 @@ allocate_resources() {
 process() {
     local new_pipe=0
     local timeout TMP i rule_base pipe_base
-    local slicename=${SLICENAME}
     local cmd=$1 ; shift
     local debug_args="$*";
     local type=$1 ; shift
@@ -318,47 +505,53 @@ process() {
     debug "Received command: <$cmd> arguments: <$debug_args>"
 
     # set the timeout value
-    # clean args from the timeout keyword
+    # if present, extract the '-t timeout' substring from the command line
     timeout=`echo ${args} | ${SED} ${SEDOPT} 's/(.+)( -t [a-zA-Z0-9]+ )(.*)/\2/'`
+    # if the '-t timeout' is specified, use the timeout provided by the user
     if [ "${timeout}" != "${args}" ] ; then    # match
+       # remove the '-t ' option
        timeout=`echo ${timeout} | ${SED} ${SEDOPT} 's/-t //'`
-       check_timeout ${timeout}        # abort on error
+       timeout=`check_timeout ${timeout}`
+       [ $timeout = 0 ] && abort "Date format $1 not valid"
+       # clean the arguments
        args=`echo ${args} | ${SED} ${SEDOPT} 's/(.+)( -t [a-zA-Z0-9]+ )(.*)/\1 \3/'`
     else
-       timeout=1day            # default to 1 day
+       # use the default value, no need to check for correctness, no need to clean arguments
+       timeout=`date --date="1day" +%s`                # default to 1 day
     fi
 
+    # if the table rule is not present, add it
+    local table_rule=`${IPFW} show $S | grep "skipto tablearg" | grep "lookup jail $SLICE_TABLE"`
+    [ -z "$table_rule" ] && ipfw_init
+
     debug "Timeout $timeout"
     # Handle special requests: show and delete
     case x"$cmd" in 
-    x"alloc") 
-       abort "XXX unimplemented " && return 0
-       ;;
     x"config") 
        [ "$type" = "server" ] && do_config $SLICE_ID $timeout $type $args && return 0
        [ "$type" = "client" ] && do_config $SLICE_ID $timeout $type $args && return 0
        [ "$type" = "service" ] && do_config $SLICE_ID $timeout $type $args && return 0
-       [ "$type" = "rule" ] && abort "XXX unimplemented " && return 0
-       [ "$type" = "pipe" ] && abort "XXX unimplemented " && return 0
-       abort "'config' should be followed by {server|client|service|rule|pipe}"
+       abort "'config' should be followed by {server|client|service}"
        ;;
     x"delete") 
-       do_delete ${SLICE_ID} $type $args
+       do_delete 1 $SLICE_ID $type $args
        ;;
     x"refresh") 
-       abort "XXX unimplemented " && return 0
-       do_refresh ${SLICE_ID} $type $args $timeout
-       ;;
-    x"release") 
-       abort "XXX unimplemented " && return 0
-       do_release ${SLICE_ID} $type $args
+       do_refresh $SLICE_ID $type $args $timeout && return 0
        ;;
     x"show")
-       # XXX should filter on jail
+       # XXX filter out sliver rules
        [ "$type" = "rules" ] && ${IPFW} show && return 0
        [ "$type" = "pipes" ] && ${IPFW} pipe show && return 0
        abort "'show' should be followed by {rules|pipes}"
        ;;
+    x"super")
+       [ $SLICE_ID = 0 ] && do_super $type $args && return 0
+       abort "no permission for ipfw-be super execution"
+       ;;
+    x"help")
+       do_help && return 0
+       ;;
     *)
        # help XXX to be done
        abort "'command' should be one of {show|config|delete|refresh|release}"
@@ -369,15 +562,8 @@ process() {
 # validate the timeout
 check_timeout() { # timeout
     local tt=`date --date="${1}" +%s`
-    [ "$?" != "0" ] && abort "Date format $1 not valid"
-}
-
-do_release() { # slice_id type args timeout
-    return
-}
-
-do_refresh() { # slice_id ttype args
-    return
+    [ "$?" != "0" ] && echo 0 && return
+    echo $tt
 }
 
 do_config() { # slice_id timeout type arg PIPE_IN pipe_conf PIPE_OUT pipe_conf
@@ -411,7 +597,7 @@ do_config() { # slice_id timeout type arg PIPE_IN pipe_conf PIPE_OUT pipe_conf
     [ "$CONFIG_PIPE_OUT" = "" ] && abort "Missing pipe out configuration"
 
     debug "Configuration Required:"
-    debug "slice_id: $slice_id"
+    debug "slice_id: $SLICE_ID"
     debug "type: $type"
     debug "arg: $arg"
     debug "timeout: $timeout"
@@ -419,42 +605,18 @@ do_config() { # slice_id timeout type arg PIPE_IN pipe_conf PIPE_OUT pipe_conf
     debug "PIPE_OUT: $CONFIG_PIPE_OUT"
     debug "-----------------------"
 
-    # XXX Search if there is a block already allocated to the slice_id
-    # if not present
-    # {
-    #    allocate the block;
-    #    update the db;
-    #    add table to ipfw;
-    # }
-    # Returns the slice base rule number
-
     # check if the link is already configured
-    debug "Search for ${slice_id} ${type} ${arg}"
-
-    set `find_rule ${slice_id} ${type} ${arg}`
-    local rule_base=$1
-    local pipe_base=$2
-    local new_pipe=0
+    debug "Search for slice_id: ${slice_id} type: ${type} port: ${arg}"
 
-    if [ ${rule_base} = "0" ] ; then
-       debug "Rule not found, new installation"
-       new_pipe=1
-       set `allocate_resources $RULE_IN_MIN $RULE_IN_MAX`
-       rule_base=$1; pipe_base=$2
-        [ $rule_base = 0 ] && abort "no resources available"
-       debug "found free resources rule: $rule_base pipe: $pipe_base"
-    else
-       debug "Rule found, just changing the pipe configuration"
-    fi
+    set `find_allocate ${slice_id} ${type} ${arg}`
+    local ipfw_rule=$1 pipe_index=$2 new_rule=$3
 
-    add_rule $new_pipe $slice_id $type $arg $rule_base $pipe_base $timeout
+    [ ${ipfw_rule} = 0 ] && abort "No resources available"
+    debug "Found or allocated resources ipfw_rule: ${ipfw_rule} and pipe_index: ${pipe_index}"
 
-    # if present, call a hook in order to collect statistical
-    # information on dummynet usage
-    if [ -n "${HOOK}" -a -x "${HOOK}" ]; then
-       # XXX
-       ${HOOK} $slice_id $type $port $rule_base $pipe_base $timeout &
-    fi
+    add_rule $slice_id $new_rule $type $arg $ipfw_rule $pipe_index $timeout
+    hook_call $type $port $rule_base $pipe_base $timeout
+    return 0; # link configured, exit
 }
 
 #
@@ -474,12 +636,94 @@ release_lock() {
     rm -f $lockfile
 }
 
+#
+# initialize the firewall with PlanetLab default rules
+ipfw_init() {
+       ${IPFW} -q delete $S
+       ${IPFW} -q delete $D
+       ${IPFW} add $S skipto tablearg lookup jail $SLICE_TABLE
+       ${IPFW} add $D allow all from any to any
+}
+
+#
+# if present, call a hook function
+# Arguments are:
+# slice_id type port rule_base pipe_base timeout
+hook_call() {
+       if [ -n "${HOOK}" -a -x "${HOOK}" ]; then
+               debug "Calling the hook function."
+               ${HOOK} ${SLICE_ID} "$*" &
+       fi
+}
+
+do_help() {
+       cat << EOF
+Usage:
+        ./neconfig [SERVER|CLIENT|SERVICE] port [-t timeout]    \
+                PIPE_IN <pipe in configuration> PIPE_OUT <pipe out configuration>
+        ./netconfig show [rules|pipes]
+        ./netconfig delete [SERVER|CLIENT|SERVICE] port
+        ./netconfig refresh [-t timeout] [SERVER|CLIENT|SERVICE] port
+
+We assume three type of connections
+  SERVER we know the local port P, and do the
+       bind/listen/accept on the local socket.
+               pipe_in in dst-port P
+               pipe_out out src-port P
+
+  CLIENT we know the remote port P, and do a connect to it
+       (src and dst are swapped wrt the previous case)
+               pipe_in in src-port P
+               pipe_out out dst-port P
+
+  SERVICE we run a server on local port P, and also connect
+       from local clients to remote servers on port P.
+               pipe_in in { dst-port P or src-port P }
+               pipe_out out { src-port P or dst-port P }
+
+  On a given port a user can have one CLIENT and/or one SERVER
+  configuration or one SERVICE configuration.
+  When a SERVICE configuration is installed any existing CLIENT
+  and SERVER configuration on the same port are removed.
+  When a CLIENT or SERVER configuration is installed any existing
+  SERVICE configuration on the same port is removed.
+
+The pipe configuration, both for the upstream and downstream link,
+follow the dummynet syntax. A quick and not exaustive example
+of the parameters that can be used to configure the delay,
+the bandwidth and the packet loss rate for a link follow:
+
+        PIPE_IN|PIPE_OUT delay 100ms bw 1Mbit/s plr 0.1
+
+The full documentation is on the manpage[1].
+
+The timeout value follow the linux 'date' command format[2]
+and can be specified as follow:
+        1week
+        2hours
+        3days
+
+--- References:
+[1] http://www.freebsd.org/cgi/man.cgi?query=ipfw
+[2] http://linuxmanpages.com/man1/date.1.php
+EOF
+}
+
 # ALLOCATION OF RULES AND PIPES
-# The ruleset is structured as follows
-#      1...X-1 generic rules
-#      X       skipto tablearg jail 0-65535 lookup jail-table
-#      X+1..Y-1 ... other generic rules
-#      Y       allow ip from any to any
+# The ruleset is composed by different sections, as follow:
+# - a first set of rules is reserved and is configurable by
+#   the root context only;
+# - the skipto rule (S), used to optimize the slice rule search;
+# - a second block of reserved rules;
+# - a default (D) rule for the generic configuration;
+# - the slice reserved rules, a block of M rules for each slice;
+# - the firewall default rule.
+#
+# To summarize:
+#      1...S-1 first block of reserved rules
+#      S       skipto tablearg lookup jail 1
+#      S+1..D-1 ... second block of reserved rules
+#      D       allow ip from any to any
 #
 #      RULE_BASE <block of M entries for first user>
 #      RULE_BASE+M <block of M entry for second user ...>
@@ -504,41 +748,26 @@ release_lock() {
 #   For each of these slices we reserve a block of M ipfw rules
 #   starting at some RULE_BASE rule number.
 #   The database entry for this info has the form
-#      XID     TABLE   block_number
+#      XID     BLOCK   block_index
 #   where blocks are numbered sequentially from 1.
-#   The actual rule number is RULE_BASE + M*(block_number)
-#   (we don't care if we waste some rules)
+#   The actual ipfw rule numbers for the block are the M rules starting at:
+#       IPFW_RULE_MIN + (M-1)*(block_number)
 #
 # - one entry for each predefined config (CLIENT, SERVER, SERVICE).
 #   The database entry for this info has the form
-#      XID     {CLIENT|SERVER|SERVICE} arg     rule_nr pipe_index
-#   rule_nr is the absolute rule number for this configuration
-#   (it must be within the block of M rules allocated to the slice)
+#      XID     {CLIENT|SERVER|SERVICE} arg     ipfw_rule pipe_index
+#   ipfw_rule is the unique ipfw rule number used for this configuration
+#   (it must be within the block of M rule indexes allocated to the slice)
 #   pipe_index is the index of the couple of pipes used for the
-#   configuration. pipe_index starts from 1.
-
-# ---OLD-START--
-# pipes are always allocated in pairs
-# rules are either individual or in groups of size NUM_RULES (e.g. 4)
-# and are allocated in two different parts of the rule namespace
-# (e.g. blocks from 10000 to 49999 and individuals from 50000 to 59999)
-# Internally allocator uses the base number for each item, e.g.
-# rule 10000..49999 -> rule_base=1..10000
-# rule 50000..59999 -> rule_base=10001..20000
-# pipe 10000..59999 -> pipe_base=1..25000
-# a bit of math lets us compute the correct numbers.
-# For CLIENT, SERVER, SERVICE the database contains entries as
-#      XID     TYPE    arg     rule_base       pipe_base
-# For blocks the entries are
-#      XID     RULE    -       rule_base       -
-#      XID     PIPE    -       -               pipe_base
-# When a rule or pipe is referenced we first check that the owner owns it.
-# more details below.
+#   configuration. pipe_index starts from 1. The actual pipes are
+#   ipfw_pipein = IPFW_PIPE_MIN + 2*(pipe_index-1)
+#   ipfw_pipeout = ipfw_pipein + 1
+#
 
 #-- main starts here
 debug "--- $0 START for $SLICENAME ---"
 
-# If the db does not exist, create it and we clean rules and pipes
+# If the db does not exist, create it and clean rules and pipes
 [ ! -e ${DBFILE} ] && clean_db
 
 # A request to the vsys backend is composed by a single line of input