vroute script to permite manipulation of routing tables in a secure manner (ie: doesn...
Claudio-Daniel Freire [Mon, 23 May 2011 08:59:42 +0000 (10:59 +0200)]
exec/vroute [new file with mode: 0755]

diff --git a/exec/vroute b/exec/vroute
new file mode 100755 (executable)
index 0000000..63a9029
--- /dev/null
@@ -0,0 +1,158 @@
+#!/usr/bin/python
+# VSYS script to configure routes on virtual network interfaces from the root slice
+#   Thom Haddow - 06/10/09
+#
+# Gets slice name as argv[1]
+# Takes routing rules on stdin, one per line
+#   rule := command ' ' target ' gw ' host device
+#   command := 'add' | 'del'
+#   target := host | network
+#   network := host [ '/' netmask ]
+#   netmask := number
+#   host := number '.' number '.' number '.' number
+#   number := [0-9]+
+#   device := ( tun | tap ) slice '-' number
+#   slice := <slice id>
+#
+# All networks and gateways MUST belong to the virtual
+# network assigned to the slice (according to the vsys_vnet tag)
+#
+# Examples:
+#
+#   add 10.0.0.0/24 gw 10.0.0.1 tap754-0
+#   del 10.0.0.0/24 gw 10.0.0.1 tap754-0
+#
+
+
+import sys
+import pwd
+import re
+import socket
+import struct
+import os
+import string
+import subprocess
+
+vsys_config_dir = "/etc/planetlab/vsys-attributes"
+
+if len(sys.argv) != 2: sys.exit(1)
+
+# VSYS scripts get slicename as $1
+slicename=sys.argv[1]
+sliceid = pwd.getpwnam(slicename).pw_uid
+
+netblock_config=os.path.join(vsys_config_dir,slicename,"vsys_vnet")
+
+# Read netblock allocation file
+vnet_base = None
+vnet_mask = None
+
+try:
+    for netblock in open(netblock_config,'r'):
+        vnet_base, vnet_mask = netblock.split('/')
+        vnet_mask = int(vnet_mask)
+except:
+    vnet_base = vnet_mask = None
+
+if vnet_base is None:
+    #print >>sys.stderr, "Could not find entry for slice %s in netblock config file %s" % (slicename, netblock_config)
+    #sys.exit(1)
+    vnet_base = '192.168.2.0'
+    vnet_mask = 24
+
+vnet_int = struct.unpack('!L', socket.inet_aton(vnet_base))[0]
+vnet_int = (vnet_int >> vnet_mask) << vnet_mask
+
+mask_int = (0xffffffff << vnet_mask) & 0xffffffff
+
+# rule line regex
+rule_re = r"(?P<cmd>add|del)\s+(?P<targetbase>(?:\d{1,3}[.]){3}\d{1,3})(?:/(?P<targetprefix>\d{1,2}))?\s+gw\s+(?P<gw>(?:\d{1,3}[.]){3}\d{1,3}) (?P<dev>(?:tun|tap)%d-\d{1,5})" % (sliceid,)
+rule_re = re.compile(rule_re)
+
+### Read args from stdin
+for argline in sys.stdin:
+    argline = argline.strip()
+    
+    # Match rule against the regex, 
+    # validating overall structure in the process
+    match = rule_re.match(argline)
+    if not match:
+        print >>sys.stderr, "Invalid rule %r:" % (argline,)
+        continue
+    
+    # Validate IPs involved
+    try:
+        target_ip_int = struct.unpack('!L',
+            socket.inet_aton(match.group('targetbase')))[0]
+    except Exception,e:
+        print >>sys.stderr, "Invalid target ip: %s" % (e,)
+        continue
+
+    try:
+        gw_ip_int = struct.unpack('!L',
+            socket.inet_aton(match.group('gw')))[0]
+    except Exception,e:
+        print >>sys.stderr, "Invalid target ip: %s" % (e,)
+        continue
+
+    if match.group('targetprefix'):
+        try:
+            target_mask = int(match.group('targetprefix'))
+        except Exception,e:
+            print >>sys.stderr, "Invalid target mask: %s" % (e,)
+            continue
+    else:
+        # host route
+        target_mask = 32
+    
+    ifname = match.group('dev')
+    if not ifname:
+        # Shouldn't happen, but just in case
+        print >>sys.stderr, "Invalid rule %r: Unspecified interface" % (argline,)
+        continue
+    
+    # Make sure all IPs/networks fall within the assigne vnet
+    if target_mask < vnet_mask:
+        print >>sys.stderr, "Invalid rule %r: target must belong to the %s/%s network" % (argline, vnet_base, vnet_mask)
+        continue
+    
+    if (target_ip_int & mask_int) != vnet_int:
+        print >>sys.stderr, "Invalid rule %r: target must belong to the %s/%s network" % (argline, vnet_base, vnet_mask)
+        continue
+    
+    if (gw_ip_int & mask_int) != vnet_int:
+        print >>sys.stderr, "Invalid rule %r: gateway must belong to the %s/%s network" % (argline, vnet_base, vnet_mask)
+        continue
+    
+    # Revalidate command (just in case)
+    command = match.group("cmd")
+    if command not in ('add','del'):
+        print >>sys.stderr, "Invalid rule %r: invalid command" % (argline,)
+        continue
+    
+    # Apply the rule
+    routeargs = [ "/sbin/route", command ]
+    
+    target_ip_str = socket.inet_ntoa(
+        struct.pack('!L', target_ip_int))
+
+    gw_ip_str = socket.inet_ntoa(
+        struct.pack('!L', gw_ip_int))
+    
+    if target_mask != 32:
+        routeargs.extend([
+            "-net", "%s/%d" % (target_ip_str, target_mask)
+        ])
+    else:
+        routeargs.append(target_ip_str)
+    
+    routeargs.extend([
+        "gw", gw_ip_str,
+        "dev", ifname,
+    ])
+    
+    proc = subprocess.Popen(routeargs)
+    if proc.wait():
+        print >>sys.stderr, "Could not set up route"
+    
+