From ed43f6aedfff63aaa7c7e8e58064993e7c11fc60 Mon Sep 17 00:00:00 2001 From: Alina Quereilhac Date: Sat, 17 Jan 2015 18:34:58 +0100 Subject: [PATCH] Ns-3/linux tunnel experiments --- src/nepi/resources/linux/application.py | 53 +-- src/nepi/resources/linux/ns3/fdtunnel.py | 362 ++++++++++++++++++ src/nepi/resources/linux/ns3/tap_fd_link.py | 119 ------ .../linux/scripts/linux-ns3-fd-udp-connect.py | 156 ++++++++ .../resources/linux/scripts/linux-passfd.py | 5 +- src/nepi/resources/linux/tunnel.py | 7 +- .../linux/ns3/cross_dce_linux_ccn.py | 4 +- .../linux/ns3/cross_tunnel_ns3_linux_ping.py | 157 ++++++++ 8 files changed, 716 insertions(+), 147 deletions(-) create mode 100644 src/nepi/resources/linux/ns3/fdtunnel.py delete mode 100644 src/nepi/resources/linux/ns3/tap_fd_link.py create mode 100644 src/nepi/resources/linux/scripts/linux-ns3-fd-udp-connect.py create mode 100755 test/resources/linux/ns3/cross_tunnel_ns3_linux_ping.py diff --git a/src/nepi/resources/linux/application.py b/src/nepi/resources/linux/application.py index 23108e54..89a9cfb1 100644 --- a/src/nepi/resources/linux/application.py +++ b/src/nepi/resources/linux/application.py @@ -720,40 +720,49 @@ class LinuxApplication(ResourceManager): return self._state def execute_command(self, command, - env = None, - sudo = False, - tty = False, - forward_x11 = False, - blocking = False): + env=None, + sudo=False, + tty=False, + forward_x11=False, + blocking=False): environ = "" if env: - environ = self.node.format_environment(env, inline = True) + environ = self.node.format_environment(env, inline=True) command = environ + command command = self.replace_paths(command) return self.node.execute(command, - sudo = sudo, - tty = tty, - forward_x11 = forward_x11, - blocking = blocking) + sudo=sudo, + tty=tty, + forward_x11=forward_x11, + blocking=blocking) - def replace_paths(self, command): + def replace_paths(self, command, node=None, app_home=None, run_home=None): """ Replace all special path tags with shell-escaped actual paths. """ + if not node: + node=self.node + + if not app_home: + app_home=self.app_home + + if not run_home: + run_home = self.run_home + return ( command - .replace("${USR}", self.node.usr_dir) - .replace("${LIB}", self.node.lib_dir) - .replace("${BIN}", self.node.bin_dir) - .replace("${SRC}", self.node.src_dir) - .replace("${SHARE}", self.node.share_dir) - .replace("${EXP}", self.node.exp_dir) - .replace("${EXP_HOME}", self.node.exp_home) - .replace("${APP_HOME}", self.app_home) - .replace("${RUN_HOME}", self.run_home) - .replace("${NODE_HOME}", self.node.node_home) - .replace("${HOME}", self.node.home_dir) + .replace("${USR}", node.usr_dir) + .replace("${LIB}", node.lib_dir) + .replace("${BIN}", node.bin_dir) + .replace("${SRC}", node.src_dir) + .replace("${SHARE}", node.share_dir) + .replace("${EXP}", node.exp_dir) + .replace("${EXP_HOME}", node.exp_home) + .replace("${APP_HOME}", app_home) + .replace("${RUN_HOME}", run_home) + .replace("${NODE_HOME}", node.node_home) + .replace("${HOME}", node.home_dir) ) def valid_connection(self, guid): diff --git a/src/nepi/resources/linux/ns3/fdtunnel.py b/src/nepi/resources/linux/ns3/fdtunnel.py new file mode 100644 index 00000000..a934e8d7 --- /dev/null +++ b/src/nepi/resources/linux/ns3/fdtunnel.py @@ -0,0 +1,362 @@ +# +# NEPI, a framework to manage network experiments +# Copyright (C) 2013 INRIA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Author: Alina Quereilhac + +from nepi.execution.attribute import Attribute, Flags, Types +from nepi.execution.resource import clsinit_copy, ResourceState +from nepi.resources.linux.udptunnel import LinuxUdpTunnel +from nepi.util.sshfuncs import ProcStatus +from nepi.util.timefuncs import tnow, tdiffsec + +import base64 +import os +import socket +import time + +@clsinit_copy +class LinuxNs3FdUdpTunnel(LinuxUdpTunnel): + _rtype = "linux::ns3::FdUdpTunnel" + _help = "Constructs a tunnel between two Linux FileDescriptorNetdevices using a UDP connection " + _platform = "linux::ns3" + + @classmethod + def _register_attributes(cls): + cipher = Attribute("cipher", + "Cipher to encript communication. " + "One of PLAIN, AES, Blowfish, DES, DES3. ", + default = None, + allowed = ["PLAIN", "AES", "Blowfish", "DES", "DES3"], + type = Types.Enumerate, + flags = Flags.Design) + + cipher_key = Attribute("cipherKey", + "Specify a symmetric encryption key with which to protect " + "packets across the tunnel. python-crypto must be installed " + "on the system." , + flags = Flags.Design) + + txqueuelen = Attribute("txQueueLen", + "Specifies the interface's transmission queue length. " + "Defaults to 1000. ", + type = Types.Integer, + flags = Flags.Design) + + bwlimit = Attribute("bwLimit", + "Specifies the interface's emulated bandwidth in bytes " + "per second.", + type = Types.Integer, + flags = Flags.Design) + + cls._register_attribute(cipher) + cls._register_attribute(cipher_key) + cls._register_attribute(txqueuelen) + cls._register_attribute(bwlimit) + + def __init__(self, ec, guid): + super(LinuxUdpTunnel, self).__init__(ec, guid) + self._home = "fd-udp-tunnel-%s" % self.guid + self._pids = dict() + self._fd1 = None + self._fd1node = None + self._fd2 = None + self._fd2node = None + + def log_message(self, msg): + self.get_endpoints() + return " guid %d - fd-udptunnel %s - %s - %s " % (self.guid, + self.node1.get("hostname"), + self.node2.get("hostname"), + msg) + + def get_endpoints(self): + """ Returns the list of RM that are endpoints to the tunnel + """ + if not self._fd2 or not self._fd1: + from nepi.resources.ns3.ns3fdnetdevice import NS3BaseFdNetDevice + devices = self.get_connected(NS3BaseFdNetDevice.get_rtype()) + if not devices or len(devices) != 2: + msg = "linux::ns3::TunTapFdLink must be connected to exactly one FdNetDevice" + self.error(msg) + raise RuntimeError, msg + + self._fd1 = devices[0] + self._fd2 = devices[1] + + simu = self._fd1.simulation + from nepi.resources.linux.node import LinuxNode + nodes = simu.get_connected(LinuxNode.get_rtype()) + self._fd1node = nodes[0] + + simu = self._fd2.simulation + from nepi.resources.linux.node import LinuxNode + nodes = simu.get_connected(LinuxNode.get_rtype()) + self._fd2node = nodes[0] + + if self._fd1node.get("hostname") == \ + self._fd2node.get("hostname"): + msg = "linux::ns3::FdUdpTunnel requires endpoints on different hosts" + self.error(msg) + raise RuntimeError, msg + + return [self._fd1, self._fd2] + + @property + def endpoint1(self): + return self._fd1 + + @property + def endpoint2(self): + return self._fd2 + + @property + def node1(self): + return self._fd1node + + @property + def node2(self): + return self._fd2node + + def endpoint_node(self, endpoint): + node = None + if endpoint == self.endpoint1: + node = self.node1 + else: + node = self.node2 + + return node + + def app_home(self, endpoint): + node = self.endpoint_node(endpoint) + return os.path.join(node.exp_home, self._home) + + def run_home(self, endpoint): + return os.path.join(self.app_home(endpoint), self.ec.run_id) + + def upload_sources(self, endpoint): + scripts = [] + + # vif-passfd python script + linux_passfd = os.path.join(os.path.dirname(__file__), + "..", + "scripts", + "linux-ns3-fd-udp-connect.py") + + scripts.append(linux_passfd) + + # Upload scripts + scripts = ";".join(scripts) + + node = self.endpoint_node(endpoint) + node.upload(scripts, + os.path.join(node.src_dir), + overwrite = False) + + def endpoint_mkdir(self, endpoint): + node = self.endpoint_node(endpoint) + run_home = self.run_home(endpoint) + node.mkdir(run_home) + + def initiate_connection(self, endpoint, remote_endpoint): + cipher = self.get("cipher") + cipher_key = self.get("cipherKey") + bwlimit = self.get("bwLimit") + txqueuelen = self.get("txQueueLen") + + # Upload the tunnel creating script + self.upload_sources(endpoint) + + # Request an address to send the file descriptor to the ns-3 simulation + address = endpoint.recv_fd() + + # execute the tunnel creation script + node = self.endpoint_node(remote_endpoint) + remote_ip = node.get("ip") + port = self.initiate(endpoint, address, remote_ip, cipher, + cipher_key, bwlimit, txqueuelen) + + return port + + def establish_connection(self, endpoint, remote_endpoint, port): + self.establish(remote_endpoint, port) + + def verify_connection(self, endpoint, remote_endpoint): + self.verify() + + def terminate_connection(self, endpoint, remote_endpoint): + # Nothing to do + return + + def check_state_connection(self): + # Make sure the process is still running in background + # No execution errors occurred. Make sure the background + # process with the recorded pid is still running. + + node1 = self.endpoint_node(self.endpoint1) + node2 = self.endpoint_node(self.endpoint2) + run_home1 = self.run_home(self.endpoint1) + run_home2 = self.run_home(self.endpoint1) + (pid1, ppid1) = self._pids[endpoint1] + (pid2, ppid2) = self._pids[endpoint2] + + status1 = node1.status(pid1, ppid1) + status2 = node2.status(pid2, ppid2) + + if status1 == ProcStatus.FINISHED and \ + status2 == ProcStatus.FINISHED: + + # check if execution errors occurred + (out1, err1), proc1 = node1.check_errors(run_home1) + (out2, err2), proc2 = node2.check_errors(run_home2) + + if err1 or err2: + msg = "Error occurred in tunnel" + self.error(msg, err1, err2) + self.fail() + else: + self.set_stopped() + + def wait_local_port(self, endpoint): + """ Waits until the local_port file for the endpoint is generated, + and returns the port number + + """ + return self.wait_file(endpoint, "local_port") + + def wait_result(self, endpoint): + """ Waits until the return code file for the endpoint is generated + + """ + return self.wait_file(endpoint, "ret_file") + + def wait_file(self, endpoint, filename): + """ Waits until file on endpoint is generated """ + result = None + delay = 1.0 + + node = self.endpoint_node(endpoint) + run_home = self.run_home(endpoint) + + for i in xrange(20): + (out, err), proc = node.check_output(run_home, filename) + + if out: + result = out.strip() + break + else: + time.sleep(delay) + delay = delay * 1.5 + else: + msg = "Couldn't retrieve %s" % filename + self.error(msg, out, err) + raise RuntimeError, msg + + return result + + def initiate(self, endpoint, address, remote_ip, cipher, cipher_key, + bwlimit, txqueuelen): + command = self._initiate_command(endpoint, address, remote_ip, + cipher, cipher_key, bwlimit, txqueuelen) + + node = self.endpoint_node(endpoint) + run_home = self.run_home(endpoint) + app_home = self.app_home(endpoint) + + # upload command to connect.sh script + shfile = os.path.join(app_home, "fd-udp-connect.sh") + node.upload_command(command, + shfile = shfile, + overwrite = False) + + # invoke connect script + cmd = "bash %s" % shfile + (out, err), proc = node.run(cmd, run_home) + + # check if execution errors occurred + msg = "Failed to connect endpoints " + + if proc.poll(): + self.error(msg, out, err) + raise RuntimeError, msg + + # Wait for pid file to be generated + pid, ppid = node.wait_pid(run_home) + + self._pids[endpoint] = (pid, ppid) + + # Check for error information on the remote machine + (out, err), proc = node.check_errors(run_home) + # Out is what was written in the stderr file + if err: + msg = " Failed to start command '%s' " % command + self.error(msg, out, err) + raise RuntimeError, msg + + port = self.wait_local_port(endpoint) + + return port + + def _initiate_command(self, endpoint, address, remote_ip, + cipher, cipher_key, bwlimit, txqueuelen): + node = self.endpoint_node(endpoint) + run_home = self.run_home(endpoint) + app_home = self.app_home(endpoint) + + local_port_file = os.path.join(run_home, "local_port") + remote_port_file = os.path.join(run_home, "remote_port") + ret_file = os.path.join(run_home, "ret_file") + + address = base64.b64encode(address) + + command = [""] + # Use pl-vid-udp-connect.py to stablish the tunnel between endpoints + command.append("PYTHONPATH=$PYTHONPATH:${SRC}") + command.append("python ${SRC}/linux-ns3-fd-udp-connect.py") + command.append("-a %s" % address) + command.append("-l %s " % local_port_file) + command.append("-r %s " % remote_port_file) + command.append("-H %s " % remote_ip) + command.append("-R %s " % ret_file) + if cipher: + command.append("-c %s " % cipher) + if cipher_key: + command.append("-k %s " % cipher_key) + if txqueuelen: + command.append("-q %s " % txqueuelen) + if bwlimit: + command.append("-b %s " % bwlimit) + + command = " ".join(command) + command = self.replace_paths(command, node=node, app_home=app_home, + run_home=run_home) + + return command + + def establish(self, remote_endpoint, port): + node = self.endpoint_node(endpoint) + run_home = self.run_home(endpoint) + + # upload remote port number to file + remote_port = "%s\n" % port + node.upload(remote_port, + os.path.join(run_home, "remote_port"), + text = True, + overwrite = False) + + def verify(self): + self.wait_result() + diff --git a/src/nepi/resources/linux/ns3/tap_fd_link.py b/src/nepi/resources/linux/ns3/tap_fd_link.py deleted file mode 100644 index 3740f6a3..00000000 --- a/src/nepi/resources/linux/ns3/tap_fd_link.py +++ /dev/null @@ -1,119 +0,0 @@ -# -# NEPI, a framework to manage network experiments -# Copyright (C) 2014 INRIA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Author: Alina Quereilhac - -from nepi.execution.attribute import Attribute, Flags, Types -from nepi.execution.resource import ResourceManager, ResourceState, \ - clsinit_copy - -import os -import socket -import struct -import fcntl - -@clsinit_copy -class TapFdLink(ResourceManager): - """ Interconnects a TAP or TUN Linux device to a FdNetDevice - """ - _rtype = "linux::ns3::TapFdLink" - - def __init__(self, ec, guid): - super(TapFdLink, self).__init__(ec, guid) - self._tap = None - self._fdnetdevice = None - self._fd = None - - @property - def fdnetdevice(self): - if not self._fdnetdevice: - from nepi.resources.ns3.ns3fdnetdevice import NS3BaseFdNetDevice - devices = self.get_connected(NS3BaseFdNetDevice.get_rtype()) - if not devices or len(devices) != 1: - msg = "TapFdLink must be connected to exactly one FdNetDevices" - self.error(msg) - raise RuntimeError, msg - - self._fdnetdevice = devices[0] - - return self._fdnetdevice - - @property - def fdnode(self): - return self.fdnetdevice.node - - @property - def tap(self): - if not self._tap: - from nepi.resources.linux.tap import LinuxTap - devices = self.get_connected(LinuxTap.get_rtype()) - if not devices or len(devices) != 1: - msg = "TapFdLink must be connected to exactly one LinuxTap" - self.error(msg) - raise RuntimeError, msg - - self._tap = devices[0] - - return self._tap - - @property - def tapnode(self): - return self.tap.node - - def do_provision(self): - tap = self.tap - fdnetdevice = self.fdnetdevice - - vif_name = self.ec.get(tap.guid, "deviceName") - vif_type = tap.vif_type_flag - pi = self.ec.get(tap.guid, "pi") - - self._fd = self.open_tap(vif_name, vif_type, pi) - - fdnetdevice.send_fd(self._fd) - - super(TapFdLink, self).do_provision() - - def do_deploy(self): - if self.tap.state < ResourceState.READY or \ - self.fdnetdevice.state < ResourceState.READY: - self.ec.schedule(self.reschedule_delay, self.deploy) - else: - self.do_discover() - self.do_provision() - - super(TapFdLink, self).do_deploy() - - def open_tap(self, vif_name, vif_type, pi): - IFF_NO_PI = 0x1000 - TUNSETIFF = 0x400454ca - - flags = 0 - flags |= vif_type - - if not pi: - flags |= IFF_NO_PI - - fd = os.open("/dev/net/tun", os.O_RDWR) - - err = fcntl.ioctl(fd, TUNSETIFF, struct.pack("16sH", vif_name, flags)) - if err < 0: - os.close(fd) - raise RuntimeError("Could not configure device %s" % vif_name) - - return fd - diff --git a/src/nepi/resources/linux/scripts/linux-ns3-fd-udp-connect.py b/src/nepi/resources/linux/scripts/linux-ns3-fd-udp-connect.py new file mode 100644 index 00000000..4598868e --- /dev/null +++ b/src/nepi/resources/linux/scripts/linux-ns3-fd-udp-connect.py @@ -0,0 +1,156 @@ +import base64 +import errno +import os +import time +import signal +import socket +import tunchannel +import passfd + +from optparse import OptionParser + +# Trak SIGTERM, and set global termination flag instead of dying +TERMINATE = [] +def _finalize(sig,frame): + global TERMINATE + TERMINATE.append(None) +signal.signal(signal.SIGTERM, _finalize) + +# SIGUSR1 suspends forwading, SIGUSR2 resumes forwarding +SUSPEND = [] +def _suspend(sig,frame): + global SUSPEND + if not SUSPEND: + SUSPEND.append(None) +signal.signal(signal.SIGUSR1, _suspend) + +def _resume(sig,frame): + global SUSPEND + if SUSPEND: + SUSPEND.remove(None) +signal.signal(signal.SIGUSR2, _resume) + +def get_options(): + usage = ("usage: %prog -a
" + "-b -c -k -q " + "-l -r -H " + "-R ") + + parser = OptionParser(usage = usage) + + parser.add_option("-a", "--address", dest="address", + help="Socket address to send file descriptor to", type="str") + + parser.add_option("-b", "--bwlimit", dest="bwlimit", + help="Specifies the interface's emulated bandwidth in bytes ", + default=None, type="int") + parser.add_option("-q", "--txqueuelen", dest="txqueuelen", + help="Specifies the interface's transmission queue length. ", + default=1000, type="int") + parser.add_option("-c", "--cipher", dest="cipher", + help="Cipher to encript communication. " + "One of PLAIN, AES, Blowfish, DES, DES3. ", + default=None, type="str") + parser.add_option("-k", "--cipher-key", dest="cipher_key", + help="Specify a symmetric encryption key with which to protect " + "packets across the tunnel. python-crypto must be installed " + "on the system." , + default=None, type="str") + + parser.add_option("-l", "--local-port-file", dest="local_port_file", + help = "File where to store the local binded UDP port number ", + default = "local_port_file", type="str") + parser.add_option("-r", "--remote-port-file", dest="remote_port_file", + help = "File where to read the remote UDP port number to connect to", + default = "remote_port_file", type="str") + parser.add_option("-H", "--remote-host", dest="remote_host", + help = "Remote host IP", default = "remote_host", type="str") + parser.add_option("-R", "--ret-file", dest="ret_file", + help = "File where to store return code (success of connection) ", + default = "ret_file", type="str") + + (options, args) = parser.parse_args() + + address = base64.b64decode(options.address) + + return ( address, + options.local_port_file, options.remote_port_file, + options.remote_host, options.ret_file, options.bwlimit, + options.cipher, options.cipher_key, options.txqueuelen ) + +if __name__ == '__main__': + + (address, local_port_file, remote_port_file, remote_host, ret_file, + bwlimit, cipher, cipher_key, txqueuelen) = get_options() + + # Create a local socket to stablish the tunnel connection + hostaddr = socket.gethostbyname(socket.gethostname()) + rsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) + rsock.bind((hostaddr, 0)) + (local_host, local_port) = rsock.getsockname() + + # Save local port information to file + f = open(local_port_file, 'w') + f.write("%d\n" % local_port) + f.close() + + # Wait until remote port information is available + while not os.path.exists(remote_port_file): + time.sleep(2) + + remote_port = '' + # Read remote port from file + # Try until something is read... + # xxx: There seems to be a weird behavior where + # even if the file exists and had the port number, + # the read operation returns empty string! + # Maybe a race condition? + for i in xrange(10): + f = open(remote_port_file, 'r') + remote_port = f.read() + f.close() + + if remote_port: + break + + time.sleep(2) + + remote_port = remote_port.strip() + remote_port = int(remote_port) + + # Connect local socket to remote port + rsock.connect((remote_host, remote_port)) + remote = os.fdopen(rsock.fileno(), 'r+b', 0) + + # create local socket to pass to fd-net-device + lsock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + lsock.bind("") + lsock.getsockname() + local = os.fdopen(lsock.fileno(), 'r+b', 0) + + # pass local socket to fd-net-device + sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + sock.connect(address) + passfd.sendfd(sock, local, '0') + + # TODO: Test connectivity! + + # Create a ret_file to indicate success + f = open(ret_file, 'w') + f.write("0") + f.close() + + # Establish tunnel + tunchannel.tun_fwd(local, remote, + with_pi = False, # No PI headers + ether_mode = IFF_TAP, # Ns-3 generates ethernet pkts + udp = True, + cipher_key = cipher_key, + cipher = cipher, + TERMINATE = TERMINATE, + SUSPEND = SUSPEND, + tunqueue = txqueuelen, + tunkqueue = 500, + bwlimit = bwlimit + ) + diff --git a/src/nepi/resources/linux/scripts/linux-passfd.py b/src/nepi/resources/linux/scripts/linux-passfd.py index 51cf4e87..ff7014ed 100644 --- a/src/nepi/resources/linux/scripts/linux-passfd.py +++ b/src/nepi/resources/linux/scripts/linux-passfd.py @@ -33,8 +33,10 @@ def get_options(): vif_type = IFF_TAP if options.vif_type and options.vif_type == "IFF_TUN": vif_type = IFF_TUN + + address = base64.b64decode(options.address) - return (options.address, options.vif_name, vif_type, options.pi) + return (address, options.vif_name, vif_type, options.pi) if __name__ == '__main__': @@ -53,7 +55,6 @@ if __name__ == '__main__': os.close(fd) raise RuntimeError("Could not retrive file descriptor from %s" % vif_name) - address = base64.b64decode(address) sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) sock.connect(address) diff --git a/src/nepi/resources/linux/tunnel.py b/src/nepi/resources/linux/tunnel.py index 43c7f997..b2a6f0f8 100644 --- a/src/nepi/resources/linux/tunnel.py +++ b/src/nepi/resources/linux/tunnel.py @@ -64,6 +64,9 @@ class LinuxTunnel(LinuxApplication): def run_home(self, endpoint): return os.path.join(self.app_home(endpoint), self.ec.run_id) + def endpoint_mkdir(self, endpoint): + self.endpoint.node.mkdir(self.run_home(self.endpoint)) + def initiate_connection(self, endpoint, remote_endpoint): raise NotImplementedError @@ -81,8 +84,8 @@ class LinuxTunnel(LinuxApplication): def do_provision(self): # create run dir for tunnel on each node - self.endpoint1.node.mkdir(self.run_home(self.endpoint1)) - self.endpoint2.node.mkdir(self.run_home(self.endpoint2)) + self.endpoint_mkdir(self.endpoint1) + self.endpoint_mkdir(self.endpoint2) self.debug("Initiate the connection") # Start 2 step connection diff --git a/test/resources/linux/ns3/cross_dce_linux_ccn.py b/test/resources/linux/ns3/cross_dce_linux_ccn.py index 714371cb..d84f6cd5 100755 --- a/test/resources/linux/ns3/cross_dce_linux_ccn.py +++ b/test/resources/linux/ns3/cross_dce_linux_ccn.py @@ -154,7 +154,7 @@ class LinuxNS3FdNetDeviceTest(unittest.TestCase): @skipIfNotAlive def t_cross_ccncat(self, host, user = None, identity = None): - ec = ExperimentController(exp_id = "test-linux-ns3-ccnpeek") + ec = ExperimentController(exp_id = "test-linux-ns3-ccncat") node = ec.register_resource("linux::Node") if host == "localhost": @@ -241,7 +241,7 @@ class LinuxNS3FdNetDeviceTest(unittest.TestCase): #ec.wait_finished([ccncat]) import time - time.sleep(60) + time.sleep(80) stdout = ec.trace(ccncat, "stdout") f = open("bunny.ts", "w") diff --git a/test/resources/linux/ns3/cross_tunnel_ns3_linux_ping.py b/test/resources/linux/ns3/cross_tunnel_ns3_linux_ping.py new file mode 100755 index 00000000..14d4de95 --- /dev/null +++ b/test/resources/linux/ns3/cross_tunnel_ns3_linux_ping.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# +# NEPI, a framework to manage network experiments +# Copyright (C) 2013 INRIA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Author: Alina Quereilhac + +from nepi.execution.ec import ExperimentController +from nepi.execution.resource import ResourceState, ResourceAction +from nepi.execution.trace import TraceAttr + +from test_utils import skipIfNotAlive + +import os +import time +import unittest + +def add_ns3_node(ec, simu): + node = ec.register_resource("ns3::Node") + ec.set(node, "enableStack", True) + ec.register_connection(node, simu) + + return node + +def add_fd_device(ec, node, ip, prefix): + dev = ec.register_resource("ns3::FdNetDevice") + ec.set(dev, "ip", ip) + ec.set(dev, "prefix", prefix) + ec.register_connection(node, dev) + + return dev + +def add_tap_device(ec, node, ip, prefix): + dev = ec.register_resource("linux::Tap") + ec.set(dev, "ip", ip) + ec.set(dev, "prefix", prefix) + ec.register_connection(node, dev) + + return dev + +def add_point2point_device(ec, node, ip, prefix): + dev = ec.register_resource("ns3::PointToPointNetDevice") + ec.set(dev, "ip", ip) + ec.set(dev, "prefix", prefix) + ec.register_connection(node, dev) + + queue = ec.register_resource("ns3::DropTailQueue") + ec.register_connection(dev, queue) + + return dev + +class LinuxNS3FdNetDeviceTest(unittest.TestCase): + def setUp(self): + self.fedora_host = "nepi2.pl.sophia.inria.fr" + self.fedora_user = "inria_nepi" + self.fedora_identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME']) + + self.ubuntu_host = "roseval.pl.sophia.inria.fr" + self.ubuntu_user = "inria_nepi" + self.ubuntu_identity = "%s/.ssh/id_rsa" % (os.environ['HOME']) + + @skipIfNotAlive + def t_cross_tunnel_ping(self, host, user = None, identity = None): + ec = ExperimentController(exp_id = "test-linux-ns3-tap-fd") + + node1 = ec.register_resource("linux::Node") + ec.set(node1, "hostname", "localhost") + ec.set(node1, "cleanProcesses", True) + ec.set(node1, "cleanExperiment", True) + + simu1 = ec.register_resource("linux::ns3::Simulation") + ec.set(simu1, "simulatorImplementationType", "ns3::RealtimeSimulatorImpl") + ec.set(simu1, "checksumEnabled", True) + ec.set(simu1, "verbose", True) + #ec.set(simu1, "buildMode", "debug") + #ec.set(simu1, "nsLog", "FdNetDevice") + ec.register_connection(simu1, node1) + + nsnode1 = ec.register_resource("ns3::Node") + ec.set(nsnode1, "enableStack", True) + ec.register_connection(nsnode1, simu1) + + fddev1 = ec.register_resource("ns3::FdNetDevice") + ec.set(fddev1, "ip", "10.0.0.1") + ec.set(fddev1, "prefix", "30") + ec.register_connection(nsnode1, fddev1) + + node2 = ec.register_resource("linux::Node") + ec.set(node2, "hostname", host) + ec.set(node2, "username", user) + ec.set(node2, "identity", identity) + ec.set(node2, "cleanProcesses", True) + ec.set(node2, "cleanExperiment", True) + + simu2 = ec.register_resource("linux::ns3::Simulation") + ec.set(simu2, "simulatorImplementationType", "ns3::RealtimeSimulatorImpl") + ec.set(simu2, "checksumEnabled", True) + ec.set(simu2, "verbose", True) + #ec.set(simu2, "buildMode", "debug") + #ec.set(simu2, "nsLog", "FdNetDevice") + ec.register_connection(simu2, node2) + + nsnode2 = ec.register_resource("ns3::Node") + ec.set(nsnode2, "enableStack", True) + ec.register_connection(nsnode2, simu2) + + fddev2 = ec.register_resource("ns3::FdNetDevice") + ec.set(fddev2, "ip", "10.0.0.2") + ec.set(fddev2, "prefix", "30") + ec.register_connection(nsnode2, fddev2) + + tunnel = ec.register_resource("linux::ns3::FdUdpTunnel") + ec.register_connection(tunnel, fddev1) + ec.register_connection(tunnel, fddev2) + + ping = ec.register_resource("ns3::V4Ping") + ec.set (ping, "Remote", "10.0.0.2") + ec.set (ping, "Interval", "1s") + ec.set (ping, "Verbose", True) + ec.set (ping, "StartTime", "0s") + ec.set (ping, "StopTime", "20s") + ec.register_connection(ping, nsnode1) + + ec.deploy() + + ec.wait_finished([ping]) + + stdout = ec.trace(simu1, "stdout") + + expected = "20 packets transmitted, 20 received, 0% packet loss" + self.assertTrue(stdout.find(expected) > -1) + + ec.shutdown() + + def ztest_cross_ping_fedora(self): + self.t_cross_ping(self.fedora_host, self.fedora_user, self.fedora_identity) + + def test_cross_tunnel_ping_ubuntu(self): + self.t_cross_tunnel_ping(self.ubuntu_host, self.ubuntu_user, self.ubuntu_identity) + + +if __name__ == '__main__': + unittest.main() + -- 2.43.0