4 # Copyright (C) 2009 UniNa
6 # This is a backend script to be used with
7 # the vsys system. It allows to define and
8 # manage slice-specific routing tables.
9 # The routing rules added to these routing
10 # tables affect only the respective slices.
12 # This script is meant to be called from a
13 # frontend script in the slice environment.
16 # sliceip enable <interface>
17 # This is the first command to issue; it enables the use
18 # of the interface for the slice. This command has to be issued
19 # for each interface involved in the slice-specific routing rules.
21 # sliceip disable <interface>
22 # This is to disable the use of the interface for the slice.
25 # This command shows the current routing rules of the
26 # slice-specific routing table
28 # sliceip route add to <destination> via <gateway> dev <interface>
29 # This command adds a rule to reach <destination> through the host
30 # <gateway> that can be reached on the interface <interface>
33 # sliceip in general supports all the commands of the "ip" command of
34 # the "iproute2" suite. Refer to ip manpage for further information.
37 # sliceip route add to 143.225.229.142 via 192.168.8.2 dev ath0
38 # sliceip route add default via 10.1.1.1 dev ath0
40 # If you want to test the script from the root context you need
41 # to call it as vsys would do:
42 # sliceip <slice_name>
43 # Then you can issue to its standard input the commands previously explained
44 # (but you have to remove the initial "sliceip"). Ex.:
45 # route add to 143.225.229.142 via 192.168.8.2 dev ath0
48 PATH=/bin:/usr/bin:/sbin:/usr/sbin
50 # sliver wich called the script
54 RT_TABLES="/etc/iproute2/rt_tables"
55 INT_FILE="/tmp/slcroute_ifns"
57 # routing tables from 100 to 255 are used
62 #this script can also work on a Linux machine, in wich case
63 #it enables user-specific routing tables
64 LINUX_ENV=0 # 0 - Linux host;
65 # does not work for the icmp protocol
67 PLANET_ENV=1 # 1 - PlanetLab context (slice-specific routing)
69 #select the PlanetLab environment
70 ENVIRONMENT=$PLANET_ENV
73 # enable an interface for the slice specific routing rules
74 # and initialise the slice routing table
75 function enable_interface(){ # $1 calling sliver; $2 interface to enable
78 local ip=`get_address $interface`
80 if [[ "" == $ip ]]; then
81 echo "Failed in getting address of $interface"
85 #create the sliceip netfilter chains
88 local nid=`get_nid $sliver`
89 local num_active=`get_num_activated $sliver`
91 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 $NAT_POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid"
104 set_active $sliver $interface $ip
106 local old_ip=`get_ip_from_file $sliver $interface`
108 #if the ip of the interface has changed, set the new SNAT rule
109 if [[ $old_ip != $ip ]]; then
110 #remove the rule for the old ip
111 exec "iptables -t nat -D $NAT_POSTROUTING -o $interface -j SNAT --to-source $old_ip -m mark --mark $nid"
113 exec "iptables -t nat -A $NAT_POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid"
114 set_deactivated $sliver $interface $old_ip
115 set_active $sliver $interface $ip
123 # disable the interface for the slice-specific routing rules
124 function disable_interface(){
127 local ip=`get_ip_from_file $sliver $interface`
128 local nid=`get_nid $sliver`
130 if is_active $sliver $interface >/dev/null 2>&1; then
131 exec "iptables -t nat -D $NAT_POSTROUTING -o $interface -j SNAT --to-source $ip -m mark --mark $nid"
133 local num_ifn_on=`get_num_activated $sliver`
135 if [[ $num_ifn_on == 1 ]]; then
138 set_deactivated $sliver $interface
143 # wrapper function used to execute system commands
147 if ! [[ $command ]]; then
148 echo "Error in exec. No argument."
152 if [[ $DEBUG == 1 ]]; then echo "Error executing \"$1\""; fi
159 # decides wich id to use for the slice-specific routing table
160 function get_table_id(){
164 while [[ $k < 255 ]] && grep $k $RT_TABLES >/dev/null 2>&1 ; do
168 if [[ $k == 255 ]]; then
169 logm "No routing tables available. Exiting."
177 # create the slice-specifig routing table
178 function create_table(){
180 local table_name=`get_table_name $sliver`
181 local table_id=`get_table_id $sliver`
182 local temp_nid=`get_temp_nid $sliver`
184 if ! grep $table_name $RT_TABLES > /dev/null 2>&1; then
185 echo "$table_id $table_name" >> $RT_TABLES
187 echo "WARNING: $table_name routing table already defined."
190 set_routes $sliver $table_name
195 # delete the slice-specific routing table
196 function delete_table(){
198 local table_name=`get_table_name $sliver`
199 local table_id=`get_nid $sliver`
200 local temp_nid=`get_temp_nid $sliver`
202 if ! grep $table_name $RT_TABLES > /dev/null 2>&1; then
205 exec "ip route flush table $table_name" >/dev/null 2>&1
206 unset_routes $sliver $table_name
207 remove_line "$RT_TABLES" "$table_name"
213 # remove a line from a file
214 function remove_line(){
218 #remove interface line from the file
219 exec "sed -i /${regex}/d $filename"
222 # get the slice-specific routing table name
223 function get_table_name(){
225 echo "${sliver}_slcip"
228 #remove files used by sliceip and the added routing tables
229 function clean_iproute_conf(){
230 while grep "slcip" $RT_TABLES >/dev/null 2>&1; do
231 remove_line $RT_TABLES "slcip"
238 #get slice network id
243 # set the firewall rules. Mainly, it asks VNET+ to set the netfilter mark of packets belonging to the slice to the slice-id
244 # and then associates those packets with the routing table allocated for that slice.
245 function set_routes(){
248 local sliver_nid=`get_nid $sliver`
249 local temp_nid=`get_temp_nid $sliver`
251 if [ $ENVIRONMENT == $PLANET_ENV ]; then
252 #Linux kernel triggers a rerouting process, wich is needed to perfom slice-specific routing,
253 #if it sees that the netfilter mark has been altered in the iptables mangle chain.
254 #As VNET+ sets the netfilter mark of some packets in advance (to the slice-id value) before they enter the iptables mangle chain,
255 #we need to change it here (otherwise the rerouting process is not triggered for them).
256 exec "iptables -t mangle -I $MANGLE_OUTPUT 1 -m mark --mark $sliver_nid -j MARK --set-mark $temp_nid"
258 #make sure the netfilter mark of those "strange packets" won't be changed by the following rule
259 exec "iptables -t mangle -I $MANGLE_OUTPUT 2 -m mark --mark $temp_nid -j RETURN"
261 #Here we ask VNET+ to set the netfilter mark for the remaining packets (to the slice-id)
262 #we need to call this only once
263 if [[ $first_time == 1 ]]; then
264 exec "iptables -t mangle -A $MANGLE_OUTPUT -j MARK --copy-xid 0x00"
267 elif [ $ENVIRONMENT == $LINUX_ENV ]; then
268 #the same in the case of a "plain" Linux box. In this case we do not use VNET+ but
269 #the owner module of iptables.
270 exec "iptables -t mangle -A $MANGLE_OUTPUT -m owner --uid-owner $sliver_nid -j MARK --set-mark $sliver_nid"
273 if [ $ENVIRONMENT == $PLANET_ENV ]; then
274 #Here the netfilter mark is restored to the slice-id value for the "strange packets"
275 exec "iptables -t mangle -A $MANGLE_POSTROUTING -m mark --mark $temp_nid -j MARK --set-mark $sliver_nid"
278 #Set the routing for the slice to be applied following the rules in $table_name"
279 #for the slice packets...
280 exec "ip rule add fwmark $sliver_nid table $table_name"
281 #...and for the "strange packets"
282 exec "ip rule add fwmark $temp_nid table $table_name"
284 exec "ip route flush cache" >/dev/null 2>&1
288 # remove the firewall rules.
289 function unset_routes(){
292 local sliver_nid=`get_nid $sliver`
293 local temp_nid=`get_temp_nid $sliver`
295 if [ $ENVIRONMENT == $PLANET_ENV ]; then
296 #removes the rules for the netfilter marks (for the "strange packets")
297 exec "iptables -t mangle -D $MANGLE_OUTPUT -m mark --mark $sliver_nid -j MARK --set-mark $temp_nid"
298 exec "iptables -t mangle -D $MANGLE_OUTPUT -m mark --mark $temp_nid -j RETURN"
300 #removes the rule for restoring the original mark
301 exec "iptables -t mangle -D $MANGLE_POSTROUTING -m mark --mark $temp_nid -j MARK --set-mark $sliver_nid"
303 elif [ $ENVIRONMENT == $LINUX_ENV ]; then
304 #removes the rules that asks the owner module of iptables to set the netfilter mark to the user-id
305 exec "iptables -t mangle -D $MANGLE_OUTPUT -m owner --uid-owner $sliver_nid -j MARK --set-mark $sliver_nid"
308 exec "ip rule del fwmark $temp_nid table $table_name"
309 exec "ip rule del fwmark $sliver_nid table $table_name"
311 exec "ip route flush cache"
315 # additional iptables chains where sliceip inserts its rules
316 NAT_POSTROUTING="sliceip"
317 MANGLE_OUTPUT="sliceip_output"
318 MANGLE_POSTROUTING="sliceip_postrouting"
324 #create the chain where SNAT is performed
325 if iptables -t nat -N $NAT_POSTROUTING >/dev/null 2>&1; then
326 #it's the first time sliceip is called, the chain was not defined
329 #create a chain where the netfilter mark is set
330 exec "iptables -t mangle -N $MANGLE_OUTPUT"
332 #create a chain where the netfilter mark for some packets is restored (see set_routes)
333 exec "iptables -t mangle -N $MANGLE_POSTROUTING"
335 #add the rules to take packets to the previously defined chains
336 exec "iptables -t nat -A POSTROUTING -j $NAT_POSTROUTING"
337 exec "iptables -t mangle -A OUTPUT -j $MANGLE_OUTPUT"
338 exec "iptables -t mangle -I POSTROUTING 1 -j $MANGLE_POSTROUTING"
345 # get the ip address of an interface
346 function get_address(){
350 ip=`ifconfig $interface | grep inet\ addr | cut -d ":" -f 2 | cut -d " " -f 1`;
352 if valid_dotted_quad $ip; then
360 # get the temporary mark to be applied to the packets of the slice
361 function get_temp_nid(){
362 local sliver_nid=`get_nid $1`
363 local temp_nid=$((0x20000+$sliver_nid))
372 # get the name of the filename in wich we store information about
373 # the interfaces in use by the user
374 function get_filename_sliver(){
376 echo "${INT_FILE}-$sliver"
379 function set_active(){
383 local filename="${INT_FILE}-$sliver"
384 echo "$interface $ip" >> $filename
387 function set_deactivated(){
390 local filename=`get_filename_sliver $sliver`
391 remove_line $filename $interface
394 function is_active(){
397 local filename=`get_filename_sliver $sliver`
399 if grep $interface $filename >/dev/null 2>&1; then
406 function get_num_activated(){
408 local filename=`get_filename_sliver $sliver`
410 if ! [ -e $filename ]; then
413 wc -l $filename | cut -f 1 -d " ";
418 function get_ip_from_file(){
422 local filename=`get_filename_sliver $sliver`
424 cat $filename | grep $interface | cut -d " " -f 2
429 function valid_dotted_quad(){
439 ""|*[!0-9]*) return 1; break ;; ## Segment empty or non-numeric char
440 *) [ $seg -gt 255 ] && return 2 ;;
444 return 3 ## Not 4 segments
453 # the script starts here
455 if [[ $sliver == "" ]]; then
456 echo "I need the first argument (the sliver name)";
460 # read a line from the vsys pipe
463 # separate the first word of the line from the others
464 command=`echo ${line%% *}`
465 rest=`echo ${line#* }`
469 logger "sliceip command received from $sliver: $line"
470 enable_interface $sliver "$rest"
474 logger "sliceip command received from $sliver: $line"
475 disable_interface $sliver "$rest"
479 logger "sliceip command received from $sliver: $line"
480 table_sliver=`get_table_name $sliver`
482 if ! grep "$table_sliver" $RT_TABLES >/dev/null 2>&1; then
483 echo "Error. The slice routing table is not defined. Execute sliceip enable <interface>."
486 #add the routing rule - ip is called with the same parameters of sliceip but the indication of
487 #the table in wich the rule is to be inserted is appended
488 exec "ip $line table $table_sliver"