4 # Copyright (C) 2009 UniNa
7 # This is a backend script to be used with
8 # the vsys system. It allows to define and
9 # manage slice-specific routing tables.
10 # The routing rules added to these routing
11 # tables affect only the respective slices.
13 # This script is meant to be called from a
14 # fronted script in the slice environment.
17 # sliceip enable <interface>
18 # This is the first command to issue; it enables the use
19 # of the interface for the slice. This command has to be issued
20 # for each interface involved in the slice-specific routing rules.
22 # sliceip disable <interface>
23 # This is to disable the use of the interface for the slice.
26 # This command shows the current routing rules of the
27 # slice-specific routing table
29 # sliceip route add to <destination> via <gateway> dev <interface>
30 # This command adds a rule to reach <destination> through the host
31 # <gateway> that can be reached on the interface <interface>
34 # sliceip in general supports all the commands of the "ip" command of
35 # the "iproute2" suite. Refer to ip manpage for further information.
38 # sliceip route add to 143.225.229.142 via 192.168.8.2 dev ath0
39 # sliceip route add default via 10.1.1.1 dev ath0
41 # If you want to test the script from the root context you need
42 # to call it as vsys would do:
43 # sliceip <slice_name>
44 # Then you can issue to its standard input the commands previously explained
45 # (but you have to remove the initial "sliceip"). Ex.:
46 # route add to 143.225.229.142 via 192.168.8.2 dev ath0
49 PATH=/bin:/usr/bin:/sbin:/usr/sbin
51 # sliver wich called the script
55 RT_TABLES="/etc/iproute2/rt_tables"
56 INT_FILE="/tmp/slcroute_ifns"
58 # routing tables from 100 to 255 are used
63 #this script can also work on a Linux machine, in wich case
64 #it enables user-specific routing tables
65 LINUX_ENV=0 # 0 - Linux host;
66 # does not work for the icmp protocol
68 PLANET_ENV=1 # 1 - PlanetLab context (slice-specific routing)
70 #select the PlanetLab environment
71 ENVIRONMENT=$PLANET_ENV
74 # enable an interface for the slice specific routing rules
75 # and initialise the slice routing table
76 function enable_interface(){ # $1 calling sliver; $2 interface to enable
79 local ip=`get_address $interface`
81 if [[ "" == $ip ]]; then
82 echo "Failed in getting address of $interface"
86 local nid=`get_nid $sliver`
88 local num_active=`get_num_activated $sliver`
90 if ! is_active $sliver $interface; then
92 if [[ $num_active == 0 ]]; then
93 #define the routing table for the slice and set some iptables rules in order to mark user's packets;
97 #Adds an SNAT rule to set the source IP address of packets that are about to go out through $interface.
98 #The kernel sets the source address of packets when the first routing process happens (following the rules
99 #of the main routing table); when the rerouting process happens (following the user/slice specific rules)
100 #the source address is not modified. Consequently, we need to add this rule to change the source ip address
101 #to the $interface source address.
102 exec "iptables -t nat -A POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid"
104 set_active $sliver $interface $ip
107 local old_ip=`get_ip_from_file $sliver $interface`
109 if [[ $old_ip != $ip ]]; then
110 #remove the rule for the old ip
111 exec "iptables -t nat -D POSTROUTING -o $interface -j SNAT --to-source $old_ip -m mark --mark $nid"
113 exec "iptables -t nat -A POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid"
115 set_deactivated $sliver $interface $old_ip
116 set_active $sliver $interface $ip
124 # disable the interface for the slice-specific routing rules
125 function disable_interface(){
129 local ip=`get_ip_from_file $sliver $interface`
131 local nid=`get_nid $sliver`
133 if is_active $sliver $interface >/dev/null 2>&1; then
134 exec "iptables -t nat -D POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid"
136 local num_ifn_on=`get_num_activated $sliver`
138 if [[ $num_ifn_on == 1 ]]; then
142 set_deactivated $sliver $interface
147 # wrapper function used to execute system commands
151 if ! [[ $command ]]; then
152 echo "Error in exec. No argument."
156 if [[ $DEBUG == 1 ]]; then echo "Error executing \"$1\""; fi
164 # decides wich id to use for the slice routing table
165 function get_table_id(){
170 while [[ $k < 255 ]] && grep $k $RT_TABLES >/dev/null 2>&1 ; do
174 if [[ $k == 255 ]]; then
175 logm "No routing tables available. Exiting."
183 # create the slice-specifig routing table
184 function create_table(){
187 local table_name=`get_table_name $sliver`
188 local table_id=`get_table_id $sliver`
189 local temp_nid=`get_temp_nid $sliver`
191 if ! grep $table_name $RT_TABLES > /dev/null 2>&1; then
192 echo "$table_id $table_name" >> $RT_TABLES
194 echo "WARNING: $table_name routing table already defined."
197 set_routes $sliver $table_name
202 # delete the slice-specific routing table
203 function delete_table(){
206 local table_name=`get_table_name $sliver`
207 local table_id=`get_nid $sliver`
208 local temp_nid=`get_temp_nid $sliver`
210 if ! grep $table_name $RT_TABLES > /dev/null 2>&1; then
213 exec "ip route flush table $table_name" >/dev/null 2>&1
214 unset_routes $sliver $table_name
215 remove_line "$RT_TABLES" "$table_name"
221 # remove a line from a file
222 function remove_line(){
226 #remove interface line from the file
228 exec "sed /${regex}/d $filename" > $filename.tmp
229 exec "mv $filename.tmp $filename"
234 # get the slice-specific routing table name
235 function get_table_name(){
237 echo "${sliver}_slcip"
240 function clean_iproute_conf(){
241 while grep "slcip" $RT_TABLES >/dev/null 2>&1; do
242 remove_line $RT_TABLES "slcip"
249 #get slice network id
254 # set the firewall rules. Mainly, it asks VNET+ to set the netfilter mark of packets belonging to the slice to the slice-id
255 # and then associates those packets with the routing table allocated for that slice.
256 function set_routes(){
260 local sliver_nid=`get_nid $sliver`
261 local temp_nid=`get_temp_nid $sliver`
263 if [ $ENVIRONMENT == $PLANET_ENV ]; then
264 #Linux kernel triggers a rerouting process, wich is needed to perfom slice-specific routing,
265 #if it sees that the netfilter mark has been altered in the iptables mangle chain.
266 #As VNET+ sets the netfilter mark of some packets in advance (to the slice-id value) before they enter the iptables mangle chain,
267 #we need to change it here (otherwise the rerouting process is not triggered for them).
268 exec "iptables -t mangle -A OUTPUT -m mark --mark $sliver_nid -j MARK --set-mark $temp_nid"
270 #Here we ask VNET+ to set the netfilter mark for the remaining packets (to the slice-id)
271 exec "iptables -t mangle -A OUTPUT -j MARK -m mark ! --mark $temp_nid --copy-xid 0x00"
272 elif [ $ENVIRONMENT == $LINUX_ENV ]; then
273 #the same in the case of a "plain" Linux box. In this case we do not use VNET+ but
274 #the owner module of iptables.
275 exec "iptables -t mangle -A OUTPUT -m owner --uid-owner $sliver_nid -j MARK --set-mark $sliver_nid"
278 #Here the netfilter mark is restored to the slice-id value for the "strange packets"
279 exec "iptables -t mangle -I POSTROUTING 1 -m mark --mark $temp_nid -j MARK --set-mark $sliver_nid"
281 #Set the routing for the slice to be applied following the rules in $table_name"
282 #for the slice packets...
283 exec "ip rule add fwmark $sliver_nid table $table_name"
284 #...and for the "strange packets"
285 exec "ip rule add fwmark $temp_nid table $table_name"
287 exec "ip route flush cache" >/dev/null 2>&1
290 # removes the firewall rules.
291 function unset_routes(){
295 local sliver_nid=`get_nid $sliver`
296 local temp_nid=`get_temp_nid $sliver`
298 if [ $ENVIRONMENT == $PLANET_ENV ]; then
299 #removes the rules for the netfilter marks (for the "strange packets")
300 exec "iptables -t mangle -D OUTPUT -m mark --mark $sliver_nid -j MARK --set-mark $temp_nid"
302 #removes the rules that asks VNET+ to set the netfilter mark to the slice-id
303 exec "iptables -t mangle -D OUTPUT -m mark ! --mark $temp_nid -j MARK --copy-xid 0x00"
304 elif [ $ENVIRONMENT == $LINUX_ENV ]; then
305 #removes the rules that asks the owner module of iptables to set the netfilter mark to the user-id
306 exec "iptables -t mangle -D OUTPUT -m owner --uid-owner $sliver_nid -j MARK --set-mark $sliver_nid"
309 exec "iptables -t mangle -D POSTROUTING -m mark --mark $temp_nid -j MARK --set-mark $sliver_nid"
311 exec "ip rule del fwmark $temp_nid table $table_name"
312 exec "ip rule del fwmark $sliver_nid table $table_name"
314 exec "ip route flush cache"
318 # get the ip address of an interface
319 function get_address(){
323 ip=`ifconfig $interface | grep inet\ addr | cut -d ":" -f 2 | cut -d " " -f 1`;
325 if valid_dotted_quad $ip; then
333 # get the temporary mark to be applied to the slice packets
334 function get_temp_nid(){
335 local sliver_nid=`get_nid $1`
336 local temp_nid=$((0x20000+$sliver_nid))
345 # get the name of the filename in wich we store information about
346 # the interfaces in use by the user
347 function get_filename_sliver(){
349 echo "${INT_FILE}-$sliver"
352 function set_active(){
357 local filename="${INT_FILE}-$sliver"
359 echo "$interface $ip" >> $filename
363 function set_deactivated(){
368 local filename=`get_filename_sliver $sliver`
370 remove_line $filename $interface
373 function is_active(){
377 local filename=`get_filename_sliver $sliver`
379 if grep $interface $filename >/dev/null 2>&1; then
386 function get_num_activated(){
389 local filename=`get_filename_sliver $sliver`
391 if ! [ -e $filename ]; then
394 wc -l $filename | cut -f 1 -d " ";
399 function get_ip_from_file(){
403 local filename=`get_filename_sliver $sliver`
405 cat $filename | grep $interface | cut -d " " -f 2
410 # checks ip addresses
411 function valid_dotted_quad(){
421 ""|*[!0-9]*) return 1; break ;; ## Segment empty or non-numeric char
422 *) [ $seg -gt 255 ] && return 2 ;;
426 return 3 ## Not 4 segments
435 # the script starts here
437 if [[ $sliver == "" ]]; then
438 echo "I need the first argument (the sliver name)";
442 # read a line from the vsys pipe
445 # separates the first word of the line from the others
446 command=`echo ${line%% *}`
447 rest=`echo ${line#* }`
451 logger "slcip command received from $sliver: $line"
452 enable_interface $sliver "$rest"
456 logger "slcip command received from $sliver: $line"
457 disable_interface $sliver "$rest"
461 logger "slcip command received from $sliver: $line"
462 table_sliver=`get_table_name $sliver`
465 if ! grep "$table_sliver" $RT_TABLES >/dev/null 2>&1; then
466 echo "Error. The slice routing table is not defined. Execute sliceip enable <interface>."
468 exec "ip $line table $table_sliver"
474 # make the frontend quit