vsys for new lxc based planetlab code
[vsys-scripts.git] / root-context / exec / sliceip.lxc
1 #!/bin/bash
2 #
3 # Giovanni Di Stasi
4 # <giovanni.distasi on the unina.it domain>
5 # Copyright (C) 2009-2013 UniNa
6 #
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.  
12 #
13 # This script is meant to be called from a 
14 # frontend script in the slice environment. 
15 #
16 # Fronted usage:
17 #
18 # sliceip route show 
19 #   Shows the current routing rules of the
20 #   slice-specific routing table
21 #
22 # sliceip route add to <destination> via <gateway> dev <interface>
23 #   Adds a rule to reach <destination> through the host
24 #   <gateway> that can be reached on the interface <interface>
25 # ...
26
27 # sliceip in general supports all the commands of the "ip" command of
28 # the "iproute2" suite. Refer to ip manpage for further information.
29
30 # Some examples:
31 #       sliceip route add to 143.225.229.142 via 192.168.8.2 dev ath0
32 #       sliceip route add default via 10.1.1.1 dev ath0
33 #       sliceip route add to 143.225.229.142  nexthop via 192.168.0.1 dev eth1  \
34 #       weight 1 src 192.168.0.2 nexthop via 192.168.1.1 dev eth2 weight 1 
35 #
36 # sliceip can also be used without a frontend program as follows.
37 #
38 # First use cat to show the output:
39 # cat < /vsys/sliceip.out &
40 # Then issue a command as, e.g., the following routing rule:
41 # echo "route add to some_ip via some_other_ip dev eth0" > /vsys/sliceip.in
42 #
43 # If you want to test the script from the root context you need 
44 # to call it as vsys would do:
45 # sliceip <slice_name>
46 # Then you can issue to its standard input the commands previously explained
47 # (but you have to remove the initial "sliceip"). Ex.:
48 #        route add to 143.225.229.142 via 192.168.8.2 dev ath0
49 #
50 # A frontend sliceip script can therefore be created as follows:
51 # #!/bin/bash
52 # cat < /vsys/sliceip.out &
53 # echo $* > /vsys/sliceip.in
54 #
55 # Notes for PlanetLab developers:
56 # For sliceip to work correctly it is required that, at boot time 
57 # i) stale entries of /etc/iproute2/rt_tables are removed, i.e. lines that 
58 # contain slcip; ii) /tmp/ is cleaned.
59
60 PATH=/bin:/usr/bin:/sbin:/usr/sbin 
61
62 # sliver which called the script
63 sliver=$1
64
65 #file used
66 RT_TABLES="/etc/iproute2/rt_tables"
67
68 # routing tables from 6 to 249 are used
69 FIRST_TABLE=6
70 LAST_TABLE=249
71
72 DEBUG=0
73
74
75
76 # wrapper function used to execute system commands
77 function exec(){
78         local command=$1        
79
80         if ! [[ $command ]]; then
81                 echo "Error in exec. No argument."
82         else
83                 if ! $command; then
84                         if [[ $DEBUG == 1 ]]; then echo "Error executing \"$1\""; fi
85                 fi
86         fi
87
88 }
89
90 # Returns the number of routing rules in routing table $table
91 function get_num_rules(){
92   local table=$1
93   
94   ip route show table $table | wc -l
95
96 }
97
98
99 # Assign a routing table to the sliver (by creating a mapping in
100 # /etc/iproute2/rt_tables).
101 # If all the routing tables are assigned, remove the mappings
102 # for slivers that do not have rules in their routing
103 # tables.
104 function create_table(){
105
106           local table_sliver=$1
107
108           #if the table already exists, returns immediately
109           if grep "$table_sliver" $RT_TABLES >/dev/null 2>&1; then
110             return 0
111           fi
112         
113           #... otherwise find a free routing table to assign to the sliver
114           k=$FIRST_TABLE
115           while [[ $k -lt $((LAST_TABLE+1)) ]] && grep "$k " $RT_TABLES >/dev/null 2>&1 ; do
116                 k=$((k+1));
117           done
118         
119           #if all were already assigned, remove unused assignements
120           if [[ $k == $((LAST_TABLE+1)) ]]; then
121               k=$FIRST_TABLE
122               while [[ $k -lt $((LAST_TABLE+1)) ]] && grep "$k " $RT_TABLES >/dev/null 2>&1 ; do
123                 if [[ `get_num_rules $k` == 0 ]]; then
124                  delete_table `cat $RT_TABLES | grep "$k " | cut -d " " -f 2`
125                 fi
126                 k=$((k+1));
127               done
128
129               #pick the first that has become available, if any
130               k=$FIRST_TABLE
131               while [[ $k -lt $((LAST_TABLE+1)) ]] && grep "$k " $RT_TABLES >/dev/null 2>&1 ; do
132                 k=$((k+1));
133               done
134               
135               #otherwise give up and return that no table is available
136               if [[ $k == $((LAST_TABLE+1)) ]]; then
137                 echo "No routing tables available. Exiting."
138                 return 1
139               fi
140         
141           fi
142         
143           echo "$k $table_sliver" >> $RT_TABLES
144
145           set_routes $sliver $table_sliver
146                   
147           return 0
148 }
149
150 # Delete the slice-specific routing table
151 function delete_table(){
152         local table_name=$1
153         local sliver=`basename $table_name "_slcip"`
154
155         if ! grep $table_name $RT_TABLES > /dev/null 2>&1; then
156                 return 1                        
157         else
158                 exec "ip route flush table $table_name" >/dev/null 2>&1
159                 unset_routes $sliver $table_name
160                 remove_line "$RT_TABLES" "$table_name"          
161         fi
162
163         return 0
164 }
165
166 # Remove a line from a file
167 function remove_line(){
168         local filename=$1
169         local regex=$2
170
171         #remove interface line from the file
172         exec "sed -i /${regex}/d $filename"
173 }
174
175 # Get the slice-specific routing table name
176 function get_table_name(){
177         local sliver=$1;
178         echo "${sliver}_slcip"
179 }
180
181 # Remove files used by sliceip and the added routing tables 
182 function clean_iproute_conf(){
183         while grep "slcip" $RT_TABLES >/dev/null 2>&1; do
184                 remove_line $RT_TABLES "slcip"
185         done
186 }
187
188
189 # Get sliver's virtual interface
190 function get_sliver_veth(){
191   local sliver=$1
192   cat /var/run/libvirt/lxc/$sliver.xml | grep "target dev"  | cut -d "=" -f 2 | cut -d "'" -f 2
193 }
194
195 function get_sliver_ip(){
196   local sliver=$1
197   local interface=eth0  
198   local ip=""
199         
200   ip=`echo "ifconfig $interface" | lxcsu $sliver | grep inet\ addr | cut -d ":" -f 2 | cut -d " " -f 1`;
201
202   if valid_dotted_quad $ip; then 
203         echo $ip;
204   else 
205         echo "";
206   fi    
207                         
208 }  
209
210
211
212 # set the firewall rules.
213 # Basically source routing is enabled.
214 function set_routes(){
215         local sliver=$1
216         local table_name=$2
217         #local sliver_veth=`get_sliver_veth $sliver`
218         local sliver_ip=`get_sliver_ip $sliver`
219
220         # Set $table_name as routing table for the sliver
221         # through source routing
222         #exec "ip rule add iif $sliver_veth table $table_name"  
223         exec "ip rule add from $sliver_ip table $table_name"    
224
225         exec "ip route flush cache"  >/dev/null 2>&1
226
227         #Flush stale routing rules in $table_name
228         exec "ip route flush table $table_name"
229 }
230
231
232 # Remove the firewall rules. 
233 function unset_routes(){
234         local sliver=$1
235         local table_name=$2
236         #local sliver_veth=`get_sliver_veth $sliver`
237         local sliver_ip=`get_sliver_ip $sliver`
238
239
240         # Unset $table_name as routing table for the sliver
241         # through source routing
242         exec "ip rule del from $sliver_ip table $table_name"    
243
244         exec "ip route flush cache"  >/dev/null 2>&1
245 }
246
247
248 # Get the ip address of an interface
249 function get_address(){
250         local interface=$1      
251         local ip=""
252         
253         ip=`ifconfig $interface | grep inet\ addr | cut -d ":" -f 2 | cut -d " " -f 1`;
254
255         if valid_dotted_quad $ip; then 
256                 echo $ip;
257         else 
258                 echo "";
259         fi      
260                         
261 }
262
263
264 # Log function
265 function logm(){
266         logger $1
267 }
268
269
270
271
272 # Check an ip address for correcteness  
273 function valid_dotted_quad(){
274     oldIFS=$IFS
275     IFS=.
276     set -f
277     set -- $1
278     if [ $# -eq 4 ]
279     then
280       for seg
281       do
282         case $seg in
283             ""|*[!0-9]*) return 1; break ;; ## Segment empty or non-numeric char
284             *) [ $seg -gt 255 ] && return 2 ;;
285         esac
286       done
287     else
288       return 3 ## Not 4 segments
289     fi
290     IFS=$oldIFS
291     set +f
292     return 0;
293 }
294
295 # Check to see if the command start with "route"
296 function checkCommand(){
297
298   local cmd="$1"
299   local first=`echo "$cmd" | awk '{print $1}'`
300   #local second=`echo $cmd | awk '{print $2}'`
301   
302   
303   if [[ "$first" != "route" ]]; then
304     echo "Command must start with route."
305     return 1
306   fi
307   
308   return 0
309   
310 }
311
312 # BEGIN
313 # the script starts here
314
315 if [[ $sliver == "" ]]; then
316         echo "I need the first argument (the sliver name)";
317         exit 1
318 fi
319
320 # read a line from the vsys pipe
321 read line
322
323 # separate the first two words of the line from the rest
324 command=`echo $line | awk '{print $1 " " $2}'`
325 rest=`echo $line | awk '{ for (i = 3; i<=NF; i++) { printf "%s ",$i } }'`
326
327 #rest=`echo ${line#* }`
328
329
330
331 case "$command" in
332   enable)
333         #enable and disable kept for compatibility
334         logger "sliceip command received from $sliver: $line"   
335         ;;
336
337   disable)
338         logger "sliceip command received from $sliver: $line"
339         ;;
340
341   *)
342         logger "sliceip command received from $sliver: $line"
343         
344         #no concurrent executions of sliceip allowed  
345         while ! mkdir /tmp/sliceip.lock >/dev/null 2>&1; do
346           sleep 0.5
347         done
348         
349         table_sliver=`get_table_name $sliver`
350         
351         #checks the command, creates the routing table and adds the rule
352         if checkCommand "$command" && create_table $table_sliver; then
353           
354         
355           #add the routing rule - ip is called with the same parameters of sliceip but the indication of 
356           #the table in which the rule is to be inserted is inserted            
357           exec "ip $command table $table_sliver $rest"
358         
359         fi
360         
361         #remove the lock
362         rmdir /tmp/sliceip.lock
363           
364         ;;
365
366 esac
367
368 exit 0