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')
"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({
"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
}),
"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,
"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({
# 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 ###
"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
}),
"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
}),
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
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):
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]
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]
"max": 1,
"min": 0
}),
+ "fd->": dict({
+ "help": "TUN device file descriptor provider",
+ "name": "fd->",
+ "max": 1,
+ "min": 0
+ }),
})
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"),
"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({
"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,
"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",
import subprocess
import re
import functools
+import time
tun_name = 'tun0'
tun_path = '/dev/net/tun'
"-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",
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)
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,
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")
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)
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 = {