3 # Marta Carbone, Luigi Rizzo
4 # Copyright (C) 2009 Universita` di Pisa
7 # This script the vsys backend used to configure emulation.
9 # - reads the user's input from the vsys input pipe
10 # - validates the input
11 # - configures the firewall
12 # - writes results on the output vsys pipe
14 # Configurable variables are at the beginning (only HOOK so far)
16 # If HOOK is set, ${HOOK} is called before configuring a rule.
17 # A sample hook can be found in the ipfwroot.rpm package,
18 # it can be used to collect statistical information on dummynet usage.
19 # To configure a hook, set the HOOK variable as follow:
20 # HOOK=/tmp/sample_hook
22 #--- You should not touch anything below this line. ----
23 # For documentation see ARCHITECTURE near the end of the file.
25 #--- global variables ---
26 VERBOSE=0 # set to !0 to enable debug messages
27 TEST=0 # set to 1 for test mode
29 # The database and the lock file
31 lockfile=/var/lock/ipfw.lock
33 # Min and max value (inclusive) for block_index
36 M=50 # size of per-slice block of rules
37 # Min and max value (inclusive) for pipe_index
41 # These are the actual rule numbers used in ipfw
42 IPFW_RULE_MIN=10000 # initial per-slice rule number
43 IPFW_PIPE_MIN=10000 # initial pipe number
45 # The skipto and the generic default rule
46 # these values are used to initialize the firewall
47 SLICE_TABLE=1 # table number used for slice ids lookup
48 S=1000 # firewall rule number for the skipto rule
49 D=2000 # default rule for reserved section
51 # set slicename and slice_id
52 # these are the credential of the user invoking the backend
54 SLICE_ID=`id -u $SLICENAME`
55 [ x"$SLICE_ID" = x"" ] && echo "No sliver present." && exit
58 # XXX check consistency for variables {}
61 [ -x ${SED} ] || { SED=`which sed` ; SEDOPT=-E ; }
63 IPFW_CHECK="/sbin/ipfw -n"
65 debug() { # $1 message to be displayed
66 [ x"${VERBOSE}" != x"0" ] && echo "ipfw-be: $1"
69 # if the first argument is -v, enable verbose mode
71 [ x"$1" = x"-v" -o x"$2" = x"-v" ] && VERBOSE=1
74 # set test mode if -q is found
76 [ x"$1" = x"-q" -o x"$2" = x"-q" ] || return
78 IPFW="/bin/echo ipfw:"
79 IPFW_CHECK="/bin/echo ipfw -n:"
82 abort() { # $1 message to be displayed in case of error
84 echo "ipfw-be aborting (netconfig help): $1"
88 # remove dangerous characters from user input
89 # if present, the leading '-v/-q' will be removed
90 filter() { # $* variables to be filtered
91 [ x${1} = x"-v" -o x${1} = x"-q" ] && shift
92 [ x${1} = x"-v" -o x${1} = x"-q" ] && shift
93 # allowed chars are: numbers, uppercase and lowercase letters,
94 # spaces, and the following symbols: .,_-/
95 echo "$*" | ${SED} ${SEDOPT} 's/[^\t0-9a-zA-Z., _\/\{}@-]*//g'
98 # remove all entries from the ipfw config, and create an empty db
102 # we would like to delete ranges of rules and pipes but this
103 # is not supported so for the time being we kill them all
105 ${IPFW} -q pipe flush
106 ${IPFW} -q table $SLICE_TABLE flush
107 #${IPFW} delete ${IPFW_RULE_MIN}-${IPFW_RULE_MAX}
108 #${IPFW} pipe delete ${IPFW_PIPE_MIN}-${IPFW_PIPE_MAX}
109 # since all rules are now deleted, we should initialize the firewall
114 # Add the ipfw rule/pipe and update the database.
115 # The pipe-in and pipe-out config are through global variables
116 # rule_in rule_out because they may be long. XXX why ?
117 # Other arguments are on the command line
119 # the new_rule variable is set if the rule to be installed is new
120 # we need to know this because we do not want to clean
121 # rule counters on pipes reconfiguration
122 add_rule() { # slice_id new_rule type arg ipfw_rule pipe_index timeout
123 local slice_id=$1 new_rule=$2 type=$3 arg=$4
124 local ipfw_rule=$5 pipe_index=$6 timeout=$7
125 local ipfw_pipe_in ipfw_pipe_out check_timeout
126 local p h # used to split the argument
129 # local rule_in rule_out # XXX test if this works
130 # find actual pipe numbers
131 ipfw_pipe_in=$(($IPFW_PIPE_MIN + $((2 * $(($pipe_index - 1)))) ))
132 ipfw_pipe_out=$(($ipfw_pipe_in + 1))
133 local del # used to delete incompatible configurations
135 # split the argument, and prepare PORTLIST (p) and ADDRLIST (h)
136 p=`echo $arg | cut -s -d "@" -f1-` # empty if no separator
137 if [ "$p" = "" ] ; then
140 p=`echo $arg | cut -d "@" -f1`
141 h=`echo $arg | cut -d "@" -f2`
144 if [ "$h" = "" ] ; then
149 h_out=" dst-ip ${h} "
152 # first, call ipfw -n to check syntax, if ok move on and do the action
153 if [ x"$new_rule" != x"0" ] ; then
156 rule_in="dst-port $p"
157 rule_out="src-port $p"
161 rule_in="src-port $p"
162 rule_out="dst-port $p"
166 rule_in="{ src-port $p or dst-port $p }"
167 rule_out="{ src-port $p or dst-port $p }"
171 abort "invalid service type $type"
175 rule_in="pipe ${ipfw_pipe_in} in ${h_in} ${rule_in} // $type $arg $slice_id"
176 rule_out="pipe ${ipfw_pipe_out} out ${h_out} ${rule_out} // $type $arg $slice_id"
178 # Move into the user root directory. The profile should be located there
179 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} add ${ipfw_rule} ${rule_in} ) > /dev/null || \
180 abort "ipfw syntax error ${rule_in}"
181 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} add ${ipfw_rule} ${rule_out} ) > /dev/null || \
182 abort "ipfw syntax error ${rule_out}"
185 # check error reporting
186 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} pipe ${ipfw_pipe_in} config ${CONFIG_PIPE_IN} ) > /dev/null || \
187 abort "ipfw syntax error pipe_in"
188 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} pipe ${ipfw_pipe_out} config ${CONFIG_PIPE_OUT} ) > /dev/null || \
189 abort "ipfw syntax error pipe_out"
191 # all good, delete and add rules if necessary
192 [ "$del" = "service" ] && do_delete 0 $slice_id service $arg
193 [ "$del" = "cli_ser" ] && do_delete 0 $slice_id client $arg
194 [ "$del" = "cli_ser" ] && do_delete 0 $slice_id server $arg
195 [ "$new_rule" != "0" ] && ${IPFW} add ${ipfw_rule} $rule_in > /dev/null
196 [ "$new_rule" != "0" ] && ${IPFW} add ${ipfw_rule} $rule_out > /dev/null
198 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW} pipe ${ipfw_pipe_in} config ${CONFIG_PIPE_IN} )
199 ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW} pipe ${ipfw_pipe_out} config ${CONFIG_PIPE_OUT} )
201 # send output to the user
202 ${IPFW} show ${ipfw_rule}
203 ${IPFW} pipe ${ipfw_pipe_in} show
204 ${IPFW} pipe ${ipfw_pipe_out} show
206 # do not write on the database on test-only
207 [ "$TEST" = "1" ] && return
208 # add to the database
209 ( grep -iv -- "^${slice_id} ${type} ${arg} " $DBFILE; \
210 echo "${slice_id} ${type} ${arg} ${ipfw_rule} ${pipe_index} ${timeout}" ) > ${DBFILE}.tmp
211 mv ${DBFILE}.tmp ${DBFILE}
215 # Delete a given configuration
216 # if block_deletion !0 free block resources (if necessary)
217 # otherwise leave the block allocated in case
218 # we are adding the first rule
219 do_delete() { # block_deletion slice_id type arg
220 local ipfw_pipe_in ipfw_pipe_out pipe_index ipfw_rule
221 local block_deletion=$1 slice_id=$2 type=$3 arg=$4
223 [ "${type}" = "BLOCK" ] && abort "A BLOCK can not be deleted"
224 [ "${arg}" = "" ] && abort "Missing args on 'delete', expected on of {CLIENT|SERVER|SERVICE} arg"
225 set `find_rule $slice_id $type $arg`
226 ipfw_rule=$1; pipe_index=$2
227 [ "$ipfw_rule" = "0" ] && return # no rules found
229 # find actual pipe numbers XXX do as function
230 ipfw_pipe_in=$(($IPFW_PIPE_MIN + $((2 * $(($pipe_index - 1)))) ))
231 ipfw_pipe_out=$(($ipfw_pipe_in + 1))
233 echo "removing configuration ${slice_id} ${type} ${arg}"
234 [ "$TEST" = "1" ] && return 0
235 $IPFW delete ${ipfw_rule}
236 $IPFW pipe delete ${ipfw_pipe_in}
237 $IPFW pipe delete ${ipfw_pipe_out}
238 # remove from the database (case insensitive)
239 grep -iv -- "^${slice_id} ${type} ${arg} " $DBFILE > ${DBFILE}.tmp
240 mv ${DBFILE}.tmp ${DBFILE}
242 # if there are no more rules for the user
243 # remove the table entry from ipfw and from the db
244 [ $block_deletion = 0 ] && return 0
246 local rule_counter=`grep ^${slice_id} ${DBFILE} | wc -l`
247 [ $rule_counter -gt 1 ] && return 0 # there are still user rules
248 # delete the block and clean the table
249 local block_n=`grep "^${slice_id} BLOCK" ${DBFILE} | cut -d " " -f 3`
250 debug "Deleting BLOCK <${block_n}> entry from ipfw and from the database"
251 table_remove $slice_id $block_n
254 # compare the argument with the first two field of
256 # On match returns the block number, otherwise returns 0.
258 find_block() { # $1 slice_id
260 ret=`grep -- "^$1 BLOCK " $DBFILE`
262 [ x"$ret" = x ] && echo "0" && return # nothing found
263 # ignore multiple matches. If the db is corrupt we are
270 # remove the default user rule and
271 # the a BLOCK entry from ipfw and update the db
273 table_remove() { # $slice_id $block_n
274 [ "$TEST" = "1" ] && return 0
276 # compute and delete the last user rule
277 local ipfw_rulemax=$(($IPFW_RULE_MIN + $(($M *${block_n})) -1))
278 ${IPFW} table $SLICE_TABLE delete $slice_id
279 ${IPFW} delete ${ipfw_rulemax}
280 ( grep -iv -- "^${slice_id} BLOCK ${block_n}" $DBFILE; ) > ${DBFILE}.tmp
281 mv ${DBFILE}.tmp ${DBFILE}
286 # Find a rule and pipe_index for the given key (xid type arg)
287 # Allocate a new block if first entry for this xid.
288 # Rule and pipe are not written into the database, only the block is.
290 # Return ipfw_rule pipe_index new_rule
291 # 'new_rule' is 0 if the rule existed, 1 if it is new
293 # return ipfw_rule = 0 if there are no resources available
294 find_allocate() { # slice_id type arg
295 local slice_id=$1 type=$2 arg=$3
296 local ipfw_rule pipe_index new_block=0
298 # search for already allocated rule and pipes
299 set `find_rule $slice_id $type $arg`
300 ipfw_rule=$1; pipe_index=$2
301 [ ! ${ipfw_rule} = 0 ] && echo $ipfw_rule $pipe_index "0" && return 0 # rules found, return
303 # no rules found, search for an already existing block, or
305 local block_n=`find_block ${slice_id}`
306 [ ${block_n} = "0" ] && new_block=1 && block_n=`find_free_block`
307 [ ${block_n} = "0" -o ${block_n} -gt $BLOCK_MAX ] && echo 0 && return 0;
309 # We have a valid block, compute the range for user rules
310 local ipfw_rulemin=$(($IPFW_RULE_MIN + $(($M *$(($block_n - 1))))))
311 local ipfw_rulemax=$(($(($ipfw_rulemin + $M)) - 1 ))
313 # Find rule and pipes, reserve the last rule for the user's
314 # default rule that catches regular traffic.
315 set `allocate_resources $ipfw_rulemin $(($ipfw_rulemax - 1))`
316 ipfw_rule=$1; pipe_index=$2
317 [ $ipfw_rule = 0 ] && echo 0 && return 0 # no resources
319 # If this is a new block, add the slice to the lookup table
320 # and put a default rule at the end of the block.
321 if [ "$TEST" = "0" -a $new_block = 1 ] ; then
322 ${IPFW} table $SLICE_TABLE add ${slice_id} ${ipfw_rulemin} > /dev/null
323 ${IPFW} add ${ipfw_rulemax} allow all from any to any > /dev/null
324 ( echo "${slice_id} BLOCK ${block_n}" ) >> ${DBFILE}
327 echo $ipfw_rule $pipe_index "1"
332 # called with the database file as input
333 # compare the tuple <slice_id type arg> with
334 # the current firewall configuration. The database contains
335 # slice_id type arg ipfw_rule pipe_index timeout
336 # On match returns <ipfw_rule pipe_index timeout>
337 # On non match returns 0 0 0
339 find_rule() { # slice_id type arg
341 ret=`grep -i -- "^$1 $2 $3 " $DBFILE | grep -v BLOCK`
343 [ x"$ret" = x ] && echo "0 0 0 " && return # nothing found
344 # ignore multiple matches. If the db is corrupt we are
351 # Find a hole in a list of numbers within a range (boundaries included)
352 # The input is passed as a sorted list of numbers on stdin.
353 # Return a "0" rule if there is no rule free
354 find_hole() { # min max
355 local min=$1 cand=$1 max=$2 line
357 [ $line -lt $min ] && continue
358 [ $line -ne $cand ] && break # found
359 [ $cand -ge $max ] && cand=0 && break # no space
365 # XXX despite the name this does not allocate but only finds holes.
366 # returns a free rule and pipe base for client|server|service
368 # Returns r=0 if there are no resources available
370 allocate_resources() { # ipfw_minrule ipfw_maxrule
372 # remove comments, extract field, sort
373 p=`grep -v '^#' $DBFILE | grep -v BLOCK | awk '{print $5}' | sort -n | \
374 find_hole $PIPE_MIN $PIPE_MAX`
375 r=`grep -v '^#' $DBFILE | grep -v BLOCK | awk '{print $4}' | sort -n | \
377 [ $r = 0 -o $p = 0 ] && r=0 # no resources available
382 # Returns the index of a free block
383 # Returns 0 if there are no resources available
386 b=`grep -v '^#' $DBFILE | grep BLOCK | awk '{print $3}' | sort -n | \
387 find_hole $BLOCK_MIN $BLOCK_MAX`
391 # parse the ipfw database and remove expired rules
393 # Each timeout value stored in the database is compared against
394 # the current time. If the timeout is older than current,
395 # the rules and related pipes will be deleted.
396 kill_expired() { # slice_id type arg
399 # if there is no database file exit
400 [ ! -f ${DBFILE} ] && return 0
402 # Get the current time
405 cp ${DBFILE} ${DBFILE}.kill
406 cat ${DBFILE}.kill | grep -v BLOCK |
408 match=`echo $line|cut -d " " -f 1-3`
409 timeout=`echo $line|cut -d " " -f 6`
410 [ $now -gt $timeout ] && do_delete 1 $match
415 # execute functions from root context
416 # can be used from root context as follow:
417 # echo "super $command $args" | /vsys/ipfw-be 0
418 do_super() { # $arguments...
427 kill_expired; return 0
430 abort "Invalid super command"
435 # refresh the rule timeout
436 do_refresh() { # slice_id type arg timeout
437 local ipfw_pipe_in ipfw_pipe_out pipe_index
438 local slice_id=$1 type=$2 arg=$3 timeout=$4
440 debug "do_refresh type: <$type> arg: <$arg> timeout: <$timeout>"
441 [ "${type}" = "BLOCK" ] && abort "BLOCK rule not valid"
442 [ "${timeout}" = "" ] && abort "Missing args on 'refresh', expected on of {SERVICE|SERVER|CLIENT} port_number"
443 set `find_rule $slice_id $type $arg`
444 ipfw_rule=$1; pipe_index=$2
445 [ "${ipfw_rule}" = "0" ] && debug "no rules found" && return 0 # no rules found
447 [ "$TEST" = "1" ] && return
448 # update the database with the new timeout value
449 ( grep -iv -- "^${slice_id} ${type} ${arg} " $DBFILE; \
450 echo "${slice_id} ${type} ${arg} ${ipfw_rule} ${pipe_index} ${timeout}" ) > ${DBFILE}.tmp
451 mv ${DBFILE}.tmp ${DBFILE}
452 echo "refreshed timeout for rule ${type} ${arg}"
456 # A request is made by a set of arguments formatted as follow:
458 # config {server|client|service} arg [-t timeout] IN <pipe_conf> OUT <pipe_conf>
459 # show {rules|pipes} [args]
461 # refresh type arg [-t timeout]
463 # The timeout value is expressed as:
464 # week, day, month or anything else accepted by the date command.
465 # The id of the slice issuing the request is in the $SLICE_ID variable,
466 # set at the beginning of this script.
469 local timeout TMP i rule_base pipe_base
471 local debug_args="$*";
472 local type=$1 ; shift
474 debug "Received command: <$cmd> arguments: <$debug_args>"
476 # set the timeout value
477 # if present, extract the '-t timeout' substring from the command line
478 timeout=`echo ${args} | ${SED} ${SEDOPT} 's/(.+)( -t [a-zA-Z0-9]+ )(.*)/\2/'`
479 # if the '-t timeout' is specified, use the timeout provided by the user
480 if [ "${timeout}" != "${args}" ] ; then # match
481 # remove the '-t ' option
482 timeout=`echo ${timeout} | ${SED} ${SEDOPT} 's/-t //'`
483 timeout=`check_timeout ${timeout}`
484 [ $timeout = 0 ] && abort "Date format $1 not valid"
485 # clean the arguments
486 args=`echo ${args} | ${SED} ${SEDOPT} 's/(.+)( -t [a-zA-Z0-9]+ )(.*)/\1 \3/'`
488 # use the default value, no need to check for correctness, no need to clean arguments
489 timeout=`date --date="1day" +%s` # default to 1 day
492 # if the table rule is not present, add it
493 local table_rule=`${IPFW} show $S | grep "skipto tablearg" | grep "lookup jail $SLICE_TABLE"`
494 [ -z "$table_rule" ] && ipfw_init
496 debug "Timeout $timeout"
497 # Handle special requests: show and delete
501 xserver|xSERVER|xclient|xCLIENT|xservice|xSERVICE)
502 do_config $SLICE_ID $timeout $type $args && return 0
505 abort "'config' should be followed by {CLIENT|SERVER|SERVICE}"
508 do_delete 1 $SLICE_ID $type $args
511 do_refresh $SLICE_ID $type $args $timeout && return 0
514 # XXX filter out sliver rules
515 [ "$type" = "rules" ] && ${IPFW} show && return 0
516 [ "$type" = "pipes" ] && ${IPFW} pipe show && return 0
517 abort "'show' should be followed by {rules|pipes}"
520 [ $SLICE_ID = 0 ] && do_super $type $args && return 0
521 abort "no permission for ipfw-be super execution"
527 # help XXX to be done
528 abort "'command' should be one of {show|config|delete|refresh|release}"
533 # validate the timeout
534 check_timeout() { # timeout
535 local tt=`date --date="${1}" +%s`
536 [ "$?" != "0" ] && echo 0 && return
540 do_config() { # slice_id timeout type arg IN pipe_conf OUT pipe_conf
541 local slice_id=$1; shift
542 local timeout=$1; shift
544 local arg=$1; shift # XXX addr not yet implemented
545 local p h; # port and optional hostname
547 [ "$1" != "IN" ] && abort "Missing addr:port, or IN requested"
550 # read pipe in configuration
552 while [ "$1" != "" -a "$1" != "OUT" ] ; do
556 CONFIG_PIPE_IN="$i" # XXX local ?
557 [ "$CONFIG_PIPE_IN" = "" ] && abort "Missing pipe in configuration"
559 [ "$1" != "OUT" ] && abort "Missing pipe in configuration, or missing OUT"
562 # read pipe out configuration
564 while [ "$1" != "" ] ; do
568 CONFIG_PIPE_OUT="$i" # XXX local ?
569 [ "$CONFIG_PIPE_OUT" = "" ] && abort "Missing pipe out configuration"
572 # process the argument (port and hostname are separated by a @)
573 # split the argument, and prepare the remote host configuration string
574 p=`echo $arg | cut -s -d "@" -f1-` # empty it there is no separator
575 if [ "$p" = "" ] ; then
578 p=`echo $arg | cut -d "@" -f1`
579 h=`echo $arg | cut -d "@" -f2`
582 # A port value is mandatory
583 [ "$p" = "" ] && abort "A port value is mandatory."
585 # SERVICE do not support remote hostname filtering
586 [ $type = "service" ] && [ "$h" != "" ] && \
587 abort "The service configuration do not support filtering remote hostnames."
589 debug "Configuration Required:"
590 debug "slice_id: $SLICE_ID"
592 debug "full arg: $arg"
593 debug "mandatory port(s): $p optional hostname(s): $h"
594 debug "timeout: $timeout"
595 debug "IN: $CONFIG_PIPE_IN"
596 debug "OUT: $CONFIG_PIPE_OUT"
597 debug "-----------------------"
599 # check if the link is already configured
600 debug "Search for slice_id: ${slice_id} type: ${type} port: ${arg}"
602 set `find_allocate ${slice_id} ${type} ${arg}`
603 local ipfw_rule=$1 pipe_index=$2 new_rule=$3
605 [ ${ipfw_rule} = 0 ] && abort "No resources available"
606 debug "Found or allocated resources ipfw_rule: ${ipfw_rule} and pipe_index: ${pipe_index}"
608 add_rule $slice_id $new_rule $type $arg $ipfw_rule $pipe_index $timeout
609 hook_call $type $port $rule_base $pipe_base $timeout
610 return 0; # link configured, exit
614 # acquire the lock XXX check lockfile
616 [ "$TEST" = 1 ] && return
617 lockfile -s 0 -r 0 $lockfile 2> /dev/null
618 if [ $? -ne 0 ] ; then
619 echo "lock acquisition failed"
631 # initialize the firewall with PlanetLab default rules
635 ${IPFW} add $S skipto tablearg lookup jail $SLICE_TABLE
636 ${IPFW} add $D allow all from any to any
640 # if present, call a hook function
642 # slice_id type port rule_base pipe_base timeout
644 if [ -n "${HOOK}" -a -x "${HOOK}" ]; then
645 debug "Calling the hook function."
646 ${HOOK} ${SLICE_ID} "$*" &
653 ./neconfig {CLIENT|SERVER|SERVICE} arg [-t timeout] \
654 IN <pipe in configuration> OUT <pipe out configuration>
655 ./netconfig show {rules|pipes}
656 ./netconfig delete {CLIENT|SERVER|SERVICE} arg
657 ./netconfig refresh [-t timeout] {CLIENT|SERVER|SERVICE} arg
659 We support three modes of operation:
661 CLIENT programs on the node connect to remote ports
662 and/or addresses. Emulation intercepts traffic
663 involving those ports/addresses
665 SERVER programs on the node listen on specific ports.
666 Emulation intercepts traffic on those ports,
667 optionally limited to specific client addresses.
669 SERVICE the node runs both clients and servers,
670 we can only specify the ports on which emulation
673 'arg' has the form PORTLIST[@ADDRLIST], where ADDRLIST is
674 optional and only supported for CLIENT and SERVER modes.
675 PORTLIST and ADDRLIST can be specified as any valid port
676 or address specifier in ipfw, e.g.
677 - a single value 443 or 10.20.30.40/24
678 - a comma-separated list 1111,2222,3333 1.2.3.4,5.6.7.8
679 - a range 1111-2222 (only for ports)
680 Addresses can also be specified as symbolic hostnames, and
681 they are resolved when the rule is installed.
682 Note that they always indicate the remote endpoint.
684 On a given port a user can have one CLIENT and/or one SERVER
685 configuration or one SERVICE configuration.
686 When a SERVICE configuration is installed any existing CLIENT
687 and SERVER configuration on the same port are removed.
688 When a CLIENT or SERVER configuration is installed any existing
689 SERVICE configuration on the same port is removed.
691 The pipe's configuration, both for the upstream and downstream link,
692 follows the dummynet syntax. A quick and not exaustive example
693 of the parameters that can be used to configure the delay,
694 the bandwidth and the packet loss rate for a link follow:
696 IN|OUT delay 100ms bw 1Mbit/s plr 0.1
698 The profile file, if present, should be located into the sliver's
700 The full documentation is on the manpage[1].
702 The timeout value follow the linux 'date' command format[2]
703 and can be specified as follow:
709 [1] http://www.freebsd.org/cgi/man.cgi?query=ipfw
710 [2] http://linuxmanpages.com/man1/date.1.php
714 #--- DOCUMENTATION AND INTERNAL ARCHITECTURE ---
716 # When a user configures an emulated link, we need to allocate
717 # two pipes and one ipfw rule number to store the parameters.
718 # Reconfigurations of existing links reuse the previous resources.
719 # We keep track of all resources (pipes, rules and blocks of rules)
720 # in a database stored in a text file, see DATABASE FORMAT below.
722 # Pipes are allocated in pairs. In the database each pair is numbered
723 # from PIPE_MIN to PIPE_MAX. The actual pipe numbers for each pair are
725 # ipfw_pipein = IPFW_PIPE_MIN + 2*(pipe_index-1)
726 # ipfw_pipeout = ipfw_pipein + 1
728 # The rules number is allocated within a block of M consecutive rules
729 # for each slice. The block is allocated at the first configuration
730 # of an emulated link, and deallocated when the last link is removed.
731 # In the database, blocks are numbered from BLOCK_MIN to BLOCK_MAX,
732 # and the range of rules for a given block_index is
734 # ipfw_min_rule = RULE_BASE
735 # ipfw_max_rule = RULE_BASE + ((M-1)*block_index) -1
737 # All lookups, and the block allocation, are done in find_allocate().
738 # The rule_number and pipe_index are written in the database
739 # by add_rule() after checking the correctness of the request.
742 #--- RULESET STRUCTURE ---
743 # The ruleset is made of different sections, as follows:
744 # - an initial block of rules, reserved and configurable by
745 # the root context only;
746 # - a skipto rule (S), used to jump directly to the block
747 # associated with a given slice;
748 # - a second block of reserved rules, to catch remaining traffic.
749 # This ends with rule number D which is an 'accept all';
750 # - after D, we have a block of M rule numbers for each slice.
751 # Each of these blocks ends with an 'accept all' rule;
752 # - finally, rule 65535 is the firewall's default rule.
755 # 1...S-1 first block of reserved rules
756 # S skipto tablearg lookup jail 1
757 # S+1..D-1 ... second block of reserved rules
758 # D allow ip from any to any
760 # RULE_BASE <block of M entries for first user>
761 # RULE_BASE+M <block of M entry for second user ...>
764 #--- DATABASE FORMAT ---
765 # The database is stored in a text file, and contains one record per
766 # line with the following structure
768 # XID TYPE arg1 arg2 ...
770 # Whitespace separates the fields. arg1, arg2, ... have different
771 # meaning depending on the TYPE. XID is the slice ID.
773 # In the database we have the following records:
774 # - one entry of type BLOCK for each slice with configured links.
775 # This entry represents the block_index of the block of M ipfw
776 # rules allocated to the slice, as follows:
778 # XID BLOCK block_index
779 # (BLOCK_MIN <= block_index <= BLOCK_MAX)
781 # - one entry for each link (CLIENT, SERVER, SERVICE).
782 # The database entry for this info has the form
784 # XID {CLIENT|SERVER|SERVICE} arg ipfw_rule pipe_index timeout
786 # 'TYPE' reflects the configuration mode;
787 # 'arg' is PORTLIST@ADDRLIST and is used as a search key together
788 # with the XID and TYPE;
789 # 'ipfw_rule' is the unique ipfw rule number used for this
790 # emulated link. It must be within the block of M rule numbers
791 # allocated to the slice;
792 # 'pipe_index' is the index of the pair of pipes used for the
796 debug "--- $0 START for $SLICENAME ---"
798 # If the db does not exist, create it and clean rules and pipes
799 [ ! -e ${DBFILE} ] && clean_db
801 # A request to the vsys backend is composed by a single line of input
802 read REQ # read one line, ignore the rest
803 set_verbose ${REQ} # use inital -v if present
804 set_test ${REQ} # use inital -t if present
805 REQ="`filter ${REQ}`" # remove -v and -q and invalid chars
806 debug "--- processing <${REQ}>"
807 acquire_lock # critical section
810 debug "--- $0 END ---"