Initial u ntested implementation of TunChannel for netns
[nepi.git] / src / nepi / testbeds / netns / metadata_v01.py
index fd0ce4f..0c5bf0a 100644 (file)
@@ -8,33 +8,73 @@ from nepi.util import validation
 from nepi.util.constants import STATUS_NOT_STARTED, STATUS_RUNNING, \
         STATUS_FINISHED
 
+from nepi.util.tunchannel_impl import \
+    preconfigure_tunchannel, postconfigure_tunchannel, \
+    wait_tunchannel, create_tunchannel, \
+    crossconnect_tunchannel_peer_init, \
+    crossconnect_tunchannel_peer_compl
+
+import functools
+
 NODE = "Node"
 P2PIFACE = "P2PNodeInterface"
 TAPIFACE = "TapNodeInterface"
 NODEIFACE = "NodeInterface"
 SWITCH = "Switch"
 APPLICATION = "Application"
+TUNCHANNEL = "TunChannel"
 
 NS3_TESTBED_ID = "ns3"
 FDNETDEV = "ns3::FileDescriptorNetDevice"
 
 ### Connection functions ####
 
-def connect_switch(testbed_instance, switch, interface):
+def connect_switch(testbed_instance, switch_guid, interface_guid):
+    switch = testbed_instance._elements[switch_guid]
+    interface = testbed_instance._elements[interface_guid]
     switch.connect(interface)
    
-#XXX: This connection function cannot be use to transfer a file descriptor
-# to a remote tap device
-def connect_fd_local(testbed_instance, tap, fdnd):
+def connect_fd(testbed_instance, tap_guid, cross_data):
     import passfd
     import socket
-    fd = tap.file_descriptor
-    address = fdnd.socket_address
+    tap = testbed_instance._elements[tap_guid]
+    address = cross_data["tun_addr"]
     sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
     sock.connect(address)
-    passfd.sendfd(sock, fd, '0')
+    passfd.sendfd(sock, tap.fd, '0')
     # TODO: after succesful transfer, the tap device should close the fd
 
+def connect_tunchannel_tap(testbed_instance, chan_guid, tap_guid):
+    tap = testbed_instance._elements[tap_guid]
+    chan = testbed_instance._elements[chan_guid]
+
+    # Create a file object for the tap's interface device 
+    # and send it to the channel. It should comply with all the
+    # requirements for the channel's tun_socket.
+    import os
+    chan.tun_socket = os.fdopen(tap.fd)
+    
+    # Set the channel to ethernet mode (it's a tap)
+    chan.ethernet_mode = True
+    
+    # Check to see if the device uses PI headers
+    # It's normally so
+    with_pi = True
+    try:
+        import fcntl
+        import struct
+        TUNGETIFF = 0x800454d2
+        IFF_NO_PI = 0x00001000
+        flags = struct.unpack("I",
+            fcntl.ioctl(tap.fd, TUNGETIFF, struct.pack("I",0)) )
+        with_pi = (0 == (flags & IFF_NO_PI))
+    except:
+        # maybe the kernel doesn't support the IOCTL,
+        # in which case, we assume it uses PI headers (as is usual)
+        pass
+    chan.with_pi = with_pi
+
+
 ### Creation functions ###
 
 def create_node(testbed_instance, guid):
@@ -133,6 +173,29 @@ def status_application(testbed_instance, guid):
         return STATUS_RUNNING
     return STATUS_FINISHED
 
+### Configure functions ###
+
+def configure_device(testbed_instance, guid):
+    element = testbed_instance._elements[guid]
+    if not guid in testbed_instance._add_address:
+        return
+    addresses = testbed_instance._add_address[guid]
+    for address in addresses:
+        (address, netprefix, broadcast) = address
+        # TODO: Decide if we should add a ipv4 or ipv6 address
+        element.add_v4_address(address, netprefix)
+
+def configure_node(testbed_instance, guid):
+    element = testbed_instance._elements[guid]
+    if not guid in testbed_instance._add_route:
+        return
+    routes = testbed_instance._add_route[guid]
+    for route in routes:
+        (destination, netprefix, nexthop) = route
+        element.add_route(prefix = destination, prefix_len = netprefix,
+            nexthop = nexthop)
+    
+
 ### Factory information ###
 
 connector_types = dict({
@@ -160,10 +223,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({
@@ -171,52 +240,79 @@ connector_types = dict({
                 "name": "switch",
                 "max": 1, 
                 "min": 0
-            })
+            }),
+    "tcp": dict({
+                "help": "ip-ip tunneling over TCP link", 
+                "name": "tcp",
+                "max": 1, 
+                "min": 0
+            }),
+    "udp": dict({
+                "help": "ip-ip tunneling over UDP datagrams", 
+                "name": "udp",
+                "max": 1, 
+                "min": 0
+            }),
    })
 
 connections = [
     dict({
         "from": (TESTBED_ID, NODE, "devs"),
         "to":   (TESTBED_ID, P2PIFACE, "node"),
-        "code": None,
         "can_cross": False
     }),
     dict({
         "from": (TESTBED_ID, NODE, "devs"),
         "to":   (TESTBED_ID, TAPIFACE, "node"),
-        "code": None,
         "can_cross": False
     }),
     dict({
         "from": (TESTBED_ID, NODE, "devs"),
         "to":   (TESTBED_ID, NODEIFACE, "node"),
-        "code": None,
         "can_cross": False
     }),
     dict({
         "from": (TESTBED_ID, P2PIFACE, "p2p"),
         "to":   (TESTBED_ID, P2PIFACE, "p2p"),
-        "code": None,
         "can_cross": False
     }),
     dict({
-        "from": (TESTBED_ID, TAPIFACE, "fd"),
-        "to":   (NS3_TESTBED_ID, FDNETDEV, "fd"),
-        "code": connect_fd_local,
+        "from": (TESTBED_ID, TAPIFACE, "fd->"),
+        "to":   (None, None, "->fd"),
+        "compl_code": connect_fd,
         "can_cross": True
     }),
      dict({
         "from": (TESTBED_ID, SWITCH, "devs"),
         "to":   (TESTBED_ID, NODEIFACE, "switch"),
-        "code": connect_switch,
+        "init_code": connect_switch,
         "can_cross": False
     }),
     dict({
         "from": (TESTBED_ID, NODE, "apps"),
         "to":   (TESTBED_ID, APPLICATION, "node"),
-        "code": None,
         "can_cross": False
-    })
+    }),
+    dict({
+        "from": (TESTBED_ID, TUNCHANNEL, "->fd" ),
+        "to":   (TESTBED_ID, TAPIFACE, "fd->" ),
+        "init_code": connect_tunchannel_tap,
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, TUNCHANNEL, "tcp"),
+        "to":   (None, None, "tcp"),
+        "init_code": functools.partial(crossconnect_tunchannel_peer_init,"tcp"),
+        "compl_code": functools.partial(crossconnect_tunchannel_peer_compl,"tcp"),
+        "can_cross": True
+    }),
+    dict({
+        "from": (TESTBED_ID, TUNCHANNEL, "udp"),
+        "to":   (None, None, "udp"),
+        "init_code": functools.partial(crossconnect_tunchannel_peer_init,"udp"),
+        "compl_code": functools.partial(crossconnect_tunchannel_peer_compl,"udp"),
+        "can_cross": True
+    }),
 ]
 
 attributes = dict({
@@ -309,15 +405,21 @@ traces = dict({
         }) 
     })
 
-factories_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE, SWITCH,
+create_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE, 
+        TUNCHANNEL, SWITCH,
         APPLICATION ]
 
+configure_order = [ P2PIFACE, NODEIFACE, TAPIFACE, 
+        TUNCHANNEL, SWITCH, 
+        NODE, APPLICATION ]
+
 factories_info = dict({
     NODE: dict({
             "allow_routes": True,
             "help": "Emulated Node with virtualized network stack",
             "category": "topology",
             "create_function": create_node,
+            "configure_function": configure_node,
             "box_attributes": ["forward_X11"],
             "connector_types": ["devs", "apps"]
        }),
@@ -326,6 +428,7 @@ factories_info = dict({
             "help": "Point to point network interface",
             "category": "devices",
             "create_function": create_p2piface,
+            "configure_function": configure_device,
             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
                 "multicast", "broadcast", "arp"],
             "connector_types": ["node", "p2p"]
@@ -335,15 +438,17 @@ factories_info = dict({
             "help": "Tap device network interface",
             "category": "devices",
             "create_function": create_tapiface,
+            "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,
             "help": "Node network interface",
             "category": "devices",
             "create_function": create_nodeiface,
+            "configure_function": configure_device,
             "box_attributes": ["lladdr", "up", "device_name", "mtu", 
                 "multicast", "broadcast", "arp"],
             "connector_types": ["node", "switch"]
@@ -371,6 +476,18 @@ factories_info = dict({
             "connector_types": ["node"],
             "traces": ["stdout", "stderr"]
         }),
+     TUNCHANNEL : dict({
+        "category": "Channel",
+        "create_function": create_tunchannel,
+        "preconfigure_function": preconfigure_tunchannel,
+        "configure_function": postconfigure_tunchannel,
+        "start_function": wait_tunchannel,
+        "help": "Channel to forward "+TAPIFACE+" data to "
+                "other TAP interfaces supporting the NEPI tunneling protocol.",
+        "connector_types": ["->fd", "udp", "tcp"],
+        "allow_addresses": False,
+        "box_attributes": ["tun_proto", "tun_addr", "tun_port", "tun_key"]
+    }),
 })
 
 testbed_attributes = dict({
@@ -381,15 +498,6 @@ testbed_attributes = dict({
                 "value": False,
                 "validation_function": validation.is_bool
             }),
-         "home_directory": dict({
-                "name": "homeDirectory",
-                "help": "Path to the directory where traces and other files \
-                        will be stored",
-                "type": Attribute.STRING,
-                "value": False,
-                "flags": Attribute.DesignOnly,
-                "validation_function": validation.is_string
-            })
     })
 
 class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
@@ -410,8 +518,12 @@ class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
         return traces
 
     @property
-    def factories_order(self):
-        return factories_order
+    def create_order(self):
+        return create_order
+
+    @property
+    def configure_order(self):
+        return configure_order
 
     @property
     def factories_info(self):