#!/usr/bin/python2 # VSYS script to configure routes on virtual network interfaces from the root slice # Claudio Freire - 2011/05/19 # # 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 := # # 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_int = struct.unpack('!L', socket.inet_aton(vnet_base))[0] vnet_int = (vnet_int >> (32-vnet_mask)) << (32-vnet_mask) mask_int = (0xffffffff >> (32-vnet_mask)) << (32-vnet_mask) # rule line regex rule_re = r"(?Padd|del)\s+(?P(?:\d{1,3}[.]){3}\d{1,3})(?:/(?P\d{1,2}))?\s+gw\s+(?P(?:\d{1,3}[.]){3}\d{1,3}) (?P(?: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"