Merge TCP handshake stuff
[nepi.git] / src / nepi / testbeds / planetlab / metadata.py
index 203b22d..b388707 100644 (file)
@@ -16,6 +16,7 @@ from nepi.util.constants import ApplicationStatus as AS, \
 import functools
 import os
 import os.path
+import weakref
 
 NODE = "Node"
 NODEIFACE = "NodeInterface"
@@ -27,6 +28,16 @@ NEPIDEPENDENCY = "NepiDependency"
 NS3DEPENDENCY = "NS3Dependency"
 INTERNET = "Internet"
 NETPIPE = "NetPipe"
+TUNFILTER = "TunFilter"
+CLASSQUEUEFILTER = "ClassQueueFilter"
+TOSQUEUEFILTER = "TosQueueFilter"
+MULTICASTFORWARDER = "MulticastForwarder"
+MULTICASTANNOUNCER = "MulticastAnnouncer"
+MULTICASTROUTER = "MulticastRouter"
+
+TUNFILTERS = (TUNFILTER, CLASSQUEUEFILTER, TOSQUEUEFILTER)
+TAPFILTERS = (TUNFILTER, )
+ALLFILTERS = (TUNFILTER, CLASSQUEUEFILTER, TOSQUEUEFILTER)
 
 PL_TESTBED_ID = "planetlab"
 
@@ -103,16 +114,54 @@ def connect_tun_iface_peer(proto, testbed_instance, iface_guid, peer_iface_guid)
     iface = testbed_instance._elements[iface_guid]
     peer_iface = testbed_instance._elements[peer_iface_guid]
     iface.peer_iface = peer_iface
+    peer_iface.peer_iface = iface
     iface.peer_proto = \
-    iface.tun_proto = proto
+    iface.tun_proto = \
+    peer_iface.peer_proto = \
+    peer_iface.tun_proto = proto
     iface.tun_key = peer_iface.tun_key
 
+def connect_tun_iface_filter(testbed_instance, iface_guid, filter_guid):
+    iface = testbed_instance._elements[iface_guid]
+    filt = testbed_instance._elements[filter_guid]
+    traces = testbed_instance._get_traces(filter_guid)
+    if 'dropped_stats' in traces: 
+        args = filt.args if filt.args else ""
+        filt.args = ','.join(filt.args.split(',') + ["logdropped=true",])
+    iface.filter_module = filt
+    filt.iface_guid = iface_guid
+    filt.iface = weakref.ref(iface)
+
+    if filt.peer_guid:
+        connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
+
+def connect_filter_peer(proto, testbed_instance, filter_guid, peer_guid):
+    peer = testbed_instance._elements[peer_guid]
+    filt = testbed_instance._elements[filter_guid]
+    filt.peer_proto = proto
+    filt.peer_guid = peer_guid
+    if filt.iface_guid:
+        connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
+
+def connect_filter_filter(proto, testbed_instance, filter_guid, peer_guid):
+    peer = testbed_instance._elements[peer_guid]
+    filt = testbed_instance._elements[filter_guid]
+    filt.peer_proto = proto
+    peer.peer_proto = proto
+    if filt.iface_guid:
+        peer.peer_guid = filt.iface_guid
+    if peer.iface_guid:
+        filt.peer_guid = peer.iface_guid
+    if filt.iface_guid and filt.peer_guid:
+        connect_tun_iface_peer(filt.peer_proto, testbed_instance, filt.iface_guid, filt.peer_guid)
+
 def crossconnect_tun_iface_peer_init(proto, testbed_instance, iface_guid, peer_iface_data):
     iface = testbed_instance._elements[iface_guid]
     iface.peer_iface = None
     iface.peer_addr = peer_iface_data.get("tun_addr")
     iface.peer_proto = peer_iface_data.get("tun_proto") or proto
     iface.peer_port = peer_iface_data.get("tun_port")
+    iface.peer_cipher = peer_iface_data.get("tun_cipher")
     iface.tun_key = min(iface.tun_key, peer_iface_data.get("tun_key"))
     iface.tun_proto = proto
     
@@ -124,6 +173,7 @@ def crossconnect_tun_iface_peer_compl(proto, testbed_instance, iface_guid, peer_
     iface.peer_addr = peer_iface_data.get("tun_addr")
     iface.peer_proto = peer_iface_data.get("tun_proto") or proto
     iface.peer_port = peer_iface_data.get("tun_port")
+    iface.peer_cipher = peer_iface_data.get("tun_cipher")
     
     postconfigure_tuniface(testbed_instance, iface_guid)
 
@@ -131,9 +181,23 @@ def crossconnect_tun_iface_peer_both(proto, testbed_instance, iface_guid, peer_i
     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]
+def crossconnect_filter_peer_init(proto, testbed_instance, filter_guid, peer_data):
+    filt = testbed_instance._elements[filter_guid]
+    filt.peer_proto = proto
+    crossconnect_tun_iface_peer_init(filt.peer_proto, testbed_instance, filt.iface_guid, peer_data)
+
+def crossconnect_filter_peer_compl(proto, testbed_instance, filter_guid, peer_data):
+    filt = testbed_instance._elements[filter_guid]
+    filt.peer_proto = proto
+    crossconnect_tun_iface_peer_compl(filt.peer_proto, testbed_instance, filt.iface_guid, peer_data)
+
+def crossconnect_filter_peer_both(proto, testbed_instance, filter_guid, peer_data):
+    crossconnect_filter_peer_init(proto, testbed_instance, iface_guid, peer_iface_data)
+    crossconnect_filter_peer_compl(proto, testbed_instance, iface_guid, peer_iface_data)
+
+def connect_dep(testbed_instance, node_guid, app_guid, node=None, app=None):
+    node = node or testbed_instance._elements[node_guid]
+    app = app or testbed_instance._elements[app_guid]
     app.node = node
     
     if app.depends:
@@ -152,6 +216,24 @@ def connect_dep(testbed_instance, node_guid, app_guid):
     if app.rpmFusion:
         node.rpmFusion = True
 
+def connect_forwarder(testbed_instance, node_guid, fwd_guid):
+    node = testbed_instance._elements[node_guid]
+    fwd = testbed_instance._elements[fwd_guid]
+    node.multicast_forwarder = fwd
+    
+    if fwd.router:
+        connect_dep(testbed_instance, node_guid, None, app=fwd.router)
+
+    connect_dep(testbed_instance, node_guid, fwd_guid)
+
+def connect_router(testbed_instance, fwd_guid, router_guid):
+    fwd = testbed_instance._elements[fwd_guid]
+    router = testbed_instance._elements[router_guid]
+    fwd.router = router
+    
+    if fwd.node:
+        connect_dep(testbed_instance, None, router_guid, node=fwd.node)
+
 def connect_node_netpipe(testbed_instance, node_guid, netpipe_guid):
     node = testbed_instance._elements[node_guid]
     netpipe = testbed_instance._elements[netpipe_guid]
@@ -179,7 +261,9 @@ def create_node(testbed_instance, guid):
     # require vroute vsys if we have routes to set up
     routes = testbed_instance._add_route.get(guid)
     if routes:
-        element.required_vsys.add("vroute")
+        vsys = element.routing_method(routes,
+            testbed_instance.vsys_vnet)
+        element.required_vsys.add(vsys)
     
     testbed_instance.elements[guid] = element
 
@@ -216,6 +300,21 @@ def create_tapiface(testbed_instance, guid):
     
     testbed_instance.elements[guid] = element
 
+def create_tunfilter(testbed_instance, guid):
+    parameters = testbed_instance._get_parameters(guid)
+    element = testbed_instance._make_tun_filter(parameters)
+    testbed_instance.elements[guid] = element
+
+def create_classqueuefilter(testbed_instance, guid):
+    parameters = testbed_instance._get_parameters(guid)
+    element = testbed_instance._make_class_queue_filter(parameters)
+    testbed_instance.elements[guid] = element
+
+def create_tosqueuefilter(testbed_instance, guid):
+    parameters = testbed_instance._get_parameters(guid)
+    element = testbed_instance._make_tos_queue_filter(parameters)
+    testbed_instance.elements[guid] = element
+
 def create_application(testbed_instance, guid):
     parameters = testbed_instance._get_parameters(guid)
     element = testbed_instance._make_application(parameters)
@@ -252,6 +351,33 @@ def create_ns3_dependency(testbed_instance, guid):
     
     testbed_instance.elements[guid] = element
 
+def create_multicast_forwarder(testbed_instance, guid):
+    parameters = testbed_instance._get_parameters(guid)
+    element = testbed_instance._make_multicast_forwarder(parameters)
+    
+    # Just inject configuration stuff
+    element.home_path = "nepi-mcfwd-%s" % (guid,)
+    
+    testbed_instance.elements[guid] = element
+
+def create_multicast_announcer(testbed_instance, guid):
+    parameters = testbed_instance._get_parameters(guid)
+    element = testbed_instance._make_multicast_announcer(parameters)
+    
+    # Just inject configuration stuff
+    element.home_path = "nepi-mcann-%s" % (guid,)
+    
+    testbed_instance.elements[guid] = element
+
+def create_multicast_router(testbed_instance, guid):
+    parameters = testbed_instance._get_parameters(guid)
+    element = testbed_instance._make_multicast_router(parameters)
+    
+    # Just inject configuration stuff
+    element.home_path = "nepi-mcrt-%s" % (guid,)
+    
+    testbed_instance.elements[guid] = element
+
 def create_internet(testbed_instance, guid):
     parameters = testbed_instance._get_parameters(guid)
     element = testbed_instance._make_internet(parameters)
@@ -272,6 +398,7 @@ def start_application(testbed_instance, guid):
     app.stdout = "stdout" in traces
     app.stderr = "stderr" in traces
     app.buildlog = "buildlog" in traces
+    app.outout = "output" in traces
     
     app.start()
 
@@ -344,36 +471,19 @@ def preconfigure_tuniface(testbed_instance, guid):
     element.validate()
     
     # First-phase setup
-    if element.peer_proto:
-        if element.peer_iface and isinstance(element.peer_iface, testbed_instance._interfaces.TunIface):
-            # intra tun
-            listening = id(element) < id(element.peer_iface)
-        else:
-            # cross tun
-            if not element.tun_addr or not element.tun_port:
-                listening = True
-            elif not element.peer_addr or not element.peer_port:
-                listening = True
-            else:
-                # both have addresses...
-                # ...the one with the lesser address listens
-                listening = element.tun_addr < element.peer_addr
-        element.prepare( 
-            'tun-%s' % (guid,),
-             listening)
+    element.prepare('tun-%s' % (guid,))
 
 def postconfigure_tuniface(testbed_instance, guid):
     element = testbed_instance._elements[guid]
     
     # Second-phase setup
-    element.setup()
+    element.launch()
     
-def wait_tuniface(testbed_instance, guid):
+def prestart_tuniface(testbed_instance, guid):
     element = testbed_instance._elements[guid]
     
     # Second-phase setup
-    element.async_launch_wait()
-    
+    element.wait()
 
 def configure_node(testbed_instance, guid):
     node = testbed_instance._elements[guid]
@@ -399,8 +509,10 @@ def configure_node_routes(testbed_instance, guid):
             for dev_guid in testbed_instance.get_connected(guid, "devs", "node")
             for dev in ( testbed_instance._elements.get(dev_guid) ,)
             if dev and isinstance(dev, testbed_instance._interfaces.TunIface) ]
+    
+        vsys = testbed_instance.vsys_vnet
         
-        node.configure_routes(routes, devs)
+        node.configure_routes(routes, devs, vsys)
 
 def configure_application(testbed_instance, guid):
     app = testbed_instance._elements[guid]
@@ -426,6 +538,41 @@ def configure_dependency(testbed_instance, guid):
     # Install stuff
     dep.async_setup()
 
+def configure_announcer(testbed_instance, guid):
+    # Link ifaces
+    fwd = testbed_instance._elements[guid]
+    fwd.ifaces = [ dev
+        for node_guid in testbed_instance.get_connected(guid, "node", "apps")
+        for dev_guid in testbed_instance.get_connected(node_guid, "devs", "node")
+        for dev in ( testbed_instance._elements.get(dev_guid) ,)
+        if dev and isinstance(dev, testbed_instance._interfaces.TunIface)
+            and dev.multicast ]
+    
+    # Install stuff
+    configure_dependency(testbed_instance, guid)
+
+def configure_forwarder(testbed_instance, guid):
+    configure_announcer(testbed_instance, guid)
+    
+    # Link ifaces to forwarder
+    fwd = testbed_instance._elements[guid]
+    for iface in fwd.ifaces:
+        iface.multicast_forwarder = '/var/run/mcastfwd'
+
+def configure_router(testbed_instance, guid):
+    # Link ifaces
+    rt = testbed_instance._elements[guid]
+    rt.nonifaces = [ dev
+        for fwd_guid in testbed_instance.get_connected(guid, "fwd", "router")
+        for node_guid in testbed_instance.get_connected(fwd_guid, "node", "apps")
+        for dev_guid in testbed_instance.get_connected(node_guid, "devs", "node")
+        for dev in ( testbed_instance._elements.get(dev_guid) ,)
+        if dev and isinstance(dev, testbed_instance._interfaces.TunIface)
+            and not dev.multicast ]
+    
+    # Install stuff
+    configure_dependency(testbed_instance, guid)
+
 def configure_netpipe(testbed_instance, guid):
     netpipe = testbed_instance._elements[guid]
     
@@ -472,6 +619,18 @@ connector_types = dict({
                 "max": 1, 
                 "min": 1
             }),
+    "router": dict({
+                "help": "Connector to a routing daemon", 
+                "name": "router",
+                "max": 1, 
+                "min": 1
+            }),
+    "fwd": dict({
+                "help": "Forwarder this routing daemon communicates with", 
+                "name": "fwd",
+                "max": 1, 
+                "min": 1
+            }),
     "pipes": dict({
                 "help": "Connector to a NetPipe", 
                 "name": "pipes",
@@ -503,6 +662,12 @@ connector_types = dict({
                 "max": 1, 
                 "min": 0
             }),
+    "->fd": dict({
+                "help": "TUN device file descriptor slot", 
+                "name": "->fd",
+                "max": 1, 
+                "min": 0
+            }),
    })
 
 connections = [
@@ -532,32 +697,20 @@ connections = [
     }),
     dict({
         "from": (TESTBED_ID, NODE, "apps"),
-        "to":   (TESTBED_ID, APPLICATION, "node"),
-        "init_code": connect_dep,
-        "can_cross": False
-    }),
-    dict({
-        "from": (TESTBED_ID, NODE, "deps"),
-        "to":   (TESTBED_ID, DEPENDENCY, "node"),
-        "init_code": connect_dep,
-        "can_cross": False
-    }),
-    dict({
-        "from": (TESTBED_ID, NODE, "deps"),
-        "to":   (TESTBED_ID, NEPIDEPENDENCY, "node"),
+        "to":   (TESTBED_ID, (APPLICATION, DEPENDENCY, NEPIDEPENDENCY, NS3DEPENDENCY, MULTICASTANNOUNCER), "node"),
         "init_code": connect_dep,
         "can_cross": False
     }),
     dict({
-        "from": (TESTBED_ID, NODE, "deps"),
-        "to":   (TESTBED_ID, NS3DEPENDENCY, "node"),
-        "init_code": connect_dep,
+        "from": (TESTBED_ID, NODE, "apps"),
+        "to":   (TESTBED_ID, MULTICASTFORWARDER, "node"),
+        "init_code": connect_forwarder,
         "can_cross": False
     }),
     dict({
-        "from": (TESTBED_ID, NODE, "pipes"),
-        "to":   (TESTBED_ID, NETPIPE, "node"),
-        "init_code": connect_node_netpipe,
+        "from": (TESTBED_ID, MULTICASTFORWARDER, "router"),
+        "to":   (TESTBED_ID, MULTICASTROUTER, "fwd"),
+        "init_code": connect_router,
         "can_cross": False
     }),
     dict({
@@ -578,6 +731,24 @@ connections = [
         "init_code": functools.partial(connect_tun_iface_peer,"gre"),
         "can_cross": False
     }),
+    dict({
+        "from": (TESTBED_ID, TUNIFACE, "fd->"),
+        "to":   (TESTBED_ID, TUNFILTERS, "->fd"),
+        "init_code": connect_tun_iface_filter,
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, TUNFILTERS, "tcp"),
+        "to":   (TESTBED_ID, TUNIFACE, "tcp"),
+        "init_code": functools.partial(connect_filter_peer,"tcp"),
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, TUNFILTERS, "udp"),
+        "to":   (TESTBED_ID, TUNIFACE, "udp"),
+        "init_code": functools.partial(connect_filter_peer,"udp"),
+        "can_cross": False
+    }),
     dict({
         "from": (TESTBED_ID, TAPIFACE, "tcp"),
         "to":   (TESTBED_ID, TAPIFACE, "tcp"),
@@ -596,6 +767,48 @@ connections = [
         "init_code": functools.partial(connect_tun_iface_peer,"gre"),
         "can_cross": False
     }),
+    dict({
+        "from": (TESTBED_ID, TAPIFACE, "fd->"),
+        "to":   (TESTBED_ID, TAPFILTERS, "->fd"),
+        "init_code": connect_tun_iface_filter,
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, TAPFILTERS, "tcp"),
+        "to":   (TESTBED_ID, TAPIFACE, "tcp"),
+        "init_code": functools.partial(connect_filter_peer,"tcp"),
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, TAPFILTERS, "udp"),
+        "to":   (TESTBED_ID, TAPIFACE, "udp"),
+        "init_code": functools.partial(connect_filter_peer,"udp"),
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, TUNFILTERS, "tcp"),
+        "to":   (TESTBED_ID, TUNFILTERS, "tcp"),
+        "init_code": functools.partial(connect_filter_filter,"tcp"),
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, TUNFILTERS, "udp"),
+        "to":   (TESTBED_ID, TUNFILTERS, "udp"),
+        "init_code": functools.partial(connect_filter_filter,"udp"),
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, TAPFILTERS, "tcp"),
+        "to":   (TESTBED_ID, TAPFILTERS, "tcp"),
+        "init_code": functools.partial(connect_filter_filter,"tcp"),
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, TAPFILTERS, "udp"),
+        "to":   (TESTBED_ID, TAPFILTERS, "udp"),
+        "init_code": functools.partial(connect_filter_filter,"udp"),
+        "can_cross": False
+    }),
     dict({
         "from": (TESTBED_ID, TUNIFACE, "tcp"),
         "to":   (None, None, "tcp"),
@@ -650,6 +863,20 @@ connections = [
         "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"gre"),
         "can_cross": True
     }),
+    dict({
+        "from": (TESTBED_ID, ALLFILTERS, "tcp"),
+        "to":   (None, None, "tcp"),
+        "init_code": functools.partial(crossconnect_filter_peer_init,"tcp"),
+        "compl_code": functools.partial(crossconnect_filter_peer_compl,"tcp"),
+        "can_cross": True
+    }),
+    dict({
+        "from": (TESTBED_ID, ALLFILTERS, "udp"),
+        "to":   (None, None, "udp"),
+        "init_code": functools.partial(crossconnect_filter_peer_init,"udp"),
+        "compl_code": functools.partial(crossconnect_filter_peer_compl,"udp"),
+        "can_cross": True
+    }),
 ]
 
 attributes = dict({
@@ -799,8 +1026,8 @@ attributes = dict({
                 "value": True,
                 "validation_function": validation.is_bool
             }),
-    "device_name": dict({
-                "name": "name",
+    "if_name": dict({
+                "name": "if_name",
                 "help": "Device name",
                 "type": Attribute.STRING,
                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
@@ -826,6 +1053,15 @@ attributes = dict({
                 "value": False,
                 "validation_function": validation.is_bool
             }),
+    "multicast":  dict({
+                "name": "multicast", 
+                "help": "Enable multicast forwarding on this device. "
+                        "Note that you still need a multicast routing daemon "
+                        "in the node.",
+                "type": Attribute.BOOL,
+                "value": False,
+                "validation_function": validation.is_bool
+            }),
     "pointopoint":  dict({
                 "name": "pointopoint", 
                 "help": "If the interface is a P2P link, the remote endpoint's IP "
@@ -834,6 +1070,14 @@ attributes = dict({
                 "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
+    "bwlimit":  dict({
+                "name": "bwlimit", 
+                "help": "Emulated transmission speed (in kbytes per second)",
+                "type": Attribute.INTEGER,
+                "range" : (1,10*2**20),
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
+                "validation_function": validation.is_integer
+            }),
     "txqueuelen":  dict({
                 "name": "txqueuelen", 
                 "help": "Transmission queue length (in packets)",
@@ -992,6 +1236,29 @@ attributes = dict({
                 "range": (0,60000),
                 "validation_function": validation.is_integer,
             }),
+    "module": dict({
+                "name": "module",
+                "help": "Path to a .c or .py source for a filter module, or a binary .so",
+                "type": Attribute.STRING,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
+                "validation_function": validation.is_string
+            }),
+    "args": dict({
+                "name": "args",
+                "help": "Module arguments - comma-separated list of name=value pairs",
+                "type": Attribute.STRING,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
+                "validation_function": validation.is_string
+            }),
+    "routing_algorithm": dict({      
+            "name": "algorithm",
+            "help": "Routing algorithm.",
+            "value": "dvmrp",
+            "type": Attribute.ENUM, 
+            "allowed": ["dvmrp"],
+            "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
+            "validation_function": validation.is_enum,
+        }),
     })
 
 traces = dict({
@@ -1021,17 +1288,44 @@ traces = dict({
                 "name": "pcap",
                 "help": "PCAP trace of all packets going through the interface",
               }),
+    "output": dict({
+                "name": "output",
+                "help": "Extra output trace for applications. When activated this trace can be referenced with wildcard a reference from an Application command line. Ex: command: 'tcpdump -w {#[elemet-label].trace[trace-id].[name|path]#}' ",
+              }),
+    "dropped_stats": dict({
+                "name": "dropped_stats",
+                "help": "Information on dropped packets on a filer or queue associated to a network interface",
+            }),
     })
 
-create_order = [ INTERNET, NODE, NODEIFACE, TAPIFACE, TUNIFACE, NETPIPE, NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
+create_order = [ 
+    INTERNET, NODE, NODEIFACE, CLASSQUEUEFILTER, TOSQUEUEFILTER, 
+    MULTICASTANNOUNCER, MULTICASTFORWARDER, MULTICASTROUTER, 
+    TUNFILTER, TAPIFACE, TUNIFACE, NETPIPE, 
+    NEPIDEPENDENCY, NS3DEPENDENCY, DEPENDENCY, APPLICATION ]
 
-configure_order = [ INTERNET, Parallel(NODE), NODEIFACE, Parallel(TAPIFACE), Parallel(TUNIFACE), NETPIPE, Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), Parallel(APPLICATION) ]
+configure_order = [ 
+    INTERNET, Parallel(NODE), 
+    NODEIFACE, 
+    Parallel(MULTICASTANNOUNCER), Parallel(MULTICASTFORWARDER), Parallel(MULTICASTROUTER), 
+    Parallel(TAPIFACE), Parallel(TUNIFACE), NETPIPE, 
+    Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), Parallel(APPLICATION) ]
 
 # Start (and prestart) node after ifaces, because the node needs the ifaces in order to set up routes
-start_order = [ INTERNET, NODEIFACE, Parallel(TAPIFACE), Parallel(TUNIFACE), Parallel(NODE), NETPIPE, Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), Parallel(APPLICATION) ]
+start_order = [ INTERNET, 
+    NODEIFACE, 
+    Parallel(TAPIFACE), Parallel(TUNIFACE), 
+    Parallel(NODE), NETPIPE, 
+    Parallel(MULTICASTANNOUNCER), Parallel(MULTICASTFORWARDER), Parallel(MULTICASTROUTER), 
+    Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), Parallel(APPLICATION) ]
 
 # cleanup order
-shutdown_order = [ Parallel(APPLICATION), Parallel(TAPIFACE), Parallel(TUNIFACE), Parallel(NETPIPE), Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), NODEIFACE, Parallel(NODE) ]
+shutdown_order = [ 
+    Parallel(APPLICATION), 
+    Parallel(MULTICASTROUTER), Parallel(MULTICASTFORWARDER), Parallel(MULTICASTANNOUNCER), 
+    Parallel(TAPIFACE), Parallel(TUNIFACE), Parallel(NETPIPE), 
+    Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), 
+    NODEIFACE, Parallel(NODE) ]
 
 factories_info = dict({
     NODE: dict({
@@ -1072,11 +1366,11 @@ factories_info = dict({
             "create_function": create_tuniface,
             "preconfigure_function": preconfigure_tuniface,
             "configure_function": postconfigure_tuniface,
-            "prestart_function": wait_tuniface,
+            "prestart_function": prestart_tuniface,
             "box_attributes": [
-                "up", "device_name", "mtu", "snat", "pointopoint",
+                "up", "if_name", "mtu", "snat", "pointopoint", "multicast", "bwlimit",
                 "txqueuelen",
-                "tun_proto", "tun_addr", "tun_port", "tun_key"
+                "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
             ],
             "traces": ["packets", "pcap"],
             "connector_types": ["node","udp","tcp","fd->","gre"],
@@ -1088,16 +1382,120 @@ factories_info = dict({
             "create_function": create_tapiface,
             "preconfigure_function": preconfigure_tuniface,
             "configure_function": postconfigure_tuniface,
-            "prestart_function": wait_tuniface,
+            "prestart_function": prestart_tuniface,
             "box_attributes": [
-                "up", "device_name", "mtu", "snat", "pointopoint",
+                "up", "if_name", "mtu", "snat", "pointopoint", "multicast", "bwlimit",
                 "txqueuelen",
-                "tun_proto", "tun_addr", "tun_port", "tun_key"
+                "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
             ],
             "traces": ["packets", "pcap"],
             "connector_types": ["node","udp","tcp","fd->","gre"],
             "tags": [tags.INTERFACE, tags.ALLOW_ADDRESSES],
         }),
+    TUNFILTER: dict({
+            "help": "TUN/TAP stream filter\n\n"
+                    "If specified, it should be either a .py or .so module. "
+                    "It will be loaded, and all incoming and outgoing packets "
+                    "will be routed through it. The filter will not be responsible "
+                    "for buffering, packet queueing is performed in tun_connect "
+                    "already, so it should not concern itself with it. It should "
+                    "not, however, block in one direction if the other is congested.\n"
+                    "\n"
+                    "Modules are expected to have the following methods:\n"
+                    "\tinit(**args)\n"
+                    "\t\tIf arguments are given, this method will be called with the\n"
+                    "\t\tgiven arguments (as keyword args in python modules, or a single\n"
+                    "\taccept_packet(packet, direction):\n"
+                    "\t\tDecide whether to drop the packet. Direction is 0 for packets "
+                        "coming from the local side to the remote, and 1 is for packets "
+                        "coming from the remote side to the local. Return a boolean, "
+                        "true if the packet is not to be dropped.\n"
+                    "\tfilter_init():\n"
+                    "\t\tInitializes a filtering pipe (filter_run). It should "
+                        "return two file descriptors to use as a bidirectional "
+                        "pipe: local and remote. 'local' is where packets from the "
+                        "local side will be written to. After filtering, those packets "
+                        "should be written to 'remote', where tun_connect will read "
+                        "from, and it will forward them to the remote peer. "
+                        "Packets from the remote peer will be written to 'remote', "
+                        "where the filter is expected to read from, and eventually "
+                        "forward them to the local side. If the file descriptors are "
+                        "not nonblocking, they will be set to nonblocking. So it's "
+                        "better to set them from the start like that.\n"
+                    "\tfilter_run(local, remote):\n"
+                    "\t\tIf filter_init is provided, it will be called repeatedly, "
+                        "in a separate thread until the process is killed. It should "
+                        "sleep at most for a second.\n"
+                    "\tfilter_close(local, remote):\n"
+                    "\t\tCalled then the process is killed, if filter_init was provided. "
+                        "It should, among other things, close the file descriptors.\n"
+                    "\n"
+                    "Python modules are expected to return a tuple in filter_init, "
+                    "either of file descriptors or file objects, while native ones "
+                    "will receive two int*.\n"
+                    "\n"
+                    "Python modules can additionally contain a custom queue class "
+                    "that will replace the FIFO used by default. The class should "
+                    "be named 'queueclass' and contain an interface compatible with "
+                    "collections.deque. That is, indexing (especiall for q[0]), "
+                    "bool(q), popleft, appendleft, pop (right), append (right), "
+                    "len(q) and clear.",
+            "category": FC.CATEGORY_CHANNELS,
+            "create_function": create_tunfilter,
+            "box_attributes": [
+                "module", "args",
+                "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
+            ],
+            "connector_types": ["->fd","udp","tcp"],
+        }),
+    CLASSQUEUEFILTER : dict({
+            "help": "TUN classfull queue, uses a separate queue for each user-definable class.\n\n"
+                    "It takes two arguments, both of which have sensible defaults:\n"
+                    "\tsize: the base size of each class' queue\n"
+                    "\tclasses: the class definitions, which follow the following syntax:\n"
+                    '\t   <CLASSLIST> ::= <CLASS> ":" CLASSLIST\n'
+                    '\t                |  <CLASS>\n'
+                    '\t   <CLASS>     ::= <PROTOLIST> "*" <PRIORITYSPEC>\n'
+                    '\t                |  <DFLTCLASS>\n'
+                    '\t   <DFLTCLASS> ::= "*" <PRIORITYSPEC>\n'
+                    '\t   <PROTOLIST> ::= <PROTO> "." <PROTOLIST>\n'
+                    '\t                |  <PROTO>\n'
+                    '\t   <PROTO>     ::= <NAME> | <NUMBER>\n'
+                    '\t   <NAME>      ::= --see http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers --\n'
+                    '\t                   --only in lowercase, with special characters removed--\n'
+                    '\t                   --or see below--\n'
+                    '\t   <NUMBER>    ::= [0-9]+\n'
+                    '\t   <PRIORITYSPEC> ::= <THOUGHPUT> [ "#" <SIZE> ] [ "p" <PRIORITY> ]\n'
+                    '\t   <THOUGHPUT> ::= NUMBER -- default 1\n'
+                    '\t   <PRIORITY>  ::= NUMBER -- default 0\n'
+                    '\t   <SIZE>      ::= NUMBER -- default 1\n'
+                    "\n"
+                    "Size, thoughput and priority are all relative terms. "
+                    "Sizes are multipliers for the size argument, thoughput "
+                    "is applied relative to other classes and the same with "
+                    "priority.",
+            "category": FC.CATEGORY_CHANNELS,
+            "create_function": create_classqueuefilter,
+            "box_attributes": [
+                "args",
+                "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
+            ],
+            "connector_types": ["->fd","udp","tcp"],
+            "traces": ["dropped_stats"],
+        }),
+    TOSQUEUEFILTER : dict({
+            "help": "TUN classfull queue that classifies according to the TOS (RFC 791) IP field.\n\n"
+                    "It takes a size argument that specifies the size of each class. As TOS is a "
+                    "subset of DiffServ, this queue half-implements DiffServ.",
+            "category": FC.CATEGORY_CHANNELS,
+            "create_function": create_tosqueuefilter,
+            "box_attributes": [
+                "args",
+                "tun_proto", "tun_addr", "tun_port", "tun_key", "tun_cipher",
+            ],
+            "connector_types": ["->fd","udp","tcp"],
+        }),
+
     APPLICATION: dict({
             "help": "Generic executable command line application",
             "category": FC.CATEGORY_APPLICATIONS,
@@ -1110,7 +1508,7 @@ factories_info = dict({
                                "depends", "build-depends", "build", "install",
                                "sources", "rpm-fusion" ],
             "connector_types": ["node"],
-            "traces": ["stdout", "stderr", "buildlog"],
+            "traces": ["stdout", "stderr", "buildlog", "output"],
             "tags": [tags.APPLICATION],
         }),
     DEPENDENCY: dict({
@@ -1141,6 +1539,51 @@ factories_info = dict({
             "connector_types": ["node"],
             "traces": ["buildlog"],
         }),
+    MULTICASTFORWARDER: dict({
+            "help": "This application installs a userspace packet forwarder "
+                    "that, when connected to a node, filters all packets "
+                    "flowing through multicast-capable virtual interfaces "
+                    "and applies custom-specified routing policies.",
+            "category": FC.CATEGORY_APPLICATIONS,
+            "create_function": create_multicast_forwarder,
+            "preconfigure_function": configure_forwarder,
+            "start_function": start_application,
+            "status_function": status_application,
+            "stop_function": stop_application,
+            "box_attributes": [ ],
+            "connector_types": ["node","router"],
+            "traces": ["buildlog","stderr"],
+        }),
+    MULTICASTANNOUNCER: dict({
+            "help": "This application installs a userspace daemon that "
+                    "monitors multicast membership and announces it on all "
+                    "multicast-capable interfaces.\n"
+                    "This does not usually happen automatically on PlanetLab slivers.",
+            "category": FC.CATEGORY_APPLICATIONS,
+            "create_function": create_multicast_announcer,
+            "preconfigure_function": configure_announcer,
+            "start_function": start_application,
+            "status_function": status_application,
+            "stop_function": stop_application,
+            "box_attributes": [ ],
+            "connector_types": ["node"],
+            "traces": ["buildlog","stderr"],
+        }),
+    MULTICASTROUTER: dict({
+            "help": "This application installs a userspace daemon that "
+                    "monitors multicast membership and announces it on all "
+                    "multicast-capable interfaces.\n"
+                    "This does not usually happen automatically on PlanetLab slivers.",
+            "category": FC.CATEGORY_APPLICATIONS,
+            "create_function": create_multicast_router,
+            "preconfigure_function": configure_router,
+            "start_function": start_application,
+            "status_function": status_application,
+            "stop_function": stop_application,
+            "box_attributes": ["routing_algorithm"],
+            "connector_types": ["fwd"],
+            "traces": ["buildlog","stdout","stderr"],
+        }),
     INTERNET: dict({
             "help": "Internet routing",
             "category": FC.CATEGORY_CHANNELS,