+# process a request.
+# A request is made by a set of arguments formatted as follow:
+#
+# config {server|client|service} arg [-t timeout] IN <pipe_conf> OUT <pipe_conf>
+# show {rules|pipes} [args]
+# delete type arg
+# refresh type arg [-t timeout]
+#
+# 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,
+# set at the beginning of this script.
+process() {
+ local new_pipe=0
+ local timeout TMP i rule_base pipe_base
+ local cmd=$1 ; shift
+ local debug_args="$*";
+ local type=$1 ; shift
+ local args="$*"
+ debug "Received command: <$cmd> arguments: <$debug_args>"
+
+ # set the timeout value
+ # 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 //'`
+ 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
+ # 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"config")
+ case x"$type" in
+ xserver|xSERVER|xclient|xCLIENT|xservice|xSERVICE)
+ do_config $SLICE_ID $timeout $type $args && return 0
+ ;;
+ esac
+ abort "'config' should be followed by {CLIENT|SERVER|SERVICE}"
+ ;;
+ x"delete")
+ do_delete 1 $SLICE_ID $type $args
+ ;;
+ x"refresh")
+ do_refresh $SLICE_ID $type $args $timeout && return 0
+ ;;
+ x"show")
+ # 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}"
+ ;;
+ esac
+}
+
+# validate the timeout
+check_timeout() { # timeout
+ local tt=`date --date="${1}" +%s`
+ [ "$?" != "0" ] && echo 0 && return
+ echo $tt
+}
+
+do_config() { # slice_id timeout type arg IN pipe_conf OUT pipe_conf
+ local slice_id=$1; shift
+ local timeout=$1; shift
+ local type=$1; shift
+ local arg=$1; shift # XXX addr not yet implemented
+ local p h; # port and optional hostname
+
+ [ "$1" != "IN" ] && abort "Missing addr:port, or IN requested"
+ shift
+
+ # read pipe in configuration
+ i=""
+ while [ "$1" != "" -a "$1" != "OUT" ] ; do
+ i="$i $1"
+ shift
+ done
+ CONFIG_PIPE_IN="$i" # XXX local ?
+ [ "$CONFIG_PIPE_IN" = "" ] && abort "Missing pipe in configuration"
+
+ [ "$1" != "OUT" ] && abort "Missing pipe in configuration, or missing OUT"
+ shift
+
+ # read pipe out configuration
+ i=""
+ while [ "$1" != "" ] ; do
+ i="$i $1"
+ shift
+ done
+ CONFIG_PIPE_OUT="$i" # XXX local ?
+ [ "$CONFIG_PIPE_OUT" = "" ] && abort "Missing pipe out configuration"
+
+
+ # process the argument (port and hostname are separated by a @)
+ # split the argument, and prepare the remote host configuration string
+ p=`echo $arg | cut -s -d "@" -f1-` # empty it there is no separator
+ if [ "$p" = "" ] ; then
+ p=$arg
+ else
+ p=`echo $arg | cut -d "@" -f1`
+ h=`echo $arg | cut -d "@" -f2`
+ fi
+
+ # A port value is mandatory
+ [ "$p" = "" ] && abort "A port value is mandatory."
+
+ # SERVICE do not support remote hostname filtering
+ [ $type = "service" ] && [ "$h" != "" ] && \
+ abort "The service configuration do not support filtering remote hostnames."
+
+ debug "Configuration Required:"
+ debug "slice_id: $SLICE_ID"
+ debug "type: $type"
+ debug "full arg: $arg"
+ debug "mandatory port(s): $p optional hostname(s): $h"
+ debug "timeout: $timeout"
+ debug "IN: $CONFIG_PIPE_IN"
+ debug "OUT: $CONFIG_PIPE_OUT"
+ debug "-----------------------"
+
+ # check if the link is already configured
+ debug "Search for slice_id: ${slice_id} type: ${type} port: ${arg}"
+
+ set `find_allocate ${slice_id} ${type} ${arg}`
+ local ipfw_rule=$1 pipe_index=$2 new_rule=$3
+
+ [ ${ipfw_rule} = 0 ] && abort "No resources available"
+ debug "Found or allocated resources ipfw_rule: ${ipfw_rule} and pipe_index: ${pipe_index}"
+
+ 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
+}
+
+#
+# acquire the lock XXX check lockfile
+acquire_lock() {
+ [ "$TEST" = 1 ] && return
+ lockfile -s 0 -r 0 $lockfile 2> /dev/null
+ if [ $? -ne 0 ] ; then
+ echo "lock acquisition failed"
+ exit -1
+ fi
+}
+
+#
+# release the lock
+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 {CLIENT|SERVER|SERVICE} arg [-t timeout] \
+ IN <pipe in configuration> OUT <pipe out configuration>
+ ./netconfig show {rules|pipes}
+ ./netconfig delete {CLIENT|SERVER|SERVICE} arg
+ ./netconfig refresh [-t timeout] {CLIENT|SERVER|SERVICE} arg
+
+We support three modes of operation:
+
+ CLIENT programs on the node connect to remote ports
+ and/or addresses. Emulation intercepts traffic
+ involving those ports/addresses
+
+ SERVER programs on the node listen on specific ports.
+ Emulation intercepts traffic on those ports,
+ optionally limited to specific client addresses.
+
+ SERVICE the node runs both clients and servers,
+ we can only specify the ports on which emulation
+ is configured.
+
+ 'arg' has the form PORTLIST[@ADDRLIST], where ADDRLIST is
+ optional and only supported for CLIENT and SERVER modes.
+ PORTLIST and ADDRLIST can be specified as any valid port
+ or address specifier in ipfw, e.g.
+ - a single value 443 or 10.20.30.40/24
+ - a comma-separated list 1111,2222,3333 1.2.3.4,5.6.7.8
+ - a range 1111-2222 (only for ports)
+ Addresses can also be specified as symbolic hostnames, and
+ they are resolved when the rule is installed.
+ Note that they always indicate the remote endpoint.
+
+ 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's configuration, both for the upstream and downstream link,
+follows 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:
+
+ IN|OUT delay 100ms bw 1Mbit/s plr 0.1
+
+The profile file, if present, should be located into the sliver's
+root directory.
+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
+}
+
+#--- DOCUMENTATION AND INTERNAL ARCHITECTURE ---
+#
+# When a user configures an emulated link, we need to allocate
+# two pipes and one ipfw rule number to store the parameters.
+# Reconfigurations of existing links reuse the previous resources.
+# We keep track of all resources (pipes, rules and blocks of rules)
+# in a database stored in a text file, see DATABASE FORMAT below.
+#
+# Pipes are allocated in pairs. In the database each pair is numbered
+# from PIPE_MIN to PIPE_MAX. The actual pipe numbers for each pair are
+#
+# ipfw_pipein = IPFW_PIPE_MIN + 2*(pipe_index-1)
+# ipfw_pipeout = ipfw_pipein + 1
+#
+# The rules number is allocated within a block of M consecutive rules
+# for each slice. The block is allocated at the first configuration
+# of an emulated link, and deallocated when the last link is removed.
+# In the database, blocks are numbered from BLOCK_MIN to BLOCK_MAX,
+# and the range of rules for a given block_index is
+#
+# ipfw_min_rule = RULE_BASE
+# ipfw_max_rule = RULE_BASE + ((M-1)*block_index) -1
+#
+# All lookups, and the block allocation, are done in find_allocate().
+# The rule_number and pipe_index are written in the database
+# by add_rule() after checking the correctness of the request.
+#
+#
+#--- RULESET STRUCTURE ---
+# The ruleset is made of different sections, as follows:
+# - an initial block of rules, reserved and configurable by
+# the root context only;
+# - a skipto rule (S), used to jump directly to the block
+# associated with a given slice;
+# - a second block of reserved rules, to catch remaining traffic.
+# This ends with rule number D which is an 'accept all';
+# - after D, we have a block of M rule numbers for each slice.
+# Each of these blocks ends with an 'accept all' rule;
+# - finally, rule 65535 is the firewall's 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 ...>
+# ...
+#
+#--- DATABASE FORMAT ---
+# The database is stored in a text file, and contains one record per
+# line with the following structure
+#
+# XID TYPE arg1 arg2 ...
+#
+# Whitespace separates the fields. arg1, arg2, ... have different
+# meaning depending on the TYPE. XID is the slice ID.
+#
+# In the database we have the following records:
+# - one entry of type BLOCK for each slice with configured links.
+# This entry represents the block_index of the block of M ipfw
+# rules allocated to the slice, as follows:
+#
+# XID BLOCK block_index
+# (BLOCK_MIN <= block_index <= BLOCK_MAX)
+#
+# - one entry for each link (CLIENT, SERVER, SERVICE).
+# The database entry for this info has the form
+#
+# XID {CLIENT|SERVER|SERVICE} arg ipfw_rule pipe_index timeout
+#
+# 'TYPE' reflects the configuration mode;
+# 'arg' is PORTLIST@ADDRLIST and is used as a search key together
+# with the XID and TYPE;
+# 'ipfw_rule' is the unique ipfw rule number used for this
+# emulated link. It must be within the block of M rule numbers
+# allocated to the slice;
+# 'pipe_index' is the index of the pair of pipes used for the
+# configuration;
+
+#-- main starts here
+debug "--- $0 START for $SLICENAME ---"
+
+# 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
+read REQ # read one line, ignore the rest
+set_verbose ${REQ} # use inital -v if present
+set_test ${REQ} # use inital -t if present
+REQ="`filter ${REQ}`" # remove -v and -q and invalid chars
+debug "--- processing <${REQ}>"
+acquire_lock # critical section
+process ${REQ}
+release_lock
+debug "--- $0 END ---"
+exit 0