3 # Marta Carbone, Luigi Rizzo
4 # Copyright (C) 2009 Universita` di Pisa
6 # This script the vsys backend used to configure emulation.
8 # - reads the user's input from the vsys input pipe
9 # - validates the input
10 # - configures the firewall
11 # - writes results on the output vsys pipe
13 # Configurable variables are at the beginning (only HOOK so far)
15 # If HOOK is set, ${HOOK} is called before configuring a rule.
16 # A sample hook can be found in the ipfwroot.rpm package,
17 # it can be used to collect statistical information on dummynet usage.
18 # To configure a hook, set the HOOK variable as follow:
19 # HOOK=/tmp/sample_hook
21 #--- You should not touch anything below this line. ----
22 # For documentation see ARCHITECTURE near the end of the file.
24 #--- global variables ---
25 VERBOSE=0 # set to !0 to enable debug messages
26 TEST=0 # set to 1 for test mode
28 # The database and the lock file
30 lockfile=/var/lock/ipfw.lock
32 # Min and max value (inclusive) for block_index
35 M=50 # size of per-slice block of rules
36 # Min and max value (inclusive) for pipe_index
40 # These are the actual rule numbers used in ipfw
41 IPFW_RULE_MIN=10000 # initial per-slice rule number
42 IPFW_PIPE_MIN=10000 # initial pipe number
44 # The skipto and the generic default rule
45 # these values are used to initialize the firewall
46 SLICE_TABLE=1 # table number used for slice ids lookup
47 S=1000 # firewall rule number for the skipto rule
48 D=2000 # default rule for reserved section
50 # set slicename and slice_id
51 # these are the credential of the user invoking the backend
53 SLICE_ID=`id -u $SLICENAME`
54 [ x"$SLICE_ID" = x"" ] && echo "No sliver present." && exit
57 # XXX check consistency for variables {}
60 [ -x ${SED} ] || { SED=`which sed` ; SEDOPT=-E ; }
62 IPFW_CHECK="/sbin/ipfw -n"
64 debug() { # $1 message to be displayed
65 [ x"${VERBOSE}" != x"0" ] && echo "ipfw-be: $1"
68 # if the first argument is -v, enable verbose mode
70 [ x"$1" = x"-v" -o x"$2" = x"-v" ] && VERBOSE=1
73 # set test mode if -q is found
75 [ x"$1" = x"-q" -o x"$2" = x"-q" ] || return
77 IPFW="/bin/echo ipfw:"
78 IPFW_CHECK="/bin/echo ipfw -n:"
81 abort() { # $1 message to be displayed in case of error
83 echo "ipfw-be aborting (netconfig help): $1"
87 # remove dangerous characters from user input
88 # if present, the leading '-v/-q' will be removed
89 filter() { # $* variables to be filtered
90 [ x${1} = x"-v" -o x${1} = x"-q" ] && shift
91 [ x${1} = x"-v" -o x${1} = x"-q" ] && shift
92 # allowed chars are: numbers, uppercase and lowercase letters,
93 # spaces, and the following symbols: .,_-/
94 echo "$*" | ${SED} ${SEDOPT} 's/[^\t0-9a-zA-Z., _\/\{}@-]*//g'
97 # remove all entries from the ipfw config, and create an empty db
101 # we would like to delete ranges of rules and pipes but this
102 # is not supported so for the time being we kill them all
104 ${IPFW} -q pipe flush
105 ${IPFW} -q table $SLICE_TABLE flush
106 #${IPFW} delete ${IPFW_RULE_MIN}-${IPFW_RULE_MAX}
107 #${IPFW} pipe delete ${IPFW_PIPE_MIN}-${IPFW_PIPE_MAX}
108 # since all rules are now deleted, we should initialize the firewall
113 # Add the ipfw rule/pipe and update the database.
114 # The pipe-in and pipe-out config are through global variables
115 # rule_in rule_out because they may be long. XXX why ?
116 # Other arguments are on the command line
118 # the new_rule variable is set if the rule to be installed is new
119 # we need to know this because we do not want to clean
120 # rule counters on pipes reconfiguration
121 add_rule() { # slice_id new_rule type arg ipfw_rule pipe_index timeout
122 local slice_id=$1 new_rule=$2 type=$3 arg=$4
123 local ipfw_rule=$5 pipe_index=$6 timeout=$7
124 local ipfw_pipe_in ipfw_pipe_out check_timeout
125 local p h # used to split the argument
128 # local rule_in rule_out # XXX test if this works
129 # find actual pipe numbers
130 ipfw_pipe_in=$(($IPFW_PIPE_MIN + $((2 * $(($pipe_index - 1)))) ))
131 ipfw_pipe_out=$(($ipfw_pipe_in + 1))
132 local del # used to delete incompatible configurations
134 # split the argument, and prepare PORTLIST (p) and ADDRLIST (h)
135 p=`echo $arg | cut -s -d "@" -f1-` # empty if no separator
136 if [ "$p" = "" ] ; then
139 p=`echo $arg | cut -d "@" -f1`
140 h=`echo $arg | cut -d "@" -f2`
143 if [ "$h" = "" ] ; then
148 h_out=" dst-ip ${h} "
151 # first, call ipfw -n to check syntax, if ok move on and do the action
152 if [ x"$new_rule" != x"0" ] ; then
155 rule_in="dst-port $p"
156 rule_out="src-port $p"
160 rule_in="src-port $p"
161 rule_out="dst-port $p"
165 rule_in="{ src-port $p or dst-port $p }"
166 rule_out="{ src-port $p or dst-port $p }"
170 abort "invalid service type $type"
174 rule_in="pipe ${ipfw_pipe_in} in ${h_in} ${rule_in} // $type $arg $slice_id"
175 rule_out="pipe ${ipfw_pipe_out} out ${h_out} ${rule_out} // $type $arg $slice_id"
177 # Move into the user root directory. The profile should be located there
178 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} add ${ipfw_rule} ${rule_in} ) > /dev/null || \
179 abort "ipfw syntax error ${rule_in}"
180 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} add ${ipfw_rule} ${rule_out} ) > /dev/null || \
181 abort "ipfw syntax error ${rule_out}"
184 # check error reporting
185 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} pipe ${ipfw_pipe_in} config ${CONFIG_PIPE_IN} ) > /dev/null || \
186 abort "ipfw syntax error pipe_in"
187 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} pipe ${ipfw_pipe_out} config ${CONFIG_PIPE_OUT} ) > /dev/null || \
188 abort "ipfw syntax error pipe_out"
190 # all good, delete and add rules if necessary
191 [ "$del" = "service" ] && do_delete 0 $slice_id service $arg
192 [ "$del" = "cli_ser" ] && do_delete 0 $slice_id client $arg
193 [ "$del" = "cli_ser" ] && do_delete 0 $slice_id server $arg
194 [ "$new_rule" != "0" ] && ${IPFW} add ${ipfw_rule} $rule_in > /dev/null
195 [ "$new_rule" != "0" ] && ${IPFW} add ${ipfw_rule} $rule_out > /dev/null
197 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW} pipe ${ipfw_pipe_in} config ${CONFIG_PIPE_IN} )
198 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW} pipe ${ipfw_pipe_out} config ${CONFIG_PIPE_OUT} )
200 # send output to the user
201 ${IPFW} show ${ipfw_rule}
202 ${IPFW} pipe ${ipfw_pipe_in} show
203 ${IPFW} pipe ${ipfw_pipe_out} show
205 # do not write on the database on test-only
206 [ "$TEST" = "1" ] && return
207 # add to the database
208 ( grep -iv -- "^${slice_id} ${type} ${arg} " $DBFILE; \
209 echo "${slice_id} ${type} ${arg} ${ipfw_rule} ${pipe_index} ${timeout}" ) > ${DBFILE}.tmp
210 mv ${DBFILE}.tmp ${DBFILE}
214 # Delete a given configuration
215 # if block_deletion !0 free block resources (if necessary)
216 # otherwise leave the block allocated in case
217 # we are adding the first rule
218 do_delete() { # block_deletion slice_id type arg
219 local ipfw_pipe_in ipfw_pipe_out pipe_index ipfw_rule
220 local block_deletion=$1 slice_id=$2 type=$3 arg=$4
222 [ "${type}" = "BLOCK" ] && abort "A BLOCK can not be deleted"
223 [ "${arg}" = "" ] && abort "Missing args on 'delete', expected on of {CLIENT|SERVER|SERVICE} arg"
224 set `find_rule $slice_id $type $arg`
225 ipfw_rule=$1; pipe_index=$2
226 [ "$ipfw_rule" = "0" ] && return # no rules found
228 # find actual pipe numbers XXX do as function
229 ipfw_pipe_in=$(($IPFW_PIPE_MIN + $((2 * $(($pipe_index - 1)))) ))
230 ipfw_pipe_out=$(($ipfw_pipe_in + 1))
232 echo "removing configuration ${slice_id} ${type} ${arg}"
233 [ "$TEST" = "1" ] && return 0
234 $IPFW delete ${ipfw_rule}
235 $IPFW pipe delete ${ipfw_pipe_in}
236 $IPFW pipe delete ${ipfw_pipe_out}
237 # remove from the database (case insensitive)
238 grep -iv -- "^${slice_id} ${type} ${arg} " $DBFILE > ${DBFILE}.tmp
239 mv ${DBFILE}.tmp ${DBFILE}
241 # if there are no more rules for the user
242 # remove the table entry from ipfw and from the db
243 [ $block_deletion = 0 ] && return 0
245 local rule_counter=`grep ^${slice_id} ${DBFILE} | wc -l`
246 [ $rule_counter -gt 1 ] && return 0 # there are still user rules
247 # delete the block and clean the table
248 local block_n=`grep "^${slice_id} BLOCK" ${DBFILE} | cut -d " " -f 3`
249 debug "Deleting BLOCK <${block_n}> entry from ipfw and from the database"
250 table_remove $slice_id $block_n
253 # compare the argument with the first two field of
255 # On match returns the block number, otherwise returns 0.
257 find_block() { # $1 slice_id
259 ret=`grep -- "^$1 BLOCK " $DBFILE`
261 [ x"$ret" = x ] && echo "0" && return # nothing found
262 # ignore multiple matches. If the db is corrupt we are
269 # remove the default user rule and
270 # the a BLOCK entry from ipfw and update the db
272 table_remove() { # $slice_id $block_n
273 [ "$TEST" = "1" ] && return 0
275 # compute and delete the last user rule
276 local ipfw_rulemax=$(($IPFW_RULE_MIN + $(($M *${block_n})) -1))
277 ${IPFW} table $SLICE_TABLE delete $slice_id
278 ${IPFW} delete ${ipfw_rulemax}
279 ( grep -iv -- "^${slice_id} BLOCK ${block_n}" $DBFILE; ) > ${DBFILE}.tmp
280 mv ${DBFILE}.tmp ${DBFILE}
285 # Find a rule and pipe_index for the given key (xid type arg)
286 # Allocate a new block if first entry for this xid.
287 # Rule and pipe are not written into the database, only the block is.
289 # Return ipfw_rule pipe_index new_rule
290 # 'new_rule' is 0 if the rule existed, 1 if it is new
292 # return ipfw_rule = 0 if there are no resources available
293 find_allocate() { # slice_id type arg
294 local slice_id=$1 type=$2 arg=$3
295 local ipfw_rule pipe_index new_block=0
297 # search for already allocated rule and pipes
298 set `find_rule $slice_id $type $arg`
299 ipfw_rule=$1; pipe_index=$2
300 [ ! ${ipfw_rule} = 0 ] && echo $ipfw_rule $pipe_index "0" && return 0 # rules found, return
302 # no rules found, search for an already existing block, or
304 local block_n=`find_block ${slice_id}`
305 [ ${block_n} = "0" ] && new_block=1 && block_n=`find_free_block`
306 [ ${block_n} = "0" -o ${block_n} -gt $BLOCK_MAX ] && echo 0 && return 0;
308 # We have a valid block, compute the range for user rules
309 local ipfw_rulemin=$(($IPFW_RULE_MIN + $(($M *$(($block_n - 1))))))
310 local ipfw_rulemax=$(($(($ipfw_rulemin + $M)) - 1 ))
312 # Find rule and pipes, reserve the last rule for the user's
313 # default rule that catches regular traffic.
314 set `allocate_resources $ipfw_rulemin $(($ipfw_rulemax - 1))`
315 ipfw_rule=$1; pipe_index=$2
316 [ $ipfw_rule = 0 ] && echo 0 && return 0 # no resources
318 # If this is a new block, add the slice to the lookup table
319 # and put a default rule at the end of the block.
320 if [ "$TEST" = "0" -a $new_block = 1 ] ; then
321 ${IPFW} table $SLICE_TABLE add ${slice_id} ${ipfw_rulemin} > /dev/null
322 ${IPFW} add ${ipfw_rulemax} allow all from any to any > /dev/null
323 ( echo "${slice_id} BLOCK ${block_n}" ) >> ${DBFILE}
326 echo $ipfw_rule $pipe_index "1"
331 # called with the database file as input
332 # compare the tuple <slice_id type arg> with
333 # the current firewall configuration. The database contains
334 # slice_id type arg ipfw_rule pipe_index timeout
335 # On match returns <ipfw_rule pipe_index timeout>
336 # On non match returns 0 0 0
338 find_rule() { # slice_id type arg
340 ret=`grep -i -- "^$1 $2 $3 " $DBFILE | grep -v BLOCK`
342 [ x"$ret" = x ] && echo "0 0 0 " && return # nothing found
343 # ignore multiple matches. If the db is corrupt we are
350 # Find a hole in a list of numbers within a range (boundaries included)
351 # The input is passed as a sorted list of numbers on stdin.
352 # Return a "0" rule if there is no rule free
353 find_hole() { # min max
354 local min=$1 cand=$1 max=$2 line
356 [ $line -lt $min ] && continue
357 [ $line -ne $cand ] && break # found
358 [ $cand -ge $max ] && cand=0 && break # no space
364 # XXX despite the name this does not allocate but only finds holes.
365 # returns a free rule and pipe base for client|server|service
367 # Returns r=0 if there are no resources available
369 allocate_resources() { # ipfw_minrule ipfw_maxrule
371 # remove comments, extract field, sort
372 p=`grep -v '^#' $DBFILE | grep -v BLOCK | awk '{print $5}' | sort -n | \
373 find_hole $PIPE_MIN $PIPE_MAX`
374 r=`grep -v '^#' $DBFILE | grep -v BLOCK | awk '{print $4}' | sort -n | \
376 [ $r = 0 -o $p = 0 ] && r=0 # no resources available
381 # Returns the index of a free block
382 # Returns 0 if there are no resources available
385 b=`grep -v '^#' $DBFILE | grep BLOCK | awk '{print $3}' | sort -n | \
386 find_hole $BLOCK_MIN $BLOCK_MAX`
390 # parse the ipfw database and remove expired rules
392 # Each timeout value stored in the database is compared against
393 # the current time. If the timeout is older than current,
394 # the rules and related pipes will be deleted.
395 kill_expired() { # slice_id type arg
398 # if there is no database file exit
399 [ ! -f ${DBFILE} ] && return 0
401 # Get the current time
404 cp ${DBFILE} ${DBFILE}.kill
405 cat ${DBFILE}.kill | grep -v BLOCK |
407 match=`echo $line|cut -d " " -f 1-3`
408 timeout=`echo $line|cut -d " " -f 6`
409 [ $now -gt $timeout ] && do_delete 1 $match
414 # execute functions from root context
415 # can be used from root context as follow:
416 # echo "super $command $args" | /vsys/ipfw-be 0
417 do_super() { # $arguments...
426 kill_expired; return 0
429 abort "Invalid super command"
434 # refresh the rule timeout
435 do_refresh() { # slice_id type arg timeout
436 local ipfw_pipe_in ipfw_pipe_out pipe_index
437 local slice_id=$1 type=$2 arg=$3 timeout=$4
439 debug "do_refresh type: <$type> arg: <$arg> timeout: <$timeout>"
440 [ "${type}" = "BLOCK" ] && abort "BLOCK rule not valid"
441 [ "${timeout}" = "" ] && abort "Missing args on 'refresh', expected on of {SERVICE|SERVER|CLIENT} port_number"
442 set `find_rule $slice_id $type $arg`
443 ipfw_rule=$1; pipe_index=$2
444 [ "${ipfw_rule}" = "0" ] && debug "no rules found" && return 0 # no rules found
446 [ "$TEST" = "1" ] && return
447 # update the database with the new timeout value
448 ( grep -iv -- "^${slice_id} ${type} ${arg} " $DBFILE; \
449 echo "${slice_id} ${type} ${arg} ${ipfw_rule} ${pipe_index} ${timeout}" ) > ${DBFILE}.tmp
450 mv ${DBFILE}.tmp ${DBFILE}
451 echo "refreshed timeout for rule ${type} ${arg}"
455 # A request is made by a set of arguments formatted as follow:
457 # config {server|client|service} arg [-t timeout] IN <pipe_conf> OUT <pipe_conf>
458 # show {rules|pipes} [args]
460 # refresh type arg [-t timeout]
462 # The timeout value is expressed as:
463 # week, day, month or anything else accepted by the date command.
464 # The id of the slice issuing the request is in the $SLICE_ID variable,
465 # set at the beginning of this script.
468 local timeout TMP i rule_base pipe_base
470 local debug_args="$*";
471 local type=$1 ; shift
473 debug "Received command: <$cmd> arguments: <$debug_args>"
475 # set the timeout value
476 # if present, extract the '-t timeout' substring from the command line
477 timeout=`echo ${args} | ${SED} ${SEDOPT} 's/(.+)( -t [a-zA-Z0-9]+ )(.*)/\2/'`
478 # if the '-t timeout' is specified, use the timeout provided by the user
479 if [ "${timeout}" != "${args}" ] ; then # match
480 # remove the '-t ' option
481 timeout=`echo ${timeout} | ${SED} ${SEDOPT} 's/-t //'`
482 timeout=`check_timeout ${timeout}`
483 [ $timeout = 0 ] && abort "Date format $1 not valid"
484 # clean the arguments
485 args=`echo ${args} | ${SED} ${SEDOPT} 's/(.+)( -t [a-zA-Z0-9]+ )(.*)/\1 \3/'`
487 # use the default value, no need to check for correctness, no need to clean arguments
488 timeout=`date --date="1day" +%s` # default to 1 day
491 # if the table rule is not present, add it
492 local table_rule=`${IPFW} show $S | grep "skipto tablearg" | grep "lookup jail $SLICE_TABLE"`
493 [ -z "$table_rule" ] && ipfw_init
495 debug "Timeout $timeout"
496 # Handle special requests: show and delete
500 xserver|xSERVER|xclient|xCLIENT|xservice|xSERVICE)
501 do_config $SLICE_ID $timeout $type $args && return 0
504 abort "'config' should be followed by {CLIENT|SERVER|SERVICE}"
507 do_delete 1 $SLICE_ID $type $args
510 do_refresh $SLICE_ID $type $args $timeout && return 0
513 # XXX filter out sliver rules
514 [ "$type" = "rules" ] && ${IPFW} show && return 0
515 [ "$type" = "pipes" ] && ${IPFW} pipe show && return 0
516 abort "'show' should be followed by {rules|pipes}"
519 [ $SLICE_ID = 0 ] && do_super $type $args && return 0
520 abort "no permission for ipfw-be super execution"
526 # help XXX to be done
527 abort "'command' should be one of {show|config|delete|refresh|release}"
532 # validate the timeout
533 check_timeout() { # timeout
534 local tt=`date --date="${1}" +%s`
535 [ "$?" != "0" ] && echo 0 && return
539 do_config() { # slice_id timeout type arg IN pipe_conf OUT pipe_conf
540 local slice_id=$1; shift
541 local timeout=$1; shift
543 local arg=$1; shift # XXX addr not yet implemented
544 local p h; # port and optional hostname
546 [ "$1" != "IN" ] && abort "Missing addr:port, or IN requested"
549 # read pipe in configuration
551 while [ "$1" != "" -a "$1" != "OUT" ] ; do
555 CONFIG_PIPE_IN="$i" # XXX local ?
556 [ "$CONFIG_PIPE_IN" = "" ] && abort "Missing pipe in configuration"
558 [ "$1" != "OUT" ] && abort "Missing pipe in configuration, or missing OUT"
561 # read pipe out configuration
563 while [ "$1" != "" ] ; do
567 CONFIG_PIPE_OUT="$i" # XXX local ?
568 [ "$CONFIG_PIPE_OUT" = "" ] && abort "Missing pipe out configuration"
571 # process the argument (port and hostname are separated by a @)
572 # split the argument, and prepare the remote host configuration string
573 p=`echo $arg | cut -s -d "@" -f1-` # empty it there is no separator
574 if [ "$p" = "" ] ; then
577 p=`echo $arg | cut -d "@" -f1`
578 h=`echo $arg | cut -d "@" -f2`
581 # A port value is mandatory
582 [ "$p" = "" ] && abort "A port value is mandatory."
584 # SERVICE do not support remote hostname filtering
585 [ $type = "service" ] && [ "$h" != "" ] && \
586 abort "The service configuration do not support filtering remote hostnames."
588 debug "Configuration Required:"
589 debug "slice_id: $SLICE_ID"
591 debug "full arg: $arg"
592 debug "mandatory port(s): $p optional hostname(s): $h"
593 debug "timeout: $timeout"
594 debug "IN: $CONFIG_PIPE_IN"
595 debug "OUT: $CONFIG_PIPE_OUT"
596 debug "-----------------------"
598 # check if the link is already configured
599 debug "Search for slice_id: ${slice_id} type: ${type} port: ${arg}"
601 set `find_allocate ${slice_id} ${type} ${arg}`
602 local ipfw_rule=$1 pipe_index=$2 new_rule=$3
604 [ ${ipfw_rule} = 0 ] && abort "No resources available"
605 debug "Found or allocated resources ipfw_rule: ${ipfw_rule} and pipe_index: ${pipe_index}"
607 add_rule $slice_id $new_rule $type $arg $ipfw_rule $pipe_index $timeout
608 hook_call $type $port $rule_base $pipe_base $timeout
609 return 0; # link configured, exit
613 # acquire the lock XXX check lockfile
615 [ "$TEST" = 1 ] && return
616 lockfile -s 0 -r 0 $lockfile 2> /dev/null
617 if [ $? -ne 0 ] ; then
618 echo "lock acquisition failed"
630 # initialize the firewall with PlanetLab default rules
634 ${IPFW} add $S skipto tablearg lookup jail $SLICE_TABLE
635 ${IPFW} add $D allow all from any to any
639 # if present, call a hook function
641 # slice_id type port rule_base pipe_base timeout
643 if [ -n "${HOOK}" -a -x "${HOOK}" ]; then
644 debug "Calling the hook function."
645 ${HOOK} ${SLICE_ID} "$*" &
652 ./neconfig {CLIENT|SERVER|SERVICE} arg [-t timeout] \
653 IN <pipe in configuration> OUT <pipe out configuration>
654 ./netconfig show {rules|pipes}
655 ./netconfig delete {CLIENT|SERVER|SERVICE} arg
656 ./netconfig refresh [-t timeout] {CLIENT|SERVER|SERVICE} arg
658 We support three modes of operation:
660 CLIENT programs on the node connect to remote ports
661 and/or addresses. Emulation intercepts traffic
662 involving those ports/addresses
664 SERVER programs on the node listen on specific ports.
665 Emulation intercepts traffic on those ports,
666 optionally limited to specific client addresses.
668 SERVICE the node runs both clients and servers,
669 we can only specify the ports on which emulation
672 'arg' has the form PORTLIST[@ADDRLIST], where ADDRLIST is
673 optional and only supported for CLIENT and SERVER modes.
674 PORTLIST and ADDRLIST can be specified as any valid port
675 or address specifier in ipfw, e.g.
676 - a single value 443 or 10.20.30.40/24
677 - a comma-separated list 1111,2222,3333 1.2.3.4,5.6.7.8
678 - a range 1111-2222 (only for ports)
679 Addresses can also be specified as symbolic hostnames, and
680 they are resolved when the rule is installed.
681 Note that they always indicate the remote endpoint.
683 On a given port a user can have one CLIENT and/or one SERVER
684 configuration or one SERVICE configuration.
685 When a SERVICE configuration is installed any existing CLIENT
686 and SERVER configuration on the same port are removed.
687 When a CLIENT or SERVER configuration is installed any existing
688 SERVICE configuration on the same port is removed.
690 The pipe's configuration, both for the upstream and downstream link,
691 follows the dummynet syntax. A quick and not exaustive example
692 of the parameters that can be used to configure the delay,
693 the bandwidth and the packet loss rate for a link follow:
695 IN|OUT delay 100ms bw 1Mbit/s plr 0.1
697 The profile file, if present, should be located into the sliver's
699 The full documentation is on the manpage[1].
701 The timeout value follow the linux 'date' command format[2]
702 and can be specified as follow:
708 [1] http://www.freebsd.org/cgi/man.cgi?query=ipfw
709 [2] http://linuxmanpages.com/man1/date.1.php
713 #--- DOCUMENTATION AND INTERNAL ARCHITECTURE ---
715 # When a user configures an emulated link, we need to allocate
716 # two pipes and one ipfw rule number to store the parameters.
717 # Reconfigurations of existing links reuse the previous resources.
718 # We keep track of all resources (pipes, rules and blocks of rules)
719 # in a database stored in a text file, see DATABASE FORMAT below.
721 # Pipes are allocated in pairs. In the database each pair is numbered
722 # from PIPE_MIN to PIPE_MAX. The actual pipe numbers for each pair are
724 # ipfw_pipein = IPFW_PIPE_MIN + 2*(pipe_index-1)
725 # ipfw_pipeout = ipfw_pipein + 1
727 # The rules number is allocated within a block of M consecutive rules
728 # for each slice. The block is allocated at the first configuration
729 # of an emulated link, and deallocated when the last link is removed.
730 # In the database, blocks are numbered from BLOCK_MIN to BLOCK_MAX,
731 # and the range of rules for a given block_index is
733 # ipfw_min_rule = RULE_BASE
734 # ipfw_max_rule = RULE_BASE + ((M-1)*block_index) -1
736 # All lookups, and the block allocation, are done in find_allocate().
737 # The rule_number and pipe_index are written in the database
738 # by add_rule() after checking the correctness of the request.
741 #--- RULESET STRUCTURE ---
742 # The ruleset is made of different sections, as follows:
743 # - an initial block of rules, reserved and configurable by
744 # the root context only;
745 # - a skipto rule (S), used to jump directly to the block
746 # associated with a given slice;
747 # - a second block of reserved rules, to catch remaining traffic.
748 # This ends with rule number D which is an 'accept all';
749 # - after D, we have a block of M rule numbers for each slice.
750 # Each of these blocks ends with an 'accept all' rule;
751 # - finally, rule 65535 is the firewall's default rule.
754 # 1...S-1 first block of reserved rules
755 # S skipto tablearg lookup jail 1
756 # S+1..D-1 ... second block of reserved rules
757 # D allow ip from any to any
759 # RULE_BASE <block of M entries for first user>
760 # RULE_BASE+M <block of M entry for second user ...>
763 #--- DATABASE FORMAT ---
764 # The database is stored in a text file, and contains one record per
765 # line with the following structure
767 # XID TYPE arg1 arg2 ...
769 # Whitespace separates the fields. arg1, arg2, ... have different
770 # meaning depending on the TYPE. XID is the slice ID.
772 # In the database we have the following records:
773 # - one entry of type BLOCK for each slice with configured links.
774 # This entry represents the block_index of the block of M ipfw
775 # rules allocated to the slice, as follows:
777 # XID BLOCK block_index
778 # (BLOCK_MIN <= block_index <= BLOCK_MAX)
780 # - one entry for each link (CLIENT, SERVER, SERVICE).
781 # The database entry for this info has the form
783 # XID {CLIENT|SERVER|SERVICE} arg ipfw_rule pipe_index timeout
785 # 'TYPE' reflects the configuration mode;
786 # 'arg' is PORTLIST@ADDRLIST and is used as a search key together
787 # with the XID and TYPE;
788 # 'ipfw_rule' is the unique ipfw rule number used for this
789 # emulated link. It must be within the block of M rule numbers
790 # allocated to the slice;
791 # 'pipe_index' is the index of the pair of pipes used for the
795 debug "--- $0 START for $SLICENAME ---"
797 # If the db does not exist, create it and clean rules and pipes
798 [ ! -e ${DBFILE} ] && clean_db
800 # A request to the vsys backend is composed by a single line of input
801 read REQ # read one line, ignore the rest
802 set_verbose ${REQ} # use inital -v if present
803 set_test ${REQ} # use inital -t if present
804 REQ="`filter ${REQ}`" # remove -v and -q and invalid chars
805 debug "--- processing <${REQ}>"
806 acquire_lock # critical section
809 debug "--- $0 END ---"