adding sliceip on behalf of Giovanni
Thierry Parmentelat [Sun, 26 Jul 2009 05:44:58 +0000 (05:44 +0000)]
sliceip [new file with mode: 0755]

diff --git a/sliceip b/sliceip
new file mode 100755 (executable)
index 0000000..b34493a
--- /dev/null
+++ b/sliceip
@@ -0,0 +1,480 @@
+#!/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
+# fronted script in the slice environment. 
+#
+# Fronted usage:
+# sliceip enable <interface> 
+#   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 <interface> 
+#   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 <destination> via <gateway> dev <interface>
+#   This command adds a rule to reach <destination> through the host
+#   <gateway> that can be reached on the interface <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 <slice_name>
+# 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
+
+       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 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 [[ $old_ip != $ip ]]; then
+                       #remove the rule for the old ip
+                       exec "iptables -t nat -D POSTROUTING -o $interface -j SNAT --to-source $old_ip -m mark --mark $nid"
+                       #insert the new rule                    
+                       exec "iptables -t nat -A 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 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
+                       echo EOF                        
+                       exit 1
+               fi
+       fi
+
+}
+
+# decides wich id to use for the slice 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 /${regex}/d $filename" > $filename.tmp
+       exec "mv $filename.tmp $filename"
+       
+
+}
+
+# get the slice-specific routing table name
+function get_table_name(){
+       local sliver=$1;
+       echo "${sliver}_slcip"
+}
+
+function clean_iproute_conf(){
+       while grep "slcip" $RT_TABLES >/dev/null 2>&1; do
+               remove_line $RT_TABLES "slcip"
+       done
+
+       rm ${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 -A OUTPUT -m mark --mark $sliver_nid -j MARK --set-mark $temp_nid"
+
+               #Here we ask VNET+ to set the netfilter mark for the remaining packets (to the slice-id) 
+               exec "iptables -t mangle -A OUTPUT -j MARK -m mark ! --mark $temp_nid --copy-xid 0x00"
+       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 OUTPUT -m owner --uid-owner $sliver_nid -j MARK --set-mark $sliver_nid"
+       fi
+       
+       #Here the netfilter mark is restored to the slice-id value for the "strange packets"
+       exec "iptables -t mangle -I POSTROUTING 1 -m mark --mark $temp_nid -j MARK --set-mark $sliver_nid"
+
+       #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
+}
+
+# removes 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 OUTPUT -m mark --mark $sliver_nid -j MARK --set-mark $temp_nid"
+
+               #removes the rules that asks VNET+ to set the netfilter mark to the slice-id            
+               exec "iptables -t mangle -D OUTPUT -m mark ! --mark $temp_nid -j MARK --copy-xid 0x00"
+       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 OUTPUT -m owner --uid-owner $sliver_nid -j MARK --set-mark $sliver_nid"
+       fi
+
+       exec "iptables -t mangle -D POSTROUTING -m mark --mark $temp_nid -j MARK --set-mark $sliver_nid"
+
+       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"
+
+}
+
+# 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 slice packets
+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
+}
+
+
+
+# checks 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
+
+# separates the first word of the line from the others
+command=`echo ${line%% *}`
+rest=`echo ${line#* }`
+
+case "$command" in
+  enable)
+       logger "slcip command received from $sliver: $line"     
+       enable_interface $sliver "$rest"
+       ;;
+
+  disable)
+       logger "slcip command received from $sliver: $line"     
+       disable_interface $sliver "$rest"
+       ;;
+
+  *)
+       logger "slcip command received from $sliver: $line"
+       table_sliver=`get_table_name $sliver`
+
+       # adds the rule
+       if ! grep "$table_sliver" $RT_TABLES >/dev/null 2>&1; then
+               echo "Error. The slice routing table is not defined. Execute sliceip enable <interface>."
+       else 
+               exec "ip $line table $table_sliver"
+       fi  
+       ;;
+
+esac
+
+# make the frontend quit
+echo "EOF"
+
+exit 0
+
+
+