From: root Date: Fri, 2 Jul 2010 15:42:17 +0000 (+0200) Subject: Merge commit 'origin/0.7' into 0.7 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=refs%2Fheads%2Ftrunk%4012817;hp=32c92cbaae712d7e0e19dc97dcd03a3c798b2b0e;p=vsys-scripts.git Merge commit 'origin/0.7' into 0.7 --- diff --git a/Makefile b/Makefile index 8d4968d..bfea8db 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,28 @@ CC=gcc CFLAGS=-g -O2 -all: dcookie +all: dcookie fd_bmsocket fd_udpsocket fd_fusemount fd_tuntap fd_tos + +fd_tuntap: fd_tuntap.c + gcc fd_tuntap.c -o exec/fd_tuntap dcookie: dcookie.c gcc dcookie.c -o exec/dcookie +fdpass.o: fdpass.c + gcc -c fdpass.c -o fdpass.o + +fd_bmsocket: fd_bmsocket.c fdpass.o + gcc fd_bmsocket.c fdpass.o -o exec/fd_bmsocket + +fd_udpsocket: fd_udpsocket.c fdpass.o + gcc fd_udpsocket.c fdpass.o -o exec/fd_udpsocket + +fd_fusemount: fd_fusemount.c fdpass.o + gcc fd_fusemount.c fdpass.o -o exec/fd_fusemount + +fd_tos: fd_tos.c fdpass.o + gcc fd_tos.c fdpass.o -o exec/fd_tos + clean: rm -f exec/* diff --git a/exec/dotsshmount b/exec/dotsshmount new file mode 100755 index 0000000..9771556 --- /dev/null +++ b/exec/dotsshmount @@ -0,0 +1,11 @@ +#!/bin/sh +SRC="/home/$1/.ssh/" +DEST="/vservers/$1/home/$1/.ssh/" + +mkdir -p $DEST &> /dev/null + +mount | grep "on $DEST type" > /dev/null +if [ $? -ne 0 ]; then + /bin/mount --bind -o ro $SRC $DEST > /dev/null +fi + diff --git a/exec/goodfences b/exec/goodfences new file mode 100755 index 0000000..88c44e1 --- /dev/null +++ b/exec/goodfences @@ -0,0 +1,32 @@ +#!/usr/bin/perl + +use strict; + +$|=1; + +my $slicename; +my $portnumber; + + +$slicename = $ARGV[0]; +$portnumber = ; + +chop($portnumber); + +if ($portnumber!~/^\d+$/) { + die("$portnumber is not a port number"); +} + +open CMD,"/sbin/fuser -n tcp $portnumber 2>&1|"; + +$_ = ; + +my @ports; +@ports=split; + +if ($#ports > 0) { + system("cat /proc/$ports[1]/vinfo"); + print "Please refer to http://planetflow.planet-lab.org/lookupslice.php to obtain the name corresponding to this slice."; +} + +close CMD; diff --git a/exec/if_port_forward b/exec/if_port_forward new file mode 100755 index 0000000..400591b --- /dev/null +++ b/exec/if_port_forward @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +$|=1; +$slice=$ARGV[0]; +$slice_ip=`cat /etc/vservers/$slice/interfaces/0/ip`; +$src_port = ; +$dst_port = ; + +chomp($slice_ip); +chomp($src_port); +chomp($dst_port); + +die ("Src and dst ports need to be numbers") if (($src_port=~/[^0-9]/) || ($src_port=~/[^0-9]/)); + +$IPTABLES_CMD_1="/sbin/iptables -t nat -D PREROUTING -m tcp -p tcp -d $slice_ip --dport $src_port -j DNAT --to-destination $slice_ip:$dst_port"; + +$IPTABLES_CMD_2="/sbin/iptables -t nat -A PREROUTING -m tcp -p tcp -d $slice_ip --dport $src_port -j DNAT --to-destination $slice_ip:$dst_port"; + +print "Removing any previous instances of this rule: $IPTABLES_CMD_1\n"; +system($IPTABLES_CMD_1); + +print "Adding forwarding rule: $IPTABLES_CMD_2\n"; +system($IPTABLES_CMD_2); + +print "Done.\n"; diff --git a/exec/ipfw-be b/exec/ipfw-be new file mode 100755 index 0000000..b3b9439 --- /dev/null +++ b/exec/ipfw-be @@ -0,0 +1,811 @@ +#!/bin/sh +# +# Marta Carbone, Luigi Rizzo +# Copyright (C) 2009 Universita` di Pisa +# $Id$ +# +# This script the vsys backend used to configure emulation. +# In detail it: +# - reads the user's input from the vsys input pipe +# - validates the input +# - configures the firewall +# - writes results on the output vsys pipe +# +# Configurable variables are at the beginning (only HOOK so far) + +# If HOOK is set, ${HOOK} is called before configuring a rule. +# A sample hook can be found in the ipfwroot.rpm package, +# it can be used to collect statistical information on dummynet usage. +# To configure a hook, set the HOOK variable as follow: +# HOOK=/tmp/sample_hook + +#--- You should not touch anything below this line. ---- +# For documentation see ARCHITECTURE near the end of the file. + +#--- global variables --- +VERBOSE=0 # set to !0 to enable debug messages +TEST=0 # set to 1 for test mode + +# The database and the lock file +DBFILE=/tmp/ff +lockfile=/var/lock/ipfw.lock + +# Min and max value (inclusive) for block_index +BLOCK_MIN=1 +BLOCK_MAX=1000 +M=50 # size of per-slice block of rules +# Min and max value (inclusive) for pipe_index +PIPE_MIN=1 +PIPE_MAX=25000 + +# These are the actual rule numbers used in ipfw +IPFW_RULE_MIN=10000 # initial per-slice rule number +IPFW_PIPE_MIN=10000 # initial pipe number + +# 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 +# these are the credential of the user invoking the backend +SLICENAME=$1 +SLICE_ID=`id -u $SLICENAME` +[ x"$SLICE_ID" = x"" ] && echo "No sliver present." && exit + +# programs +# XXX check consistency for variables {} +SED=/bin/sed +SEDOPT=-r +[ -x ${SED} ] || { SED=`which sed` ; SEDOPT=-E ; } +IPFW=/sbin/ipfw +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 mode if -q is found +set_test() { + [ x"$1" = x"-q" -o x"$2" = x"-q" ] || return + TEST=1 + IPFW="/bin/echo ipfw:" + IPFW_CHECK="/bin/echo ipfw -n:" +} + +abort() { # $1 message to be displayed in case of error + release_lock + echo "ipfw-be aborting (netconfig help): $1" + exit 1 +} + +# remove dangerous characters from user input +# if present, the leading '-v/-q' will be removed +filter() { # $* variables to be filtered + [ x${1} = x"-v" -o x${1} = x"-q" ] && shift + [ x${1} = x"-v" -o x${1} = x"-q" ] && shift + # allowed chars are: numbers, uppercase and lowercase letters, + # spaces, and the following symbols: .,_-/ + echo "$*" | ${SED} ${SEDOPT} 's/[^\t0-9a-zA-Z., _\/\{}@-]*//g' +} + +# remove all entries from the ipfw config, and create an empty db +clean_db() { + rm -f ${DBFILE} + touch ${DBFILE} + # we would like to delete ranges of rules and pipes but this + # is not supported so for the time being we kill them all + ${IPFW} -q flush + ${IPFW} -q pipe flush + ${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 +# rule_in rule_out because they may be long. XXX why ? +# Other arguments are on the command line +# +# 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 + local p h # used to split the argument + + local h_in h_out + # local rule_in rule_out # XXX test if this works + # find actual pipe numbers + ipfw_pipe_in=$(($IPFW_PIPE_MIN + $((2 * $(($pipe_index - 1)))) )) + ipfw_pipe_out=$(($ipfw_pipe_in + 1)) + local del # used to delete incompatible configurations + + # split the argument, and prepare PORTLIST (p) and ADDRLIST (h) + p=`echo $arg | cut -s -d "@" -f1-` # empty if no separator + if [ "$p" = "" ] ; then + p=$arg + else + p=`echo $arg | cut -d "@" -f1` + h=`echo $arg | cut -d "@" -f2` + fi + + if [ "$h" = "" ] ; then + h_in="" + h_out="" + else + h_in=" src-ip ${h} " + h_out=" dst-ip ${h} " + fi + + # first, call ipfw -n to check syntax, if ok move on and do the action + if [ x"$new_rule" != x"0" ] ; then + case $type in + SERVER|server) + rule_in="dst-port $p" + rule_out="src-port $p" + del=service + ;; + CLIENT|client) + rule_in="src-port $p" + rule_out="dst-port $p" + del=service + ;; + SERVICE|service) + rule_in="{ src-port $p or dst-port $p }" + rule_out="{ src-port $p or dst-port $p }" + del="cli_ser" + ;; + *) + abort "invalid service type $type" + ;; + esac + + rule_in="pipe ${ipfw_pipe_in} in ${h_in} ${rule_in} // $type $arg $slice_id" + rule_out="pipe ${ipfw_pipe_out} out ${h_out} ${rule_out} // $type $arg $slice_id" + + # Move into the user root directory. The profile should be located there + ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} add ${ipfw_rule} ${rule_in} ) > /dev/null || \ + abort "ipfw syntax error ${rule_in}" + ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} add ${ipfw_rule} ${rule_out} ) > /dev/null || \ + abort "ipfw syntax error ${rule_out}" + fi + + # check error reporting + ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW_CHECK} pipe ${ipfw_pipe_in} config ${CONFIG_PIPE_IN} ) > /dev/null || \ + abort "ipfw syntax error pipe_in" + ( cd /vservers/${SLICENAME}/`pwd`/ ; ${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 0 $slice_id service $arg + [ "$del" = "cli_ser" ] && do_delete 0 $slice_id client $arg + [ "$del" = "cli_ser" ] && do_delete 0 $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 + ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW} pipe ${ipfw_pipe_in} config ${CONFIG_PIPE_IN} ) + ( cd /vservers/${SLICENAME}/`pwd`/ ; ${IPFW} pipe ${ipfw_pipe_out} config ${CONFIG_PIPE_OUT} ) + + # send output to the user + ${IPFW} show ${ipfw_rule} + ${IPFW} pipe ${ipfw_pipe_in} show + ${IPFW} pipe ${ipfw_pipe_out} show + + # do not write on the database on test-only + [ "$TEST" = "1" ] && return + # add to the database + ( grep -iv -- "^${slice_id} ${type} ${arg} " $DBFILE; \ + echo "${slice_id} ${type} ${arg} ${ipfw_rule} ${pipe_index} ${timeout}" ) > ${DBFILE}.tmp + mv ${DBFILE}.tmp ${DBFILE} +} + +# +# Delete a given configuration +# 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 {CLIENT|SERVER|SERVICE} arg" + set `find_rule $slice_id $type $arg` + 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 (case insensitive) + grep -iv -- "^${slice_id} ${type} ${arg} " $DBFILE > ${DBFILE}.tmp + mv ${DBFILE}.tmp ${DBFILE} + + # if there are no more 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 -iv -- "^${slice_id} BLOCK ${block_n}" $DBFILE; ) > ${DBFILE}.tmp + mv ${DBFILE}.tmp ${DBFILE} + return 0 +} + +# +# Find a rule and pipe_index for the given key (xid type arg) +# Allocate a new block if first entry for this xid. +# Rule and pipe are not written into the database, only the block is. +# +# Return ipfw_rule pipe_index new_rule +# 'new_rule' is 0 if the rule existed, 1 if it is new +# +# return ipfw_rule = 0 if there are no resources 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 existing block, or + # allocate a new one + local block_n=`find_block ${slice_id}` + [ ${block_n} = "0" ] && new_block=1 && block_n=`find_free_block` + [ ${block_n} = "0" -o ${block_n} -gt $BLOCK_MAX ] && echo 0 && return 0; + + # We have a valid block, compute the range for user rules + local ipfw_rulemin=$(($IPFW_RULE_MIN + $(($M *$(($block_n - 1)))))) + local ipfw_rulemax=$(($(($ipfw_rulemin + $M)) - 1 )) + + # Find rule and pipes, reserve the last rule for the user's + # default rule that catches regular traffic. + set `allocate_resources $ipfw_rulemin $(($ipfw_rulemax - 1))` + ipfw_rule=$1; pipe_index=$2 + [ $ipfw_rule = 0 ] && echo 0 && return 0 # no resources + + # If this is a new block, add the slice to the lookup table + # and put a default rule at the end of the block. + 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 with +# the current firewall configuration. The database contains +# slice_id type arg ipfw_rule pipe_index timeout +# On match returns +# On non match returns 0 0 0 +# no echo inside +find_rule() { # slice_id type arg + local ret + ret=`grep -i -- "^$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 + # screwed anyways + set $ret + 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 +find_hole() { # min max + local min=$1 cand=$1 max=$2 line + while read line ; do + [ $line -lt $min ] && continue + [ $line -ne $cand ] && break # found + [ $cand -ge $max ] && cand=0 && break # no space + cand=$(($cand + 1)) + done + echo $cand +} + +# XXX despite the name this does not allocate but only finds holes. +# returns a free rule and pipe base for client|server|service +# within a block +# Returns r=0 if there are no resources available +# no echo inside +allocate_resources() { # ipfw_minrule ipfw_maxrule + local p r + # remove comments, extract field, sort + p=`grep -v '^#' $DBFILE | grep -v BLOCK | awk '{print $5}' | sort -n | \ + find_hole $PIPE_MIN $PIPE_MAX` + 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 +} + + +# Returns the index of a free block +# Returns 0 if there are no resources available +# no debug inside +find_free_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 -iv -- "^${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] IN OUT +# 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 OUT + ./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 +# RULE_BASE+M +# ... +# +#--- 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 diff --git a/exec/mknod b/exec/mknod new file mode 100755 index 0000000..2dc11b4 --- /dev/null +++ b/exec/mknod @@ -0,0 +1,16 @@ +#!/usr/bin/perl + +use strict; + +my $slicename=$ARGV[0]; +my $slice_dev="/vservers/$slicename/dev"; + +my $name = ; +my $major = ; +my $minor = ; +chomp($name); +chomp($major); +chomp($minor); + +$name=~s/\.\.//g; +system("mknod $slice_dev".$name."c $major $minor"); diff --git a/exec/reserve_tcp_port b/exec/reserve_tcp_port new file mode 100755 index 0000000..a9043ea --- /dev/null +++ b/exec/reserve_tcp_port @@ -0,0 +1,61 @@ +#!/usr/bin/perl + +use strict; + +$|=1; + +my $slicename; +my $xid; +my $portnumber; +my $fuser_cmd; +my $pid; +my @vinfo; +my $foo; +my $real_xid; + +$slicename = $ARGV[0]; +$xid = int(`id -u $slicename`) || die("Could not determine xid of $slicename\n"); +$portnumber = ; +chop($portnumber); + +if ($portnumber!~/^\d+$/) { + die("$portnumber is not a port number"); +} + +if ($portnumber<61001 || $portnumber>65535) { + die("$portnumber lies in the local port-rover range\n"); +} + +$fuser_cmd = "ncontext --nid $xid --migrate -- vcontext --xid $xid --migrate fuser -n tcp $portnumber 2>/dev/null"; +$pid = `$fuser_cmd` || die("Please bind to port $portnumber, e.g. run nc -l $portnumber."); +$pid=~s/\s+//g; + +my $cat_cmd; + +$cat_cmd = "chcontext --ctx 1 cat /proc/$pid/vinfo"; +$_ = `$cat_cmd`; +@vinfo = split /\n/; +($#vinfo>0) || die("Port reservation failed. Please try again.\n"); + +$_=@vinfo[0]; + +($foo,$real_xid) = split; + +if (int($real_xid)!=$xid) { + die("Port $portnumber belongs to user $real_xid, not to you.\n"); +} + +# OK. We're good +# Only, slices are allowed only 1 port reservation/session + +if ( -f "/dev/shm/$slicename-port" ) { + print "You have already reserved the following port:\n"; + system("cat /dev/shm/$slicename-port"); + exit(1); +} + +open FIL,">/dev/shm/$slicename-port"; +print FIL "$portnumber"; +close FIL; + +system("iptables -t mangle -A INPUT -m tcp -p tcp --dport $portnumber -j MARK --set-mark $xid"); diff --git a/exec/reserve_udp_port b/exec/reserve_udp_port new file mode 100755 index 0000000..909f3bc --- /dev/null +++ b/exec/reserve_udp_port @@ -0,0 +1,61 @@ +#!/usr/bin/perl + +use strict; + +$|=1; + +my $slicename; +my $xid; +my $portnumber; +my $fuser_cmd; +my $pid; +my @vinfo; +my $foo; +my $real_xid; + +$slicename = $ARGV[0]; +$xid = int(`id -u $slicename`) || die("Could not determine xid of $slicename\n"); +$portnumber = ; +chop($portnumber); + +if ($portnumber!~/^\d+$/) { + die("$portnumber is not a port number"); +} + +if ($portnumber<61001 || $portnumber>65535) { + die("$portnumber lies in the local port-rover range\n"); +} + +$fuser_cmd = "ncontext --nid $xid --migrate -- vcontext --xid $xid --migrate fuser -n udp $portnumber 2>/dev/null"; +$pid = `$fuser_cmd` || die("Please bind to port $portnumber, e.g. run nc -ul $portnumber."); +$pid=~s/\s+//g; + +my $cat_cmd; + +$cat_cmd = "chcontext --ctx 1 cat /proc/$pid/vinfo"; +$_ = `$cat_cmd`; +@vinfo = split /\n/; +($#vinfo>0) || die("Port reservation failed. Please try again.\n"); + +$_=@vinfo[0]; + +($foo,$real_xid) = split; + +if (int($real_xid)!=$xid) { + die("Port $portnumber belongs to user $real_xid, not to you.\n"); +} + +# OK. We're good +# Only, slices are allowed only 1 port reservation/session + +if ( -f "/dev/shm/$slicename-port" ) { + print "You have already reserved the following port:\n"; + system("cat /dev/shm/$slicename-port"); + exit(1); +} + +open FIL,">/dev/shm/$slicename-port"; +print FIL "$portnumber"; +close FIL; + +system("iptables -t mangle -A INPUT -m udp -p udp --dport $portnumber -j MARK --set-mark $xid"); diff --git a/exec/rsyncauthlogs b/exec/rsyncauthlogs new file mode 100755 index 0000000..4914501 --- /dev/null +++ b/exec/rsyncauthlogs @@ -0,0 +1,5 @@ +#!/bin/sh + +DEST="/vservers/$1/home/$1/" + +/usr/bin/rsync -a /var/log/secure* $DEST diff --git a/exec/run_with_devices b/exec/run_with_devices new file mode 100644 index 0000000..c0b2312 --- /dev/null +++ b/exec/run_with_devices @@ -0,0 +1,57 @@ +#!/usr/bin/python -u +# All vsys scripts should use STDOUT in unbuferred mode, or else sometimes your output will get bufferred and you will not see it till the buffer gets flushed. + +import sys +import os + +vsys_config_dir = "/etc/planetlab/vsys-attributes" + +slicename=sys.argv[1] +sliceid = pwd.getpwnam(slicename).pw_uid + +arglines = map(string.strip, sys.stdin.readlines()) +command_name = arglines[0] +device_names = arglines[1:] + +print "Validating interface names... ", +# Validate interface names + +for vif in device_names: + if len(vif)>16: + print "Interface name %s invalid"%(vif) + sys.exit(1) + if re.match(r'(tun|tap)%d-\d+' % sliceid, vif ) is None: + print "Interface name %s does not match slice id %d."%(vif, sliceid) + sys.exit(1) + print "[OK]" + +# The interfaces have been validated. We don't need to validate the executable +# path for escape hatches because we are going to use execve. + +pid = os.fork() +if (pid): + # Close open fds before execve + print "Closing file descriptors." + for fd in xrange(3, 1023): + try: + os.close(fd) + except OSError: + pass + # Execute command + vserver_command = "/usr/sbin/vserver" + args = [slicename] + args += ['exec'] + args += [command_name] + + os.system('touch /etc/vservers/%s/spaces/net'%slicename) + + try: + os.execve(vserver_command, args) + except: + pass + + os.system('rm /etc/vservers/%s/spaces/net'%slicename) +else: + for vif in device_names: + os.system('/sbin/ip link set %s netns %d'%(vif, pid)) + diff --git a/exec/setup-link b/exec/setup-link index e5cbd2f..cc800ce 100755 --- a/exec/setup-link +++ b/exec/setup-link @@ -1,7 +1,5 @@ #!/bin/sh +x -IP=/sbin/ip - SLICE=$1 SLICEID=`id -u $SLICE` read INDEX @@ -11,17 +9,16 @@ read KEY LINK=${KEY}if${INDEX} modprobe ip_gre -modprobe etun ### Setup EGRE tunnel EGRE=d$LINK -$IP tunnel add $EGRE mode gre/eth remote $REMOTE key $KEY -$IP link set $EGRE up +ip tunnel add $EGRE mode gre type eth remote $REMOTE key $KEY ttl 64 +ip link set $EGRE up ### Setup etun ETUN0=a$LINK ETUN1=b$LINK -echo $ETUN0,$ETUN1 > /sys/module/etun/parameters/newif +ip link add name $ETUN0 type veth peer name $ETUN1 ifconfig $ETUN0 mtu 1458 up ifconfig $ETUN1 up @@ -44,7 +41,7 @@ cat > $GRAB < /sys/class/net/$ETUN0/new_ns_pid +ip link set $ETUN0 netns \$PID EOF chmod +x $GRAB @@ -79,7 +76,7 @@ read NULL iptables -t mangle -D FORWARD -o $BRIDGE -j MARK --set-mark $SLICEID # Get rid of etun devices, only need name of one of them -echo $ETUN1 > /sys/module/etun/parameters/delif +ip link delete dev $ETUN1 # Get rid of bridge ifconfig $BRIDGE down diff --git a/exec/setup-nat b/exec/setup-nat index 64be40b..9a0c4f5 100755 --- a/exec/setup-nat +++ b/exec/setup-nat @@ -1,22 +1,18 @@ #!/bin/sh +x -IP=/sbin/ip - SLICE=$1 SLICEID=`id -u $SLICE` read KEY -modprobe etun - ### Setup etun ETUN0=nat$KEY ETUN1=natx$KEY -echo $ETUN0,$ETUN1 > /sys/module/etun/parameters/newif -ifconfig $ETUN1 10.0.$KEY.1 up +ip link add name $ETUN0 type veth peer name $ETUN1 +ifconfig $ETUN1 10.0.$KEY.1/24 up -/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE -/sbin/iptables -A FORWARD -i eth0 -o $ETUN1 -m state --state RELATED,ESTABLISHED -j ACCEPT -/sbin/iptables -A FORWARD -i $ETUN1 -o eth0 -j ACCEPT +iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE +iptables -A FORWARD -i eth0 -o $ETUN1 -m state --state RELATED,ESTABLISHED -j ACCEPT +iptables -A FORWARD -i $ETUN1 -o eth0 -j ACCEPT ### Create "grab link" script GRAB=/vsys/local_grab-$ETUN0 @@ -27,7 +23,7 @@ cat > $GRAB < /sys/class/net/$ETUN0/new_ns_pid +ip link set $ETUN0 netns \$PID EOF chmod +x $GRAB @@ -41,12 +37,12 @@ cat > $DELETE < /sys/module/etun/parameters/delif +ip link delete dev $ETUN1 # Clean up files rm -f $GRAB $GRAB.acl diff --git a/exec/sliceip b/exec/sliceip new file mode 100755 index 0000000..8316856 --- /dev/null +++ b/exec/sliceip @@ -0,0 +1,498 @@ +#!/bin/bash +# +# Giovanni Di Stasi +# Copyright (C) 2009 UniNa +# $Id$ +# +# This is a backend script to be used with +# the vsys system. It allows to define and +# manage slice-specific routing tables. +# The routing rules added to these routing +# tables affect only the respective slices. +# +# This script is meant to be called from a +# frontend script in the slice environment. +# +# Fronted usage: +# sliceip enable +# This is the first command to issue; it enables the use +# of the interface for the slice. This command has to be issued +# for each interface involved in the slice-specific routing rules. +# +# sliceip disable +# This is to disable the use of the interface for the slice. +# +# sliceip route show +# This command shows the current routing rules of the +# slice-specific routing table +# +# sliceip route add to via dev +# This command adds a rule to reach through the host +# that can be reached on the interface +# ... +# +# sliceip in general supports all the commands of the "ip" command of +# the "iproute2" suite. Refer to ip manpage for further information. +# +# Some examples: +# sliceip route add to 143.225.229.142 via 192.168.8.2 dev ath0 +# sliceip route add default via 10.1.1.1 dev ath0 +# +# If you want to test the script from the root context you need +# to call it as vsys would do: +# sliceip +# Then you can issue to its standard input the commands previously explained +# (but you have to remove the initial "sliceip"). Ex.: +# route add to 143.225.229.142 via 192.168.8.2 dev ath0 + + +PATH=/bin:/usr/bin:/sbin:/usr/sbin + +# sliver wich called the script +sliver=$1 + +#files used +RT_TABLES="/etc/iproute2/rt_tables" +INT_FILE="/tmp/slcroute_ifns" + +# routing tables from 100 to 255 are used +FIRST_TABLE=100 + +DEBUG=0 + +#this script can also work on a Linux machine, in wich case +#it enables user-specific routing tables +LINUX_ENV=0 # 0 - Linux host; + # does not work for the icmp protocol + +PLANET_ENV=1 # 1 - PlanetLab context (slice-specific routing) + +#select the PlanetLab environment +ENVIRONMENT=$PLANET_ENV + + +# enable an interface for the slice specific routing rules +# and initialise the slice routing table +function enable_interface(){ # $1 calling sliver; $2 interface to enable + local sliver=$1 + local interface="$2" + local ip=`get_address $interface` + + if [[ "" == $ip ]]; then + echo "Failed in getting address of $interface" + return 1 + fi + + #create the sliceip netfilter chains + check_chains + + local nid=`get_nid $sliver` + local num_active=`get_num_activated $sliver` + + if ! is_active $sliver $interface; then + if [[ $num_active == 0 ]]; then + #define the routing table for the slice and set some iptables rules in order to mark user's packets; + create_table $sliver + fi + + #Adds an SNAT rule to set the source IP address of packets that are about to go out through $interface. + #The kernel sets the source address of packets when the first routing process happens (following the rules + #of the main routing table); when the rerouting process happens (following the user/slice specific rules) + #the source address is not modified. Consequently, we need to add this rule to change the source ip address + #to the $interface source address. + exec "iptables -t nat -A $NAT_POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid" + + set_active $sliver $interface $ip + else + local old_ip=`get_ip_from_file $sliver $interface` + + #if the ip of the interface has changed, set the new SNAT rule + if [[ $old_ip != $ip ]]; then + #remove the rule for the old ip + exec "iptables -t nat -D $NAT_POSTROUTING -o $interface -j SNAT --to-source $old_ip -m mark --mark $nid" + #insert the new rule + exec "iptables -t nat -A $NAT_POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid" + set_deactivated $sliver $interface $old_ip + set_active $sliver $interface $ip + fi + + + fi + +} + +# disable the interface for the slice-specific routing rules +function disable_interface(){ + local sliver=$1 + local interface=$2 + local ip=`get_ip_from_file $sliver $interface` + local nid=`get_nid $sliver` + + if is_active $sliver $interface >/dev/null 2>&1; then + exec "iptables -t nat -D $NAT_POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid" + + local num_ifn_on=`get_num_activated $sliver` + + if [[ $num_ifn_on == 1 ]]; then + delete_table $sliver + fi + set_deactivated $sliver $interface + fi + +} + +# wrapper function used to execute system commands +function exec(){ + local command=$1 + + if ! [[ $command ]]; then + echo "Error in exec. No argument." + exit 1 + else + if ! $command; then + if [[ $DEBUG == 1 ]]; then echo "Error executing \"$1\""; fi + exit 1 + fi + fi + +} + +# decides wich id to use for the slice-specific routing table +function get_table_id(){ + local sliver=$1 + k=$FIRST_TABLE + + while [[ $k < 255 ]] && grep $k $RT_TABLES >/dev/null 2>&1 ; do + k=$((k+1)) + done + + if [[ $k == 255 ]]; then + logm "No routing tables available. Exiting." + return 1 + fi + + echo $k +} + + +# create the slice-specifig routing table +function create_table(){ + local sliver=$1 + local table_name=`get_table_name $sliver` + local table_id=`get_table_id $sliver` + local temp_nid=`get_temp_nid $sliver` + + if ! grep $table_name $RT_TABLES > /dev/null 2>&1; then + echo "$table_id $table_name" >> $RT_TABLES + else + echo "WARNING: $table_name routing table already defined." + fi + + set_routes $sliver $table_name + + return 0 +} + +# delete the slice-specific routing table +function delete_table(){ + local sliver=$1 + local table_name=`get_table_name $sliver` + local table_id=`get_nid $sliver` + local temp_nid=`get_temp_nid $sliver` + + if ! grep $table_name $RT_TABLES > /dev/null 2>&1; then + return 1 + else + exec "ip route flush table $table_name" >/dev/null 2>&1 + unset_routes $sliver $table_name + remove_line "$RT_TABLES" "$table_name" + fi + + return 0 +} + +# remove a line from a file +function remove_line(){ + local filename=$1 + local regex=$2 + + #remove interface line from the file + exec "sed -i /${regex}/d $filename" +} + +# get the slice-specific routing table name +function get_table_name(){ + local sliver=$1; + echo "${sliver}_slcip" +} + +#remove files used by sliceip and the added routing tables +function clean_iproute_conf(){ + while grep "slcip" $RT_TABLES >/dev/null 2>&1; do + remove_line $RT_TABLES "slcip" + done + + rm -f ${INT_FILE}-* + +} + +#get slice network id +function get_nid(){ + id -u ${1} +} + +# set the firewall rules. Mainly, it asks VNET+ to set the netfilter mark of packets belonging to the slice to the slice-id +# and then associates those packets with the routing table allocated for that slice. +function set_routes(){ + local sliver=$1 + local table_name=$2 + local sliver_nid=`get_nid $sliver` + local temp_nid=`get_temp_nid $sliver` + + if [ $ENVIRONMENT == $PLANET_ENV ]; then + #Linux kernel triggers a rerouting process, wich is needed to perfom slice-specific routing, + #if it sees that the netfilter mark has been altered in the iptables mangle chain. + #As VNET+ sets the netfilter mark of some packets in advance (to the slice-id value) before they enter the iptables mangle chain, + #we need to change it here (otherwise the rerouting process is not triggered for them). + exec "iptables -t mangle -I $MANGLE_OUTPUT 1 -m mark --mark $sliver_nid -j MARK --set-mark $temp_nid" + + #make sure the netfilter mark of those "strange packets" won't be changed by the following rule + exec "iptables -t mangle -I $MANGLE_OUTPUT 2 -m mark --mark $temp_nid -j RETURN" + + #Here we ask VNET+ to set the netfilter mark for the remaining packets (to the slice-id) + #we need to call this only once + if [[ $first_time == 1 ]]; then + exec "iptables -t mangle -A $MANGLE_OUTPUT -j MARK --copy-xid 0x00" + fi + + elif [ $ENVIRONMENT == $LINUX_ENV ]; then + #the same in the case of a "plain" Linux box. In this case we do not use VNET+ but + #the owner module of iptables. + exec "iptables -t mangle -A $MANGLE_OUTPUT -m owner --uid-owner $sliver_nid -j MARK --set-mark $sliver_nid" + fi + + if [ $ENVIRONMENT == $PLANET_ENV ]; then + #Here the netfilter mark is restored to the slice-id value for the "strange packets" + exec "iptables -t mangle -A $MANGLE_POSTROUTING -m mark --mark $temp_nid -j MARK --set-mark $sliver_nid" + fi + + #Set the routing for the slice to be applied following the rules in $table_name" + #for the slice packets... + exec "ip rule add fwmark $sliver_nid table $table_name" + #...and for the "strange packets" + exec "ip rule add fwmark $temp_nid table $table_name" + + exec "ip route flush cache" >/dev/null 2>&1 +} + + +# remove the firewall rules. +function unset_routes(){ + local sliver=$1 + local table_name=$2 + local sliver_nid=`get_nid $sliver` + local temp_nid=`get_temp_nid $sliver` + + if [ $ENVIRONMENT == $PLANET_ENV ]; then + #removes the rules for the netfilter marks (for the "strange packets") + exec "iptables -t mangle -D $MANGLE_OUTPUT -m mark --mark $sliver_nid -j MARK --set-mark $temp_nid" + exec "iptables -t mangle -D $MANGLE_OUTPUT -m mark --mark $temp_nid -j RETURN" + + #removes the rule for restoring the original mark + exec "iptables -t mangle -D $MANGLE_POSTROUTING -m mark --mark $temp_nid -j MARK --set-mark $sliver_nid" + + elif [ $ENVIRONMENT == $LINUX_ENV ]; then + #removes the rules that asks the owner module of iptables to set the netfilter mark to the user-id + exec "iptables -t mangle -D $MANGLE_OUTPUT -m owner --uid-owner $sliver_nid -j MARK --set-mark $sliver_nid" + fi + + exec "ip rule del fwmark $temp_nid table $table_name" + exec "ip rule del fwmark $sliver_nid table $table_name" + + exec "ip route flush cache" + +} + +# additional iptables chains where sliceip inserts its rules +NAT_POSTROUTING="sliceip" +MANGLE_OUTPUT="sliceip_output" +MANGLE_POSTROUTING="sliceip_postrouting" + +# create the chains +check_chains(){ + first_time=0 + + #create the chain where SNAT is performed + if iptables -t nat -N $NAT_POSTROUTING >/dev/null 2>&1; then + #it's the first time sliceip is called, the chain was not defined + first_time=1 + + #create a chain where the netfilter mark is set + exec "iptables -t mangle -N $MANGLE_OUTPUT" + + #create a chain where the netfilter mark for some packets is restored (see set_routes) + exec "iptables -t mangle -N $MANGLE_POSTROUTING" + + #add the rules to take packets to the previously defined chains + exec "iptables -t nat -A POSTROUTING -j $NAT_POSTROUTING" + exec "iptables -t mangle -A OUTPUT -j $MANGLE_OUTPUT" + exec "iptables -t mangle -I POSTROUTING 1 -j $MANGLE_POSTROUTING" + + #cleaning up + clean_iproute_conf + fi +} + +# get the ip address of an interface +function get_address(){ + local interface=$1 + local ip="" + + ip=`ifconfig $interface | grep inet\ addr | cut -d ":" -f 2 | cut -d " " -f 1`; + + if valid_dotted_quad $ip; then + echo $ip; + else + echo ""; + fi + +} + +# get the temporary mark to be applied to the packets of the slice +function get_temp_nid(){ + local sliver_nid=`get_nid $1` + local temp_nid=$((0x20000+$sliver_nid)) + echo $temp_nid +} + +# log function +function logm(){ + logger $1 +} + +# get the name of the filename in wich we store information about +# the interfaces in use by the user +function get_filename_sliver(){ + local sliver=$1 + echo "${INT_FILE}-$sliver" +} + +function set_active(){ + local sliver=$1 + local interface=$2 + local ip=$3 + local filename="${INT_FILE}-$sliver" + echo "$interface $ip" >> $filename +} + +function set_deactivated(){ + local sliver=$1 + local interface=$2 + local filename=`get_filename_sliver $sliver` + remove_line $filename $interface +} + +function is_active(){ + local sliver=$1 + local interface=$2 + local filename=`get_filename_sliver $sliver` + + if grep $interface $filename >/dev/null 2>&1; then + return 0 + else + return 1 + fi +} + +function get_num_activated(){ + local sliver=$1 + local filename=`get_filename_sliver $sliver` + + if ! [ -e $filename ]; then + echo 0; + else + wc -l $filename | cut -f 1 -d " "; + fi +} + + +function get_ip_from_file(){ + local sliver=$1 + local interface=$2 + + local filename=`get_filename_sliver $sliver` + + cat $filename | grep $interface | cut -d " " -f 2 +} + + +# check ip addresses +function valid_dotted_quad(){ + oldIFS=$IFS + IFS=. + set -f + set -- $1 + if [ $# -eq 4 ] + then + for seg + do + case $seg in + ""|*[!0-9]*) return 1; break ;; ## Segment empty or non-numeric char + *) [ $seg -gt 255 ] && return 2 ;; + esac + done + else + return 3 ## Not 4 segments + fi + IFS=$oldIFS + set +f + return 0; +} + + +# BEGIN +# the script starts here + +if [[ $sliver == "" ]]; then + echo "I need the first argument (the sliver name)"; + exit 1 +fi + +# read a line from the vsys pipe +read line + +# separate the first word of the line from the others +command=`echo ${line%% *}` +rest=`echo ${line#* }` + +case "$command" in + enable) + logger "sliceip command received from $sliver: $line" + enable_interface $sliver "$rest" + ;; + + disable) + logger "sliceip command received from $sliver: $line" + disable_interface $sliver "$rest" + ;; + + *) + logger "sliceip command received from $sliver: $line" + table_sliver=`get_table_name $sliver` + + if ! grep "$table_sliver" $RT_TABLES >/dev/null 2>&1; then + echo "Error. The slice routing table is not defined. Execute sliceip enable ." + exit 1 + else + #add the routing rule - ip is called with the same parameters of sliceip but the indication of + #the table in wich the rule is to be inserted is appended + exec "ip $line table $table_sliver" + fi + ;; + +esac + +exit 0 + + + diff --git a/exec/umount b/exec/umount new file mode 100755 index 0000000..7bde900 --- /dev/null +++ b/exec/umount @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +use strict; + +my $slicename=$ARGV[0]; +my $slice_dir="/vservers/$slicename/"; +my $localpath = ; + +chomp($localpath); + +$localpath=$slice_dir.$localpath; + +my $pathtest; + +$pathtest=''; +while ($localpath=~/(\/?[\w\d\-_]+\/?)/g) { + $pathtest=$pathtest.$1; +} + +if ($pathtest ne $localpath) { + die "$localpath is not a safe path"; +} + +my $mntcmd="/bin/umount $localpath"; +system($mntcmd); + +if ($?) { + print "Mount failed: $?"; +} diff --git a/exec/vif_down b/exec/vif_down new file mode 100755 index 0000000..8f87a89 --- /dev/null +++ b/exec/vif_down @@ -0,0 +1,47 @@ +#!/usr/bin/python +# VSYS script to ifdown per-slice virtual network interfaces from the root slice +# Thom Haddow - 06/10/09 +# +# Gets slice name as argv[1] +# Takes remaining args on stdin: +# - Interface name (eg [tun|tap]-) + +import sys +import pwd +import os +import re + + +if len(sys.argv) != 2: sys.exit(1) + +# VSYS scripts get slicename as $1 +slicename=sys.argv[1] +sliceid = pwd.getpwnam(slicename).pw_uid + + +# Read interface name +vif = sys.stdin.readline().strip() # interface name + + +# Validate interface name +if len(vif)>16: + print >>sys.stderr, "Interface name %s invalid"%(vif) + sys.exit(1) + +if re.match(r'(tun|tap)%d-\d+' % sliceid, vif ) is None: + print >>sys.stderr, "Interface name %s does not match slice id %d."%(vif, sliceid) + sys.exit(1) + + + +# Bring interface down + +cmd_ifconfig = "/sbin/ifconfig %s down" % (vif) +os.system(cmd_ifconfig) + +# Remove iptables rules +cmd_iptables_del_in = "/sbin/iptables -D INPUT -i %s -m mark ! --mark %d -j DROP 2>/dev/null" % (vif, sliceid) +cmd_iptables_del_out = "/sbin/iptables -D OUTPUT -o %s -m mark ! --mark %d -j DROP 2>/dev/null" % (vif, sliceid) + +os.system(cmd_iptables_del_in) +os.system(cmd_iptables_del_out) diff --git a/exec/vif_up b/exec/vif_up new file mode 100755 index 0000000..2829b58 --- /dev/null +++ b/exec/vif_up @@ -0,0 +1,159 @@ +#!/usr/bin/python +# VSYS script to configure per-slice virtual network interfaces from the root slice +# Thom Haddow - 06/10/09 +# +# Gets slice name as argv[1] +# Takes remaining args on stdin: +# - Interface name (eg [tun|tap]-) +# - IP address (eg 1.2.3.4) +# - Netmask (as int, e.g. 24) +# - Followed by options as name=value pairs + +import sys +import pwd +import re +import socket +import struct +import os +import string + +vsys_config_dir = "/etc/planetlab/vsys-attributes" + +if len(sys.argv) != 2: sys.exit(1) + +# VSYS scripts get slicename as $1 +slicename=sys.argv[1] +sliceid = pwd.getpwnam(slicename).pw_uid + +netblock_config=os.path.join(vsys_config_dir,slicename,"vsys_vnet") + +# Read netblock allocation file +base = None + +for netblock in open(netblock_config,'r'): + base, mask = netblock.split('/') + +if base is None: + print >>sys.stderr, "Could not find entry for slice %s in netblock config file %s" % (slicename, netblock_config) + sys.exit(1) + + +### Read args from stdin +arglines = map(string.strip, sys.stdin.readlines()) + +if len(arglines)<3: + print >>sys.stderr, "Insufficient argument lines." + sys.exit(1) + +vif = arglines[0] # interface name +vip = arglines[1] # IP +vmask = int(arglines[2]) # netmask as int + +# Create options list +if len(arglines)>3: + options = arglines[3:] +else: + options = [] + +# Convert network base addr to int format by unpacking as 32bit net-ordered long (!L) +base_int = struct.unpack('!L',socket.inet_aton(base))[0] +mask = int(mask) + + +### Validate args +# Validate interface name +if len(vif)>16: + print >>sys.stderr, "Interface name %s invalid"%(vif) + sys.exit(1) + +if re.match(r'(tun|tap)%d-\d+' % sliceid, vif ) is None: + print >>sys.stderr, "Interface name %s does not match slice id %d."%(vif, sliceid) + sys.exit(1) + + + +# Validate requested IP and convert to int format. +try: + vip_int = struct.unpack('!L',socket.inet_aton(vip))[0] +except socket.error: + print >>sys.stderr, "Invalid IP: %s" % vip + sys.exit(1) + +# Check IP is in netblock +if (vip_int>>(32-mask)) != (base_int>>(32-mask)): + print >>sys.stderr, "Requested IP %s not in netblock %s/%d" % (vip,base,mask) + sys.exit(1) + +# TODO. Check IP is not in use? + +# Validate mask: Check requested mask is sane and within our netblock +if vmask>32 or vmask <8: + print >>sys.stderr, "Requested netmask /%d is invalid" %(vmask) + sys.exit(1) + +if vmask>sys.stderr, "Requested netmask /%d larger than allocation /%d" %(vmask, mask) + sys.exit(1) + + + +### Process options + +opt_txqueuelen = None +opt_rp_filter = None + + +for optionline in options: + if len(optionline)==0: continue + try: + opt, val = optionline.split('=') + except: + print >>sys.stderr, "Bad option line: \"%s\"" % (optionline) + sys.exit(1) + + if opt=="rp_filter": + if val=="0": + opt_rp_filter="0" + elif val=="1": + opt_rp_filter="1" + else: + print >>sys.stderr, "rp_filter value invalid: \"%s\"" % (val) + sys.exit(1) + + elif opt=="txqueuelen": + intval = int(val) + if intval<1 or intval>10000: + print >>sys.stderr, "txqueuelen value %s out of range 1-10000" % (val) + sys.exit(1) + opt_txqueuelen = intval + + else: + print >>sys.stderr, "Unknown option: \"%s\"" % (opt) + sys.exit(1) + + + +### Configure interface + +if opt_txqueuelen is None: + cmd_ifconfig = "/sbin/ifconfig %s %s/%d" % (vif, vip, vmask) +else: + cmd_ifconfig = "/sbin/ifconfig %s %s/%d txqueuelen %d" % (vif, vip, vmask, opt_txqueuelen) + +os.system(cmd_ifconfig) + +# Add iptables rules (Clearing old ones first, if they exist) +cmd_iptables_in = "/sbin/iptables -A INPUT -i %s -m mark -m state --state NEW ! --mark %d -j DROP" % (vif, sliceid) +cmd_iptables_del_in = "/sbin/iptables -D INPUT -i %s -m mark -m state --state NEW ! --mark %d -j DROP 2>/dev/null" % (vif, sliceid) +cmd_iptables_out = "/sbin/iptables -A OUTPUT -o %s -m state --state NEW -m mark ! --mark %d -j DROP" % (vif, sliceid) +cmd_iptables_del_out = "/sbin/iptables -D OUTPUT -o %s -m state --state NEW -m mark ! --mark %d -j DROP 2>/dev/null" % (vif, sliceid) + +os.system(cmd_iptables_del_in) +os.system(cmd_iptables_in) +os.system(cmd_iptables_del_out) +os.system(cmd_iptables_out) + +# Process additional options +if opt_rp_filter is not None: + rp_cmd = "/sbin/sysctl net.ipv4.conf.%s.rp_filter=%s" % (vif, opt_rp_filter) + os.system(rp_cmd) diff --git a/fd_bmsocket.c b/fd_bmsocket.c new file mode 100644 index 0000000..6a830ed --- /dev/null +++ b/fd_bmsocket.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include "fdpass.h" + +#define MAX_BUFSIZE (32*1024*1024) + +/*------------------------------------------------------------------*/ +void +receive_argument(int control_fd, int *rcvbuf, int *sndbuf) +{ + if (recv(control_fd, rcvbuf, sizeof(int), 0) != sizeof(int)) { + fprintf(stderr, "receiving the first argument failed\n"); + exit(-1); + } + if (recv(control_fd, sndbuf, sizeof(int), 0) != sizeof(int)) { + fprintf(stderr, "receiving the first argument failed\n"); + exit(-1); + } +} +/*------------------------------------------------------------------*/ +int +main(int argc, char *argv[]) +{ + int control_channel_fd, magic_socket; + int rcvbufsize = 0, sndbufsize = 0; + + if (argc < 3) { + printf("This script is called by vsys.\n"); + exit(1); + } + + sscanf(argv[2],"%d", &control_channel_fd); + + /* receive paramaters: rcvbufsize and sndbufsize */ + receive_argument(control_channel_fd, &rcvbufsize, &sndbufsize); + if (rcvbufsize > MAX_BUFSIZE) + rcvbufsize = MAX_BUFSIZE; + if (sndbufsize > MAX_BUFSIZE) + sndbufsize = MAX_BUFSIZE; + + magic_socket = socket(AF_INET, SOCK_STREAM, 0); + if (magic_socket == -1) { + fprintf(stderr, "Error creating socket: %d\n", errno); + exit(1); + } + + /* buffer size <= 0 means we should ignore the parameter */ + if (rcvbufsize > 0) { + if (setsockopt(magic_socket, + SOL_SOCKET, + SO_RCVBUFFORCE, + &rcvbufsize, sizeof(unsigned int))) { + fprintf(stderr, "Error calling setsockopt for RCVBUFFORCE: %d\n", + errno); + exit(1); + } + } + if (sndbufsize > 0) { + if (setsockopt(magic_socket, + SOL_SOCKET, + SO_SNDBUFFORCE, + &sndbufsize, sizeof(unsigned int))) { + fprintf(stderr, "Error calling setsockopt for SNDBUFFORCE: %d\n", + errno); + exit(1); + } + } + + send_fd(control_channel_fd, magic_socket); +} diff --git a/fd_fusemount.c b/fd_fusemount.c new file mode 100644 index 0000000..5c29c98 --- /dev/null +++ b/fd_fusemount.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include "fdpass.h" + +unsigned int rcvbuf = 16*1024*1024; +unsigned int arg_length = 128; +unsigned int mountflags = MS_NODEV | MS_NOSUID; + +FILE *logfd; + +void receive_argument(int control_channel_fd, char *source) { + int received; + received=recv(control_channel_fd, source, arg_length, 0); + if (received 1023) { + printf ("Got control_channel_fd = %d\n", control_channel_fd); + exit(1); + } + + receive_argument(control_channel_fd, source); + receive_argument(control_channel_fd, target); + receive_argument(control_channel_fd, filesystemtype); + receive_argument(control_channel_fd, data); + + mount_fd = receive_fd (control_channel_fd); + + if (mount_fd < 2) { + printf("mount_fd = %d\n", mount_fd); + exit(1); + } + + set_magic_fd(data, mount_fd); + + + check_source(source); + check_target(target); + check_fstype(filesystemtype); + + sprintf(slice_target,"/vservers/%s/%s", slice_name, target); + + fprintf(logfd, "Mount fd: %d Source: %s slice_target: %s fstype: %s mountflags: %d data: %s\n", mount_fd, source, slice_target, filesystemtype, mountflags, data); + + if (!mount(source, slice_target, filesystemtype, mountflags, data)) { + send_fd(control_channel_fd, mount_fd); + } + else { + printf ("Error executing mount\n"); + exit(1); + } + + close(logfd); + return 0; +} diff --git a/fd_tos.c b/fd_tos.c new file mode 100644 index 0000000..970bafa --- /dev/null +++ b/fd_tos.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +#include "fdpass.h" + +/* + * Definitions for IP type of service + */ +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_MINCOST 0x02 +#define IPTOS_NORMALSVC 0x00 + +static void receive_argument(int control_fd, int *TOS_value) +{ + if (recv(control_fd, TOS_value, sizeof(int), 0) != sizeof(int)) { + fprintf(stderr, "receiving the IP_TOS argument failed\n"); + exit(-1); + } +} + +int main(int argc, char *argv[]) +{ + int control_channel_fd, magic_socket; + int TOS_value = IPTOS_NORMALSVC; + + if (argc < 3) { + printf("This script is called by vsys.\n"); + exit(1); + } + + control_channel_fd = atoi(argv[2]); + + /* receive IP_TOS paramater */ + receive_argument(control_channel_fd, &TOS_value); + + switch (TOS_value) + { + case IPTOS_NORMALSVC: + case IPTOS_MINCOST: + case IPTOS_RELIABILITY: + case IPTOS_THROUGHPUT: + case IPTOS_LOWDELAY: + break; + default: + fprintf(stderr, "IP_TOS value not known: %d\n", errno); + exit(1); + } + + magic_socket = receive_fd(control_channel_fd); + if (magic_socket == -1) { + fprintf(stderr, "Error creating socket: %d\n", errno); + exit(1); + } + + if (setsockopt(magic_socket, IPPROTO_IP, IP_TOS, &TOS_value, sizeof(TOS_value)) < 0 ) { + fprintf(stderr, "Error calling setsockopt for IPPROTO_IP: %d\n", errno); + exit(1); + } + + send_fd(control_channel_fd, magic_socket); +} diff --git a/fd_tuntap.c b/fd_tuntap.c new file mode 100644 index 0000000..45e66be --- /dev/null +++ b/fd_tuntap.c @@ -0,0 +1,135 @@ +/* fd_tuntap.c: VSYS script to allocate slice-local tuntap interfaces. + * Thom Haddow - 06/09/09 + * + * Reads interface type from local control unix socket, replies with fd for new + * (unconfigured) tuntap interface. VSYS client can get interface name with + * TUNGETIFF ioctl. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int send_vif_fd(int sock_fd, int vif_fd, char *vif_name) +{ + int retval; + struct msghdr msg; + struct cmsghdr *p_cmsg; + struct iovec vec; + size_t cmsgbuf[CMSG_SPACE(sizeof(vif_fd)) / sizeof(size_t)]; + int *p_fds; + + + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + p_cmsg = CMSG_FIRSTHDR(&msg); + p_cmsg->cmsg_level = SOL_SOCKET; + p_cmsg->cmsg_type = SCM_RIGHTS; + p_cmsg->cmsg_len = CMSG_LEN(sizeof(vif_fd)); + p_fds = (int *) CMSG_DATA(p_cmsg); + *p_fds = vif_fd; + msg.msg_controllen = p_cmsg->cmsg_len; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + + /* Send the interface name as the iov */ + vec.iov_base = vif_name; + vec.iov_len = strlen(vif_name)+1; + + while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR); + if (retval != 1) { + perror("sending file descriptor"); + return -1; + } + return 0; +} + + + +int main(int argc, char *argv[]) +{ + int control_channel_fd; + int tap_fd; + int slice_uid; + char if_name[IFNAMSIZ]; + int if_type; + struct ifreq ifr; + struct passwd *pwd; + + if(argc < 3) { + printf("This script is called by vsys.\n"); + exit(-1); + } + + + /* Get slice UID and control channel fd from VSYS args */ + pwd = getpwnam(argv[1]); + if(pwd==NULL) { + perror("Failed to lookup UID"); + exit(-1); + } + slice_uid = pwd->pw_uid; + sscanf(argv[2],"%d", &control_channel_fd); + + + + /* Get type param from control channel. */ + if(recv(control_channel_fd, &if_type, sizeof(int), 0) != sizeof(int)) { + perror("fd_tuntap: Failed to read from control channel"); + exit(-1); + } + + + /* Generate basename for interface */ + if(if_type==IFF_TUN) { + sprintf(if_name, "tun%d-%%d", slice_uid); + } + else if(if_type==IFF_TAP) { + sprintf(if_name, "tap%d-%%d", slice_uid); + } + else { /* TODO: Might also want to allow the other types? */ + fprintf(stderr, "fd_tuntap: %d is not a valid interface type",if_type); + exit(-1); + } + + /* Open tun device */ + if( (tap_fd = open("/dev/net/tun", O_RDWR)) < 0 ) { + system("modprobe tun"); + if( (tap_fd = open("/dev/net/tun", O_RDWR)) < 0 ) { + perror("ERROR: tun_alloc():open(/dev/net/tun)"); + exit(-1); + } + } + + + /* Set interface type */ + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = if_type; + strncpy(ifr.ifr_name, if_name, IFNAMSIZ); + + if( ioctl(tap_fd, TUNSETIFF, (void *) &ifr) < 0 ) { + close(tap_fd); + perror("fd_tuntap: Failed to set tun type"); + } + + /* Read initialised interface name */ + strcpy(if_name, ifr.ifr_name); + + + /* Send tap_fd to slice */ + send_vif_fd(control_channel_fd, tap_fd, if_name); + + return 0; +} diff --git a/fd_udpsocket.c b/fd_udpsocket.c new file mode 100644 index 0000000..8fa6651 --- /dev/null +++ b/fd_udpsocket.c @@ -0,0 +1,75 @@ +/* Copy of fd_bmsocket.c that creates UDP sockets instead of TCP sockets. */ + +#include +#include +#include +#include +#include +#include "fdpass.h" + +#define MAX_BUFSIZE (32*1024*1024) + +/*------------------------------------------------------------------*/ +void +receive_argument(int control_fd, int *rcvbuf, int *sndbuf) +{ + if (recv(control_fd, rcvbuf, sizeof(int), 0) != sizeof(int)) { + fprintf(stderr, "receiving the first argument failed\n"); + exit(-1); + } + if (recv(control_fd, sndbuf, sizeof(int), 0) != sizeof(int)) { + fprintf(stderr, "receiving the first argument failed\n"); + exit(-1); + } +} +/*------------------------------------------------------------------*/ +int +main(int argc, char *argv[]) +{ + int control_channel_fd, magic_socket; + int rcvbufsize = 0, sndbufsize = 0; + + if (argc < 3) { + printf("This script is called by vsys.\n"); + exit(1); + } + + sscanf(argv[2],"%d", &control_channel_fd); + + /* receive paramaters: rcvbufsize and sndbufsize */ + receive_argument(control_channel_fd, &rcvbufsize, &sndbufsize); + if (rcvbufsize > MAX_BUFSIZE) + rcvbufsize = MAX_BUFSIZE; + if (sndbufsize > MAX_BUFSIZE) + sndbufsize = MAX_BUFSIZE; + + magic_socket = socket(AF_INET, SOCK_DGRAM, 0); + if (magic_socket == -1) { + fprintf(stderr, "Error creating socket: %d\n", errno); + exit(1); + } + + /* buffer size <= 0 means we should ignore the parameter */ + if (rcvbufsize > 0) { + if (setsockopt(magic_socket, + SOL_SOCKET, + SO_RCVBUFFORCE, + &rcvbufsize, sizeof(unsigned int))) { + fprintf(stderr, "Error calling setsockopt for RCVBUFFORCE: %d\n", + errno); + exit(1); + } + } + if (sndbufsize > 0) { + if (setsockopt(magic_socket, + SOL_SOCKET, + SO_SNDBUFFORCE, + &sndbufsize, sizeof(unsigned int))) { + fprintf(stderr, "Error calling setsockopt for SNDBUFFORCE: %d\n", + errno); + exit(1); + } + } + + send_fd(control_channel_fd, magic_socket); +} diff --git a/fdpass.c b/fdpass.c new file mode 100644 index 0000000..5548064 --- /dev/null +++ b/fdpass.c @@ -0,0 +1,93 @@ +// Modified version of library functions in FUSE +// + +#include +#include +#include +#include +#include +#include +#include +#include + +int send_fd(int sock_fd, int fd) +{ + int retval; + struct msghdr msg; + struct cmsghdr *p_cmsg; + struct iovec vec; + size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)]; + int *p_fds; + char sendchar = 0; + + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + p_cmsg = CMSG_FIRSTHDR(&msg); + p_cmsg->cmsg_level = SOL_SOCKET; + p_cmsg->cmsg_type = SCM_RIGHTS; + p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + p_fds = (int *) CMSG_DATA(p_cmsg); + *p_fds = fd; + msg.msg_controllen = p_cmsg->cmsg_len; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + /* "To pass file descriptors or credentials you need to send/read at + * least one byte" (man 7 unix) */ + vec.iov_base = &sendchar; + vec.iov_len = sizeof(sendchar); + while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR); + if (retval != 1) { + perror("sending file descriptor"); + return -1; + } + return 0; +} + + +/* return value: + * >= 0 => fd + * -1 => error + */ +int receive_fd(int fd) +{ + struct msghdr msg; + struct iovec iov; + char buf[1]; + int rv; + size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)]; + struct cmsghdr *cmsg; + + iov.iov_base = buf; + iov.iov_len = 1; + + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + /* old BSD implementations should use msg_accrights instead of + * msg_control; the interface is different. */ + msg.msg_control = ccmsg; + msg.msg_controllen = sizeof(ccmsg); + + while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR); + if (rv == -1) { + perror("recvmsg"); + return -1; + } + if(!rv) { + /* EOF */ + return -1; + } + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg->cmsg_type == SCM_RIGHTS) { + fprintf(stderr, "got control message of unknown type %d\n", + cmsg->cmsg_type); + return -1; + } + return *(int*)CMSG_DATA(cmsg); +} + diff --git a/fdpass.h b/fdpass.h new file mode 100644 index 0000000..f00a6fb --- /dev/null +++ b/fdpass.h @@ -0,0 +1,5 @@ +// Modified version of library functions in FUSE +// +int send_fd(int sock_fd, int fd); +int receive_fd(int fd); + diff --git a/support/tun_test.c b/support/tun_test.c new file mode 100644 index 0000000..947019f --- /dev/null +++ b/support/tun_test.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include + +#include "tunalloc.h" + +int main(void) +{ + printf("Allocating tap device via VSYS\n"); + + char if_name[IFNAMSIZ]; + + int tun_fd = tun_alloc(IFF_TAP, if_name); + + printf("Allocated tap device: %s fd=%d\n", if_name, tun_fd); + + printf("Sleeping for 120 secs...\n"); + + sleep(120); + + printf("Closing\n"); + + return 0; +} diff --git a/support/tunalloc.c b/support/tunalloc.c new file mode 100644 index 0000000..6adcf24 --- /dev/null +++ b/support/tunalloc.c @@ -0,0 +1,95 @@ +/* Slice-side code to allocate tuntap interface in root slice + * Based on bmsocket.c + * Thom Haddow - 08/10/09 + * + * Call tun_alloc() with IFFTUN or IFFTAP as an argument to get back fd to + * new tuntap interface. Interface name can be acquired via TUNGETIFF ioctl. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define VSYS_TUNTAP "/vsys/fd_tuntap.control" + +/* Reads vif FD from "fd", writes interface name to vif_name, and returns vif FD. + * vif_name should be IFNAMSIZ chars long. */ +int receive_vif_fd(int fd, char *vif_name) +{ + struct msghdr msg; + struct iovec iov; + int rv; + size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)]; + struct cmsghdr *cmsg; + + /* Use IOV to read interface name */ + iov.iov_base = vif_name; + iov.iov_len = IFNAMSIZ; + + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + /* old BSD implementations should use msg_accrights instead of + * msg_control; the interface is different. */ + msg.msg_control = ccmsg; + msg.msg_controllen = sizeof(ccmsg); + + while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR); + if (rv == -1) { + perror("recvmsg"); + return -1; + } + if(!rv) { + /* EOF */ + return -1; + } + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg->cmsg_type == SCM_RIGHTS) { + fprintf(stderr, "got control message of unknown type %d\n", + cmsg->cmsg_type); + return -1; + } + return *(int*)CMSG_DATA(cmsg); +} + + +int tun_alloc(int iftype, char *if_name) +{ + int control_fd; + struct sockaddr_un addr; + int remotefd; + + control_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (control_fd == -1) { + perror("Could not create UNIX socket\n"); + exit(-1); + } + + memset(&addr, 0, sizeof(struct sockaddr_un)); + /* Clear structure */ + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, VSYS_TUNTAP, + sizeof(addr.sun_path) - 1); + + if (connect(control_fd, (struct sockaddr *) &addr, + sizeof(struct sockaddr_un)) == -1) { + perror("Could not connect to Vsys control socket"); + exit(-1); + } + + /* passing type param */ + if (send(control_fd, &iftype, sizeof(iftype), 0) != sizeof(iftype)) { + perror("Could not send paramater to Vsys control socket"); + exit(-1); + } + + remotefd = receive_vif_fd(control_fd, if_name); + return remotefd; +} diff --git a/support/tunalloc.h b/support/tunalloc.h new file mode 100644 index 0000000..3e5caae --- /dev/null +++ b/support/tunalloc.h @@ -0,0 +1,6 @@ +#ifndef _TUNALLOC_H +#define _TUNALLOC_H + +int tun_alloc(int iftype, char *if_name); + +#endif diff --git a/svn-commit.tmp b/svn-commit.tmp deleted file mode 100644 index d04acbe..0000000 --- a/svn-commit.tmp +++ /dev/null @@ -1,5 +0,0 @@ -First checkin. Vsys scripts will live here in the future. - ---This line, and those below, will be ignored-- - -A vsys-factory diff --git a/vsys-factory.spec b/vsys-factory.spec deleted file mode 100644 index 8aec52b..0000000 --- a/vsys-factory.spec +++ /dev/null @@ -1,57 +0,0 @@ -# -# Vsys filesystem -# -# RPM spec file -# -# $Id: vsys-factory.spec 9786 2008-07-02 08:54:09Z thierry $ -# - -%define name vsys-factory -%define version 0.7 -%define taglevel 0 - -%define release %{taglevel}%{?pldistro:.%{pldistro}}%{?date:.%{date}} - -Vendor: PlanetLab -Packager: PlanetLab Central -Distribution: PlanetLab %{plrelease} -URL: %(echo %{url} | cut -d ' ' -f 2) - -Summary: Vsys factory scripts -Name: %{name} -Version: %{version} -Release: %{release} -License: GPL -Group: System Environment/Kernel -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot -Requires: vsys - -Source0: vsys-factory-%{version}.tar.gz - -%description -Vsys scripts for privileged operations on PlanetLab. These scripts are defined by maintainers of various components, -to which users require privileged access. - -%prep -%setup - -%build -rm -rf $RPM_BUILD_ROOT -make - -%install -mkdir -p $RPM_BUILD_ROOT/vsys -cp exec/* $RPM_BUILD_ROOT/vsys - -%clean -rm -rf $RPM_BUILD_ROOT - -%files -/vsys/* - -%post - -%postun - -%changelog - diff --git a/vsys-factory.spec.orig b/vsys-factory.spec.orig index 13e5393..edc8959 100644 --- a/vsys-factory.spec.orig +++ b/vsys-factory.spec.orig @@ -3,10 +3,10 @@ # # RPM spec file # -# $Id: vsys.spec 9786 2008-07-02 08:54:09Z thierry $ +# $Id$ # -%define url $URL: svn+ssh://sapanb@poppins/svn/vsys/trunk/vsys.spec $ +%define url $URL$ %define name vsys %define version 0.8 diff --git a/vsys-scripts.spec b/vsys-scripts.spec new file mode 100644 index 0000000..12bfaf1 --- /dev/null +++ b/vsys-scripts.spec @@ -0,0 +1,124 @@ +# +# Vsys filesystem +# +# RPM spec file +# +# $Id$ +# + +%define name vsys-scripts +%define version 0.95 +%define taglevel 19 + +%define release %{taglevel}%{?pldistro:.%{pldistro}}%{?date:.%{date}} + +Vendor: PlanetLab +Packager: PlanetLab Central +Distribution: PlanetLab %{plrelease} +URL: %(echo %{url} | cut -d ' ' -f 2) + +Summary: Vsys scripts scripts +Name: %{name} +Version: %{version} +Release: %{release} +License: GPL +Group: System Environment/Kernel +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot +Requires: vsys + +Source0: vsys-scripts-%{version}.tar.gz + +%description +Vsys scripts for privileged operations on PlanetLab. These scripts are defined by maintainers of various components, +to which users require privileged access. + +%prep +%setup + +%build +rm -rf $RPM_BUILD_ROOT +make + +%install +mkdir -p $RPM_BUILD_ROOT/vsys +cp exec/* $RPM_BUILD_ROOT/vsys + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +/vsys/* + +%post + +%postun + +%changelog +* Thu Jul 01 2010 Baris Metin - vsys-scripts-0.95-19 +- change dotsshmount's mount point + +* Fri Jun 25 2010 Sapan Bhatia - vsys-scripts-0.95-18 +- Switch fd_tuntap to 2.6.27. + +* Fri Apr 30 2010 Sapan Bhatia - vsys-scripts-0.95-17 +- Tweak to if_port_forward script. + +* Fri Apr 30 2010 Sapan Bhatia - vsys-scripts-0.95-16 +- Added if_port_forward script to enable slices to forward ports for their +- allocated IP addresses. + +* Wed Mar 03 2010 Talip Baris Metin - vsys-scripts-0.95-15 +- - hostname filter and updated documentation for ipfw scripts - Marta +- - vsys script for letting users to set the Type of Services(ToS) byte in the IPv4 header - Caglar +- (if this is not needed anymore please remove and tag again) + +* Thu Jan 21 2010 Sapan Bhatia - vsys-scripts-0.95-14 +- Tweak to Thom Haddow's script to let a slice see packets related to +- existing connections. + +* Mon Jan 11 2010 Thierry Parmentelat - vsys-scripts-0.95-13 +- ipfw frontend has a rules cleanup feature +- ongoing work around vif_up +- new script 'run_with_devices' + +* Thu Nov 26 2009 Thierry Parmentelat - vsys-scripts-0.95-12 +- improve usability of the ipfw backend + +* Wed Oct 28 2009 Sapan Bhatia - vsys-scripts-0.95-11 +- * Load the tun module if it is not already loaded +- * Fixed a bug in fd_tuntap + +* Tue Oct 27 2009 Sapan Bhatia - vsys-scripts-0.95-10 +- Adds Thom Haddow's tun/tap changes. I omitted a change in my previous commit. + +* Tue Oct 27 2009 Sapan Bhatia - vsys-scripts-0.95-9 +- This update adds Thom Haddow's custom tun/tap-device scripts. + +* Wed Sep 09 2009 Faiyaz Ahmed - vsys-scripts-0.95-8 +- Added script to create UDP sockets instead of TCP sockets w/ big buffers + +* Mon Sep 07 2009 Thierry Parmentelat - vsys-scripts-0.95-7 +- minor changes in sliceip and ipfw, new reserve_udp_port + +* Mon Aug 03 2009 Sapan Bhatia - vsys-scripts-0.95-6 +- Added reserve_tcp_port and Giovanni's slice_ip tool. + +* Tue Jul 07 2009 Thierry Parmentelat - vsys-scripts-0.95-5 +- tweak in ipfw-be for in-node emulation + +* Thu Jun 11 2009 Thierry Parmentelat - vsys-scripts-0.95-4 +- Added the ipfw backend script + +* Fri Jun 05 2009 Faiyaz Ahmed - vsys-scripts-0.95-3 +- rsync authlogs to ~/ + +* Mon May 18 2009 Sapan Bhatia - vsys-scripts-0.95-2 +- Security update. umount had an issue that might have been exploited. + +* Thu May 14 2009 Sapan Bhatia - vsys-scripts-0.95-1 +- Adding port reservation script (goodfences) +- Modifying umount to be more (or less, depending on your point of view) robust + + + +%define module_current_branch 0.7