From: Claudio-Daniel Freire Date: Wed, 4 May 2011 15:03:01 +0000 (+0200) Subject: Ticket #14: cross-connections between NS3 and PlanetLab through file descriptors... X-Git-Tag: nepi_v2~74 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=147a18eeffae96c2be4357d1351f55057a20bec1;p=nepi.git Ticket #14: cross-connections between NS3 and PlanetLab through file descriptors (that means, NS3 instances running inside PL ATM) --- diff --git a/src/nepi/testbeds/netns/metadata_v01.py b/src/nepi/testbeds/netns/metadata_v01.py index 60cc096e..caa65c42 100644 --- a/src/nepi/testbeds/netns/metadata_v01.py +++ b/src/nepi/testbeds/netns/metadata_v01.py @@ -29,7 +29,7 @@ def connect_fd(testbed_instance, tap_guid, cross_data): import passfd import socket tap = testbed_instance._elements[tap_guid] - address = cross_data["LinuxSocketAddress"] + address = cross_data["tun_addr"] sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) sock.connect(address) passfd.sendfd(sock, tap.fd, '0') @@ -182,10 +182,16 @@ connector_types = dict({ "max": 1, "min": 0 }), - "fd": dict({ - "help": "Connector to a network interface that can receive a file descriptor", - "name": "fd", - "max": 1, + "->fd": dict({ + "help": "File descriptor receptor for devices with file descriptors", + "name": "->fd", + "max": 1, + "min": 0 + }), + "fd->": dict({ + "help": "File descriptor provider for devices with file descriptors", + "name": "fd->", + "max": 1, "min": 0 }), "switch": dict({ @@ -218,8 +224,8 @@ connections = [ "can_cross": False }), dict({ - "from": (TESTBED_ID, TAPIFACE, "fd"), - "to": (NS3_TESTBED_ID, FDNETDEV, "fd"), + "from": (TESTBED_ID, TAPIFACE, "fd->"), + "to": (None, None, "->fd"), "compl_code": connect_fd, "can_cross": True }), @@ -360,7 +366,7 @@ factories_info = dict({ "configure_function": configure_device, "box_attributes": ["lladdr", "up", "device_name", "mtu", "multicast", "broadcast", "arp"], - "connector_types": ["node", "fd"] + "connector_types": ["node", "fd->"] }), NODEIFACE: dict({ "allow_addresses": True, diff --git a/src/nepi/testbeds/ns3/factories_metadata_v3_9_RC3.py b/src/nepi/testbeds/ns3/factories_metadata_v3_9_RC3.py index be21fc92..5b8a79f3 100644 --- a/src/nepi/testbeds/ns3/factories_metadata_v3_9_RC3.py +++ b/src/nepi/testbeds/ns3/factories_metadata_v3_9_RC3.py @@ -625,10 +625,11 @@ factories_info = dict({ "create_function": create_element, "configure_function": configure_device, "help": "Network interface associated to a file descriptor", - "connector_types": ["node", "fd"], + "connector_types": ["node", "->fd"], "allow_addresses": True, "box_attributes": ["Address", - "LinuxSocketAddress"], + "LinuxSocketAddress", + "tun_proto", "tun_addr", "tun_port", "tun_key"], "traces": ["fdpcap"] }), "ns3::CsmaNetDevice": dict({ diff --git a/src/nepi/testbeds/ns3/metadata_v3_9_RC3.py b/src/nepi/testbeds/ns3/metadata_v3_9_RC3.py index 9ce7f03e..03bc41ba 100644 --- a/src/nepi/testbeds/ns3/metadata_v3_9_RC3.py +++ b/src/nepi/testbeds/ns3/metadata_v3_9_RC3.py @@ -96,6 +96,12 @@ def connect_fd(testbed_instance, fdnd_guid, cross_data): # to see how the address should be decoded address = endpoint.replace(":", "").decode('hex')[2:] testbed_instance.set(fdnd_guid, "LinuxSocketAddress", address) + + # Set tun standard contract attributes + testbed_instance.set(fdnd_guid, "tun_addr", address) + testbed_instance.set(fdnd_guid, "tun_proto", "fd") + testbed_instance.set(fdnd_guid, "tun_port", 0) + testbed_instance.set(fdnd_guid, "tun_key", "\xff"*32) # unimportant, fds aren't encrypted ### Connector information ### @@ -154,9 +160,15 @@ connector_types = dict({ "max": 1, "min": 0 }), - "fd": dict({ - "help": "Connector to interconnect devices with file descriptors", - "name": "fd", + "->fd": dict({ + "help": "File descriptor receptor for devices with file descriptors", + "name": "->fd", + "max": 1, + "min": 0 + }), + "fd->": dict({ + "help": "File descriptor provider for devices with file descriptors", + "name": "fd->", "max": 1, "min": 0 }), @@ -482,8 +494,8 @@ connections = [ "can_cross": False }), dict({ - "from": ( "ns3", "ns3::FileDescriptorNetDevice", "fd" ), - "to": ( "netns", "TapNodeInterface", "fd" ), + "from": ( "ns3", "ns3::FileDescriptorNetDevice", "->fd" ), + "to": ( None, None, "fd->" ), "init_code": connect_fd, "can_cross": True }), diff --git a/src/nepi/testbeds/planetlab/interfaces.py b/src/nepi/testbeds/planetlab/interfaces.py index 014ccee8..30895b86 100644 --- a/src/nepi/testbeds/planetlab/interfaces.py +++ b/src/nepi/testbeds/planetlab/interfaces.py @@ -168,6 +168,12 @@ class TunIface(object): if not self.address or not self.netprefix or not self.netmask: raise RuntimeError, "Misconfigured %s iface - missing address" % (self._KIND,) + def _impl_instance(self, home_path, listening): + impl = self._PROTO_MAP[self.peer_proto]( + self, self.peer_iface, home_path, self.tun_key, listening) + impl.port = self.tun_port + return impl + def prepare(self, home_path, listening): if not self.peer_iface and (self.peer_proto and (listening or (self.peer_addr and self.peer_port))): # Ad-hoc peer_iface @@ -177,9 +183,7 @@ class TunIface(object): self.peer_port) if self.peer_iface: if not self.peer_proto_impl: - self.peer_proto_impl = self._PROTO_MAP[self.peer_proto]( - self, self.peer_iface, home_path, self.tun_key, listening) - self.peer_proto_impl.port = self.tun_port + self.peer_proto_impl = self._impl_instance(home_path, listening) self.peer_proto_impl.prepare() def setup(self): diff --git a/src/nepi/testbeds/planetlab/metadata_v01.py b/src/nepi/testbeds/planetlab/metadata_v01.py index 81718da0..62e39fac 100644 --- a/src/nepi/testbeds/planetlab/metadata_v01.py +++ b/src/nepi/testbeds/planetlab/metadata_v01.py @@ -96,7 +96,7 @@ def connect_tun_iface_node(testbed_instance, node_guid, iface_guid): raise RuntimeError, "Use of TUN interfaces requires emulation" iface.node = node node.required_vsys.update(('fd_tuntap', 'vif_up')) - node.required_packages.add('python-crypto') + node.required_packages.update(('python', 'python-crypto', 'python-setuptools', 'gcc')) def connect_tun_iface_peer(proto, testbed_instance, iface_guid, peer_iface_guid): iface = testbed_instance._elements[iface_guid] @@ -118,8 +118,18 @@ def crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_i preconfigure_tuniface(testbed_instance, iface_guid) def crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data): + # refresh (refreshable) attributes for second-phase + iface = testbed_instance._elements[iface_guid] + iface.peer_addr = peer_iface_data.get("tun_addr") + iface.peer_proto = peer_iface_data.get("tun_proto") + iface.peer_port = peer_iface_data.get("tun_port") + postconfigure_tuniface(testbed_instance, iface_guid) +def crossconnect_tun_iface_peer_both(proto, testbed_instance, iface_guid, peer_iface_data): + crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data) + crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data) + def connect_dep(testbed_instance, node_guid, app_guid): node = testbed_instance._elements[node_guid] app = testbed_instance._elements[app_guid] @@ -437,6 +447,12 @@ connector_types = dict({ "max": 1, "min": 0 }), + "fd->": dict({ + "help": "TUN device file descriptor provider", + "name": "fd->", + "max": 1, + "min": 0 + }), }) connections = [ @@ -532,6 +548,12 @@ connections = [ "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"), "can_cross": True }), + dict({ + "from": (TESTBED_ID, TUNIFACE, "fd->"), + "to": (None, None, "->fd"), + "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"), + "can_cross": True + }), dict({ "from": (TESTBED_ID, TAPIFACE, "tcp"), "to": (None, None, "tcp"), @@ -546,6 +568,12 @@ connections = [ "compl_code": functools.partial(crossconnect_tun_iface_peer_compl,"udp"), "can_cross": True }), + dict({ + "from": (TESTBED_ID, TAPIFACE, "fd->"), + "to": (None, None, "->fd"), + "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"), + "can_cross": True + }), ] attributes = dict({ @@ -910,7 +938,7 @@ factories_info = dict({ "tun_proto", "tun_addr", "tun_port", "tun_key" ], "traces": ["packets"], - "connector_types": ["node","udp","tcp"] + "connector_types": ["node","udp","tcp","fd->"] }), TAPIFACE: dict({ "allow_addresses": True, @@ -925,7 +953,7 @@ factories_info = dict({ "tun_proto", "tun_addr", "tun_port" ], "traces": ["packets"], - "connector_types": ["node","udp","tcp"] + "connector_types": ["node","udp","tcp","fd->"] }), APPLICATION: dict({ "help": "Generic executable command line application", diff --git a/src/nepi/testbeds/planetlab/scripts/tun_connect.py b/src/nepi/testbeds/planetlab/scripts/tun_connect.py index 415423cb..19b08978 100644 --- a/src/nepi/testbeds/planetlab/scripts/tun_connect.py +++ b/src/nepi/testbeds/planetlab/scripts/tun_connect.py @@ -12,6 +12,7 @@ import threading import subprocess import re import functools +import time tun_name = 'tun0' tun_path = '/dev/net/tun' @@ -33,6 +34,13 @@ parser.add_option( "-p", "--port", dest="port", metavar="PORT", type="int", default = 15000, help = "Peering TCP port to connect or listen to.") +parser.add_option( + "--pass-fd", dest="pass_fd", metavar="UNIX_SOCKET", + default = None, + help = "Path to a unix-domain socket to pass the TUN file descriptor to. " + "If given, all other connectivity options are ignored, tun_connect will " + "simply wait to be killed after passing the file descriptor, and it will be " + "the receiver's responsability to handle the tunneling.") parser.add_option( "-m", "--mode", dest="mode", metavar="MODE", @@ -526,7 +534,19 @@ except: try: - if options.udp: + if options.pass_fd: + # send FD to whoever wants it + import passfd + + sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + sock.connect(options.pass_fd) + passfd.sendfd(sock, tun.fileno(), '0') + + # just wait forever + def tun_fwd(tun, remote): + while True: + time.sleep(1) + elif options.udp: # connect to remote endpoint if remaining_args and not remaining_args[0].startswith('-'): print >>sys.stderr, "Listening at: %s:%d" % (hostaddr,options.udp) diff --git a/src/nepi/testbeds/planetlab/tunproto.py b/src/nepi/testbeds/planetlab/tunproto.py index 03293f26..5ba9e1cc 100644 --- a/src/nepi/testbeds/planetlab/tunproto.py +++ b/src/nepi/testbeds/planetlab/tunproto.py @@ -80,7 +80,22 @@ class TunProtoBase(object): if proc.wait(): raise RuntimeError, "Failed upload TUN connect script %r: %s %s" % (source, out,err,) - cmd = "cd %s && gcc -shared tunalloc.c -o tunalloc.so" % (server.shell_escape(self.home_path),) + cmd = ( ( + "cd %(home)s && gcc -shared tunalloc.c -o tunalloc.so" + + ( " && " + "wget -q -c -O python-passfd-src.tar.gz %(passfd_url)s && " + "mkdir -p python-passfd && " + "cd python-passfd && " + "tar xzf ../python-passfd-src.tar.gz --strip-components=1 && " + "python setup.py build && " + "python setup.py install --install-lib .. " + + if local.tun_proto == "fd" else "" + ) ) + % { + 'home' : server.shell_escape(self.home_path), + 'passfd_url' : "http://yans.pl.sophia.inria.fr/code/hgwebdir.cgi/python-passfd/archive/2a6472c64c87.tar.gz", + } ) (out,err),proc = server.popen_ssh_command( cmd, host = local.node.hostname, @@ -122,11 +137,17 @@ class TunProtoBase(object): raise RuntimeError, "Misconfigured TUN: %s" % (local,) args = ["python", "tun_connect.py", - "-m", str(self.mode), - "-p", str(local_port if listen else peer_port), - "-A", str(local_addr), - "-M", str(local_mask), - "-k", str(self.key)] + "-m", str(self.mode)] + + if check_proto == 'fd': + args.extend([ + "--pass-fd", str(peer_addr)]) + else: + args.extend([ + "-p", str(local_port if listen else peer_port), + "-A", str(local_addr), + "-M", str(local_mask), + "-k", str(self.key)]) if local_snat: args.append("-S") @@ -319,6 +340,20 @@ class TunProtoUDP(TunProtoBase): def shutdown(self): self.kill() +class TunProtoFD(TunProtoBase): + def __init__(self, local, peer, home_path, key, listening): + super(TunProtoFD, self).__init__(local, peer, home_path, key) + self.listening = listening + + def prepare(self): + pass + + def setup(self): + self.async_launch('fd', False) + + def shutdown(self): + self.kill() + class TunProtoTCP(TunProtoBase): def __init__(self, local, peer, home_path, key, listening): super(TunProtoTCP, self).__init__(local, peer, home_path, key) @@ -355,6 +390,11 @@ class TapProtoTCP(TunProtoTCP): super(TapProtoTCP, self).__init__(local, peer, home_path, key, listening) self.mode = 'pl-tap' +class TapProtoFD(TunProtoFD): + def __init__(self, local, peer, home_path, key, listening): + super(TapProtoUDP, self).__init__(local, peer, home_path, key, listening) + self.mode = 'pl-tap' + TUN_PROTO_MAP = {