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 # frontend 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 #create the sliceip netfilter chains
89 local nid=`get_nid $sliver`
90 local num_active=`get_num_activated $sliver`
92 if ! is_active $sliver $interface; then
93 if [[ $num_active == 0 ]]; then
94 #define the routing table for the slice and set some iptables rules in order to mark user's packets;
98 #Adds an SNAT rule to set the source IP address of packets that are about to go out through $interface.
99 #The kernel sets the source address of packets when the first routing process happens (following the rules
100 #of the main routing table); when the rerouting process happens (following the user/slice specific rules)
101 #the source address is not modified. Consequently, we need to add this rule to change the source ip address
102 #to the $interface source address.
103 exec "iptables -t nat -A $NAT_POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid"
105 set_active $sliver $interface $ip
107 local old_ip=`get_ip_from_file $sliver $interface`
109 #if the ip of the interface has changed, set the new SNAT rule
110 if [[ $old_ip != $ip ]]; then
111 #remove the rule for the old ip
112 exec "iptables -t nat -D $NAT_POSTROUTING -o $interface -j SNAT --to-source $old_ip -m mark --mark $nid"
114 exec "iptables -t nat -A $NAT_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(){
128 local ip=`get_ip_from_file $sliver $interface`
129 local nid=`get_nid $sliver`
131 if is_active $sliver $interface >/dev/null 2>&1; then
132 exec "iptables -t nat -D $NAT_POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid"
134 local num_ifn_on=`get_num_activated $sliver`
136 if [[ $num_ifn_on == 1 ]]; then
139 set_deactivated $sliver $interface
144 # wrapper function used to execute system commands
148 if ! [[ $command ]]; then
149 echo "Error in exec. No argument."
153 if [[ $DEBUG == 1 ]]; then echo "Error executing \"$1\""; fi
160 # decides wich id to use for the slice-specific routing table
161 function get_table_id(){
165 while [[ $k < 255 ]] && grep $k $RT_TABLES >/dev/null 2>&1 ; do
169 if [[ $k == 255 ]]; then
170 logm "No routing tables available. Exiting."
178 # create the slice-specifig routing table
179 function create_table(){
181 local table_name=`get_table_name $sliver`
182 local table_id=`get_table_id $sliver`
183 local temp_nid=`get_temp_nid $sliver`
185 if ! grep $table_name $RT_TABLES > /dev/null 2>&1; then
186 echo "$table_id $table_name" >> $RT_TABLES
188 echo "WARNING: $table_name routing table already defined."
191 set_routes $sliver $table_name
196 # delete the slice-specific routing table
197 function delete_table(){
199 local table_name=`get_table_name $sliver`
200 local table_id=`get_nid $sliver`
201 local temp_nid=`get_temp_nid $sliver`
203 if ! grep $table_name $RT_TABLES > /dev/null 2>&1; then
206 exec "ip route flush table $table_name" >/dev/null 2>&1
207 unset_routes $sliver $table_name
208 remove_line "$RT_TABLES" "$table_name"
214 # remove a line from a file
215 function remove_line(){
219 #remove interface line from the file
220 exec "sed -i /${regex}/d $filename"
223 # get the slice-specific routing table name
224 function get_table_name(){
226 echo "${sliver}_slcip"
229 #remove files used by sliceip and the added routing tables
230 function clean_iproute_conf(){
231 while grep "slcip" $RT_TABLES >/dev/null 2>&1; do
232 remove_line $RT_TABLES "slcip"
239 #get slice network id
244 # set the firewall rules. Mainly, it asks VNET+ to set the netfilter mark of packets belonging to the slice to the slice-id
245 # and then associates those packets with the routing table allocated for that slice.
246 function set_routes(){
249 local sliver_nid=`get_nid $sliver`
250 local temp_nid=`get_temp_nid $sliver`
252 if [ $ENVIRONMENT == $PLANET_ENV ]; then
253 #Linux kernel triggers a rerouting process, wich is needed to perfom slice-specific routing,
254 #if it sees that the netfilter mark has been altered in the iptables mangle chain.
255 #As VNET+ sets the netfilter mark of some packets in advance (to the slice-id value) before they enter the iptables mangle chain,
256 #we need to change it here (otherwise the rerouting process is not triggered for them).
257 exec "iptables -t mangle -I $MANGLE_OUTPUT 1 -m mark --mark $sliver_nid -j MARK --set-mark $temp_nid"
259 #make sure the netfilter mark of those "strange packets" won't be changed by the following rule
260 exec "iptables -t mangle -I $MANGLE_OUTPUT 2 -m mark --mark $temp_nid -j RETURN"
262 #Here we ask VNET+ to set the netfilter mark for the remaining packets (to the slice-id)
263 #we need to call this only once
264 if [[ $first_time == 1 ]]; then
265 exec "iptables -t mangle -A $MANGLE_OUTPUT -j MARK --copy-xid 0x00"
268 elif [ $ENVIRONMENT == $LINUX_ENV ]; then
269 #the same in the case of a "plain" Linux box. In this case we do not use VNET+ but
270 #the owner module of iptables.
271 exec "iptables -t mangle -A $MANGLE_OUTPUT -m owner --uid-owner $sliver_nid -j MARK --set-mark $sliver_nid"
274 if [ $ENVIRONMENT == $PLANET_ENV ]; then
275 #Here the netfilter mark is restored to the slice-id value for the "strange packets"
276 exec "iptables -t mangle -A $MANGLE_POSTROUTING -m mark --mark $temp_nid -j MARK --set-mark $sliver_nid"
279 #Set the routing for the slice to be applied following the rules in $table_name"
280 #for the slice packets...
281 exec "ip rule add fwmark $sliver_nid table $table_name"
282 #...and for the "strange packets"
283 exec "ip rule add fwmark $temp_nid table $table_name"
285 exec "ip route flush cache" >/dev/null 2>&1
289 # remove the firewall rules.
290 function unset_routes(){
293 local sliver_nid=`get_nid $sliver`
294 local temp_nid=`get_temp_nid $sliver`
296 if [ $ENVIRONMENT == $PLANET_ENV ]; then
297 #removes the rules for the netfilter marks (for the "strange packets")
298 exec "iptables -t mangle -D $MANGLE_OUTPUT -m mark --mark $sliver_nid -j MARK --set-mark $temp_nid"
299 exec "iptables -t mangle -D $MANGLE_OUTPUT -m mark --mark $temp_nid -j RETURN"
301 #removes the rule for restoring the original mark
302 exec "iptables -t mangle -D $MANGLE_POSTROUTING -m mark --mark $temp_nid -j MARK --set-mark $sliver_nid"
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 $MANGLE_OUTPUT -m owner --uid-owner $sliver_nid -j MARK --set-mark $sliver_nid"
309 exec "ip rule del fwmark $temp_nid table $table_name"
310 exec "ip rule del fwmark $sliver_nid table $table_name"
312 exec "ip route flush cache"
316 # additional iptables chains where sliceip inserts its rules
317 NAT_POSTROUTING="sliceip"
318 MANGLE_OUTPUT="sliceip_output"
319 MANGLE_POSTROUTING="sliceip_postrouting"
325 #create the chain where SNAT is performed
326 if iptables -t nat -N $NAT_POSTROUTING >/dev/null 2>&1; then
327 #it's the first time sliceip is called, the chain was not defined
330 #create a chain where the netfilter mark is set
331 exec "iptables -t mangle -N $MANGLE_OUTPUT"
333 #create a chain where the netfilter mark for some packets is restored (see set_routes)
334 exec "iptables -t mangle -N $MANGLE_POSTROUTING"
336 #add the rules to take packets to the previously defined chains
337 exec "iptables -t nat -A POSTROUTING -j $NAT_POSTROUTING"
338 exec "iptables -t mangle -A OUTPUT -j $MANGLE_OUTPUT"
339 exec "iptables -t mangle -I POSTROUTING 1 -j $MANGLE_POSTROUTING"
346 # get the ip address of an interface
347 function get_address(){
351 ip=`ifconfig $interface | grep inet\ addr | cut -d ":" -f 2 | cut -d " " -f 1`;
353 if valid_dotted_quad $ip; then
361 # get the temporary mark to be applied to the packets of the slice
362 function get_temp_nid(){
363 local sliver_nid=`get_nid $1`
364 local temp_nid=$((0x20000+$sliver_nid))
373 # get the name of the filename in wich we store information about
374 # the interfaces in use by the user
375 function get_filename_sliver(){
377 echo "${INT_FILE}-$sliver"
380 function set_active(){
384 local filename="${INT_FILE}-$sliver"
385 echo "$interface $ip" >> $filename
388 function set_deactivated(){
391 local filename=`get_filename_sliver $sliver`
392 remove_line $filename $interface
395 function is_active(){
398 local filename=`get_filename_sliver $sliver`
400 if grep $interface $filename >/dev/null 2>&1; then
407 function get_num_activated(){
409 local filename=`get_filename_sliver $sliver`
411 if ! [ -e $filename ]; then
414 wc -l $filename | cut -f 1 -d " ";
419 function get_ip_from_file(){
423 local filename=`get_filename_sliver $sliver`
425 cat $filename | grep $interface | cut -d " " -f 2
430 function valid_dotted_quad(){
440 ""|*[!0-9]*) return 1; break ;; ## Segment empty or non-numeric char
441 *) [ $seg -gt 255 ] && return 2 ;;
445 return 3 ## Not 4 segments
454 # the script starts here
456 if [[ $sliver == "" ]]; then
457 echo "I need the first argument (the sliver name)";
461 # read a line from the vsys pipe
464 # separate the first word of the line from the others
465 command=`echo ${line%% *}`
466 rest=`echo ${line#* }`
470 logger "sliceip command received from $sliver: $line"
471 enable_interface $sliver "$rest"
475 logger "sliceip command received from $sliver: $line"
476 disable_interface $sliver "$rest"
480 logger "sliceip command received from $sliver: $line"
481 table_sliver=`get_table_name $sliver`
483 if ! grep "$table_sliver" $RT_TABLES >/dev/null 2>&1; then
484 echo "Error. The slice routing table is not defined. Execute sliceip enable <interface>."
487 #add the routing rule - ip is called with the same parameters of sliceip but the indication of
488 #the table in wich the rule is to be inserted is appended
489 exec "ip $line table $table_sliver"