From 47e7024b4c75a1bba05eecdf404bb271cb841f46 Mon Sep 17 00:00:00 2001 From: Claudio-Daniel Freire Date: Tue, 9 Aug 2011 17:43:19 +0200 Subject: [PATCH] TunChannel improvements: - Use python-iovec library for increased performance - In PlanetLab, support for GRE tunnels --- src/nepi/testbeds/planetlab/metadata.py | 49 +++++++++- .../testbeds/planetlab/scripts/tun_connect.py | 95 +++++++++++++++++-- src/nepi/testbeds/planetlab/tunproto.py | 55 ++++++++++- src/nepi/util/tunchannel.py | 84 +++++++++------- test/testbeds/planetlab/execute.py | 8 ++ test/testbeds/planetlab/integration_multi.py | 5 + 6 files changed, 250 insertions(+), 46 deletions(-) diff --git a/src/nepi/testbeds/planetlab/metadata.py b/src/nepi/testbeds/planetlab/metadata.py index 92647b8c..203b22d4 100644 --- a/src/nepi/testbeds/planetlab/metadata.py +++ b/src/nepi/testbeds/planetlab/metadata.py @@ -491,6 +491,12 @@ connector_types = dict({ "max": 1, "min": 0 }), + "gre": dict({ + "help": "IP or Ethernet tunneling using the GRE protocol", + "name": "gre", + "max": 1, + "min": 0 + }), "fd->": dict({ "help": "TUN device file descriptor provider", "name": "fd->", @@ -566,6 +572,12 @@ connections = [ "init_code": functools.partial(connect_tun_iface_peer,"udp"), "can_cross": False }), + dict({ + "from": (TESTBED_ID, TUNIFACE, "gre"), + "to": (TESTBED_ID, TUNIFACE, "gre"), + "init_code": functools.partial(connect_tun_iface_peer,"gre"), + "can_cross": False + }), dict({ "from": (TESTBED_ID, TAPIFACE, "tcp"), "to": (TESTBED_ID, TAPIFACE, "tcp"), @@ -578,6 +590,12 @@ connections = [ "init_code": functools.partial(connect_tun_iface_peer,"udp"), "can_cross": False }), + dict({ + "from": (TESTBED_ID, TAPIFACE, "gre"), + "to": (TESTBED_ID, TAPIFACE, "gre"), + "init_code": functools.partial(connect_tun_iface_peer,"gre"), + "can_cross": False + }), dict({ "from": (TESTBED_ID, TUNIFACE, "tcp"), "to": (None, None, "tcp"), @@ -598,6 +616,12 @@ connections = [ "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"), "can_cross": True }), + dict({ + "from": (TESTBED_ID, TUNIFACE, "gre"), + "to": (None, None, "gre"), + "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"gre"), + "can_cross": True + }), dict({ "from": (TESTBED_ID, TAPIFACE, "tcp"), "to": (None, None, "tcp"), @@ -618,6 +642,14 @@ connections = [ "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"fd"), "can_cross": True }), + # EGRE is an extension of PlanetLab, so we can't connect externally + # if the other testbed isn't another PlanetLab + dict({ + "from": (TESTBED_ID, TAPIFACE, "gre"), + "to": (TESTBED_ID, None, "gre"), + "compl_code": functools.partial(crossconnect_tun_iface_peer_both,"gre"), + "can_cross": True + }), ] attributes = dict({ @@ -998,6 +1030,9 @@ configure_order = [ INTERNET, Parallel(NODE), NODEIFACE, Parallel(TAPIFACE), Par # 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) ] +# cleanup order +shutdown_order = [ Parallel(APPLICATION), Parallel(TAPIFACE), Parallel(TUNIFACE), Parallel(NETPIPE), Parallel(NEPIDEPENDENCY), Parallel(NS3DEPENDENCY), Parallel(DEPENDENCY), NODEIFACE, Parallel(NODE) ] + factories_info = dict({ NODE: dict({ "help": "Virtualized Node (V-Server style)", @@ -1044,7 +1079,7 @@ factories_info = dict({ "tun_proto", "tun_addr", "tun_port", "tun_key" ], "traces": ["packets", "pcap"], - "connector_types": ["node","udp","tcp","fd->"], + "connector_types": ["node","udp","tcp","fd->","gre"], "tags": [tags.INTERFACE, tags.ALLOW_ADDRESSES], }), TAPIFACE: dict({ @@ -1060,7 +1095,7 @@ factories_info = dict({ "tun_proto", "tun_addr", "tun_port", "tun_key" ], "traces": ["packets", "pcap"], - "connector_types": ["node","udp","tcp","fd->"], + "connector_types": ["node","udp","tcp","fd->","gre"], "tags": [tags.INTERFACE, tags.ALLOW_ADDRESSES], }), APPLICATION: dict({ @@ -1213,6 +1248,16 @@ testbed_attributes = dict({ "range": (2000,30000), "validation_function": validation.is_integer_range(2000,30000) }), + "dedicated_slice": dict({ + "name": "dedicatedSlice", + "help": "Set to True if the slice will be dedicated to this experiment. " + "NEPI will perform node and slice cleanup, making sure slices are " + "in a clean, repeatable state before running the experiment.", + "type": Attribute.BOOL, + "value": False, + "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable, + "validation_function": validation.is_bool + }), }) supported_recovery_policies = [ diff --git a/src/nepi/testbeds/planetlab/scripts/tun_connect.py b/src/nepi/testbeds/planetlab/scripts/tun_connect.py index 8f794309..0eb46e39 100644 --- a/src/nepi/testbeds/planetlab/scripts/tun_connect.py +++ b/src/nepi/testbeds/planetlab/scripts/tun_connect.py @@ -16,6 +16,7 @@ import re import functools import time import base64 +import traceback import tunchannel @@ -51,7 +52,7 @@ parser.add_option( "-m", "--mode", dest="mode", metavar="MODE", default = "none", help = - "Set mode. One of none, tun, tap, pl-tun, pl-tap. In any mode except none, a TUN/TAP will be created " + "Set mode. One of none, tun, tap, pl-tun, pl-tap, pl-gre-ip, pl-gre-eth. In any mode except none, a TUN/TAP will be created " "by using the proper interface (tunctl for tun/tap, /vsys/fd_tuntap.control for pl-tun/pl-tap), " "and it will be brought up (with ifconfig for tun/tap, with /vsys/vif_up for pl-tun/pl-tap). You have " "to specify an VIF_ADDRESS and VIF_MASK in any case (except for none).") @@ -96,6 +97,11 @@ parser.add_option( help = "Specify a symmetric encryption key with which to protect packets across " "the tunnel. python-crypto must be installed on the system." ) +parser.add_option( + "-K", "--gre-key", dest="gre_key", metavar="KEY", type="int", + default = None, + help = + "Specify a demultiplexing 32-bit numeric key for GRE." ) parser.add_option( "-N", "--no-capture", dest="no_capture", action = "store_true", @@ -144,7 +150,14 @@ class HostLock(object): processcond.release() self.lockfile = lockfile - fcntl.flock(self.lockfile, fcntl.LOCK_EX) + + while True: + try: + fcntl.flock(self.lockfile, fcntl.LOCK_EX) + break + except (OSError, IOError), e: + if e.args[0] != os.errno.EINTR: + raise def __del__(self): processcond = self.__class__.processcond @@ -196,6 +209,12 @@ def tunclose(tun_path, tun_name, tun): # close TUN object tun.close() +def noopen(tun_path, tun_name): + print >>sys.stderr, "Using tun:", tun_name + return None +def noclose(tun_path, tun_name, tun): + pass + def tuntap_alloc(kind, tun_path, tun_name): args = ["tunctl"] if kind == "tun": @@ -285,7 +304,39 @@ def pl_tuntap_alloc(kind, tun_path, tun_name): name = c_tun_name.value return str(fd), name +_name_reservation = None +def pl_tuntap_namealloc(kind, tun_path, tun_name): + global _name_reservation + # Serialize access + lockfile = open("/tmp/nepi-tun-connect.lock", "a") + _name_reservation = lock = HostLock(lockfile) + + # We need to do this, fd_tuntap is the only one who can + # tell us our slice id (this script runs as root, so no uid), + # and the pattern of device names accepted by vsys scripts + tunalloc_so = ctypes.cdll.LoadLibrary("./tunalloc.so") + c_tun_name = ctypes.c_char_p("\x00"*IFNAMSIZ) # the string will be mutated! + nkind= {"tun":IFF_TUN, + "tap":IFF_TAP}[kind] + fd = tunalloc_so.tun_alloc(nkind, c_tun_name) + name = c_tun_name.value + os.close(fd) + + base = name[:name.index('-')+1] + existing = set(map(str.strip,os.popen("ip a | grep -o '%s[0-9]*'" % (base,)).read().strip().split('\n'))) + + for i in xrange(9000,10000): + name = base + str(i) + if name not in existing: + break + else: + raise RuntimeError, "Could not assign interface name" + + return None, name + def pl_vif_start(tun_path, tun_name): + global _name_reservation + out = [] def outreader(): stdout = open("/vsys/vif_up.out","r") @@ -295,8 +346,9 @@ def pl_vif_start(tun_path, tun_name): # Serialize access to vsys lockfile = open("/tmp/nepi-tun-connect.lock", "a") - lock = HostLock(lockfile) - + lock = _name_reservation or HostLock(lockfile) + _name_reservation = None + stdin = open("/vsys/vif_up.in","w") t = threading.Thread(target=outreader) @@ -311,6 +363,9 @@ def pl_vif_start(tun_path, tun_name): stdin.write("pointopoint=%s\n" % (options.vif_pointopoint,)) if options.vif_txqueuelen is not None: stdin.write("txqueuelen=%d\n" % (options.vif_txqueuelen,)) + if options.mode.startswith('pl-gre'): + stdin.write("gre=%d\n" % (options.gre_key,)) + stdin.write("remote=%s\n" % (remaining_args[0],)) stdin.close() t.join() @@ -326,7 +381,13 @@ def pl_vif_stop(tun_path, tun_name): stdout = open("/vsys/vif_down.out","r") out.append(stdout.read()) stdout.close() - time.sleep(1) + + while True: + ifaces = set(map(str.strip,os.popen("ip a | grep -o '%s'" % (tun_name,)).read().strip().split('\n'))) + if tun_name in ifaces: + time.sleep(1) + else: + break # Serialize access to vsys lockfile = open("/tmp/nepi-tun-connect.lock", "a") @@ -397,6 +458,16 @@ MODEINFO = { dealloc=nop, start=pl_vif_start, stop=pl_vif_stop), + 'pl-gre-ip' : dict(alloc=functools.partial(pl_tuntap_namealloc, "tun"), + tunopen=noopen, tunclose=tunclose, + dealloc=nop, + start=pl_vif_start, + stop=pl_vif_stop), + 'pl-gre-eth': dict(alloc=functools.partial(pl_tuntap_namealloc, "tap"), + tunopen=noopen, tunclose=noclose, + dealloc=nop, + start=pl_vif_start, + stop=pl_vif_stop), } tun_path = options.tun_path @@ -456,10 +527,16 @@ try: passfd.sendfd(sock, tun.fileno(), '0') # just wait forever - def tun_fwd(tun, remote): + def tun_fwd(tun, remote, **kw): while not TERMINATE: time.sleep(1) remote = None + elif options.mode.startswith('pl-gre'): + # just wait forever + def tun_fwd(tun, remote, **kw): + while not TERMINATE: + time.sleep(1) + remote = remaining_args[0] elif options.udp: # connect to remote endpoint if remaining_args and not remaining_args[0].startswith('-'): @@ -558,17 +635,17 @@ finally: try: modeinfo['stop'](tun_path, tun_name) except: - pass + traceback.print_exc() try: modeinfo['tunclose'](tun_path, tun_name, tun) except: - pass + traceback.print_exc() try: modeinfo['dealloc'](tun_path, tun_name) except: - pass + traceback.print_exc() print >>sys.stderr, "TERMINATED GRACEFULLY" diff --git a/src/nepi/testbeds/planetlab/tunproto.py b/src/nepi/testbeds/planetlab/tunproto.py index 08a33ed7..d3cf6b83 100644 --- a/src/nepi/testbeds/planetlab/tunproto.py +++ b/src/nepi/testbeds/planetlab/tunproto.py @@ -107,7 +107,17 @@ class TunProtoBase(object): local.node.wait_dependencies() cmd = ( ( - "cd %(home)s && gcc -fPIC -shared tunalloc.c -o tunalloc.so" + "cd %(home)s && " + "gcc -fPIC -shared tunalloc.c -o tunalloc.so && " + + "wget -q -c -O python-iovec-src.tar.gz %(iovec_url)s && " + "mkdir -p python-iovec && " + "cd python-iovec && " + "tar xzf ../python-iovec-src.tar.gz --strip-components=1 && " + "python setup.py build && " + "python setup.py install --install-lib .. && " + "cd .. " + + ( " && " "wget -q -c -O python-passfd-src.tar.gz %(passfd_url)s && " "mkdir -p python-passfd && " @@ -117,10 +127,12 @@ class TunProtoBase(object): "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", + 'iovec_url' : "http://yans.pl.sophia.inria.fr/code/hgwebdir.cgi/python-iovec/archive/tip.tar.gz", } ) (out,err),proc = server.popen_ssh_command( cmd, @@ -186,6 +198,10 @@ class TunProtoBase(object): args.extend([ "--pass-fd", passfd_arg ]) + elif check_proto == 'gre': + args.extend([ + "-K", str(min(local_port, peer_port)) + ]) else: args.extend([ "-p", str(local_port if listen else peer_port), @@ -312,9 +328,14 @@ class TunProtoBase(object): out = out.strip() - match = re.match(r"Using +tun: +([-a-zA-Z0-9]*) +.*",out) + match = re.match(r"Using +tun: +([-a-zA-Z0-9]*).*",out) if match: self._if_name = match.group(1) + elif out: + self._logger.debug("if_name: %r does not match expected pattern", out) + time.sleep(1) + else: + pself._logger.warn("if_name: Could not get interface name") return self._if_name def async_launch(self, check_proto, listen, extra_args=[]): @@ -540,6 +561,27 @@ class TunProtoFD(TunProtoBase): def launch(self, check_proto='fd', listen=False, extra_args=[]): super(TunProtoFD, self).launch(check_proto, listen, extra_args) +class TunProtoGRE(TunProtoBase): + def __init__(self, local, peer, home_path, key, listening): + super(TunProtoGRE, self).__init__(local, peer, home_path, key) + self.listening = listening + self.mode = 'pl-gre-ip' + + def prepare(self): + pass + + def setup(self): + self.async_launch('gre', False) + + def shutdown(self): + self.kill() + + def destroy(self): + self.waitkill() + + def launch(self, check_proto='gre', listen=False, extra_args=[]): + super(TunProtoGRE, self).launch(check_proto, listen, extra_args) + class TunProtoTCP(TunProtoBase): def __init__(self, local, peer, home_path, key, listening): super(TunProtoTCP, self).__init__(local, peer, home_path, key) @@ -587,18 +629,25 @@ class TapProtoFD(TunProtoFD): super(TapProtoFD, self).__init__(local, peer, home_path, key, listening) self.mode = 'pl-tap' +class TapProtoGRE(TunProtoGRE): + def __init__(self, local, peer, home_path, key, listening): + super(TapProtoGRE, self).__init__(local, peer, home_path, key, listening) + self.mode = 'pl-gre-eth' + TUN_PROTO_MAP = { 'tcp' : TunProtoTCP, 'udp' : TunProtoUDP, 'fd' : TunProtoFD, + 'gre' : TunProtoGRE, } TAP_PROTO_MAP = { 'tcp' : TapProtoTCP, 'udp' : TapProtoUDP, 'fd' : TapProtoFD, + 'gre' : TapProtoGRE, } diff --git a/src/nepi/util/tunchannel.py b/src/nepi/util/tunchannel.py index db0a6d93..9e34b45f 100644 --- a/src/nepi/util/tunchannel.py +++ b/src/nepi/util/tunchannel.py @@ -9,6 +9,8 @@ import fcntl import traceback import functools import collections +import ctypes +import time def ipfmt(ip): ipbytes = map(ord,ip.decode("hex")) @@ -24,7 +26,7 @@ tagtype = { } def etherProto(packet, len=len): if len(packet) > 14: - if packet[12:14] == "\x81\x00": + if packet[12] == "\x81" and packet[13] == "\x00": # tagged return packet[16:18] else: @@ -190,7 +192,8 @@ def nonblock(fd): def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr=sys.stderr, reconnect=None, rwrite=None, rread=None, tunqueue=1000, tunkqueue=1000, cipher='AES', - len=len, max=max, OSError=OSError, select=select.select, selecterror=select.error, piWrap=piWrap, piStrip=piStrip, os=os, socket=socket): + len=len, max=max, OSError=OSError, select=select.select, selecterror=select.error, os=os, socket=socket, + retrycodes=(os.errno.EWOULDBLOCK, os.errno.EAGAIN, os.errno.EINTR) ): crypto_mode = False try: if cipher_key: @@ -232,18 +235,48 @@ def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr rnonblock = nonblock(remote) tnonblock = nonblock(tun) + # Pick up TUN/TAP writing method + if with_pi: + try: + import iovec + + # We have iovec, so we can skip PI injection + # and use iovec which does it natively + if ether_mode: + twrite = iovec.ethpiwrite + tread = iovec.piread2 + else: + twrite = iovec.ippiwrite + tread = iovec.piread2 + except ImportError: + # We have to inject PI headers pythonically + def twrite(fd, packet, oswrite=os.write, piWrap=piWrap, ether_mode=ether_mode): + return oswrite(fd, piWrap(packet, ether_mode)) + + # For reading, we strip PI headers with buffer slicing and that's it + def tread(fd, maxlen, osread=os.read, piStrip=piStrip): + return piStrip(osread(fd, maxlen)) + else: + # No need to inject PI headers + twrite = os.write + tread = os.read + # Limited frame parsing, to preserve packet boundaries. # Which is needed, since /dev/net/tun is unbuffered maxbkbuf = maxfwbuf = max(10,tunqueue-tunkqueue) tunhurry = max(0,maxbkbuf/2) fwbuf = collections.deque() bkbuf = collections.deque() + nfwbuf = 0 + nbkbuf = 0 if ether_mode: packetReady = bool pullPacket = collections.deque.popleft + reschedule = collections.deque.appendleft else: packetReady = _packetReady pullPacket = _pullPacket + reschedule = collections.deque.appendleft tunfd = tun.fileno() os_read = os.read os_write = os.write @@ -290,16 +323,9 @@ def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr packet = pullPacket(fwbuf) if crypto_mode: - enpacket = encrypt_(packet, crypter) - else: - enpacket = packet + packet = encrypt_(packet, crypter) - # try twice - sometimes it barks the first time, - # due to ICMP Port Unreachable packets from previous writes - try: - rwrite(remote, enpacket) - except socket.error: - rwrite(remote, enpacket) + rwrite(remote, packet) #wr += 1 if not rnonblock or not packetReady(fwbuf): @@ -308,9 +334,9 @@ def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr # This except handles the entire While block on PURPOSE # as an optimization (setting a try/except block is expensive) # The only operation that can raise this exception is rwrite - if e.errno == os.errno.EWOULDBLOCK: + if e.errno in retrycodes: # re-schedule packet - fwbuf.insert(0, packet) + reschedule(fwbuf, packet) else: raise except: @@ -323,14 +349,12 @@ def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr elif not udp: # in UDP mode, we ignore errors - packet loss man... raise - traceback.print_exc(file=sys.stderr) + #traceback.print_exc(file=sys.stderr) if tun in wrdy: try: - while 1: + for x in xrange(50): packet = pullPacket(bkbuf) - if with_pi: - packet = piWrap(packet, ether_mode) - os_write(tunfd, packet) + twrite(tunfd, packet) #wt += 1 # Do not inject packets into the TUN faster than they arrive, unless we're falling @@ -339,13 +363,16 @@ def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr # we'll have high packet loss. if not tnonblock or len(bkbuf) < tunhurry or not packetReady(bkbuf): break + else: + # Give some time for the kernel to process the packets + time.sleep(0) except OSError,e: # This except handles the entire While block on PURPOSE # as an optimization (setting a try/except block is expensive) # The only operation that can raise this exception is os_write - if e.errno == os.errno.EWOULDBLOCK: + if e.errno in retrycodes: # re-schedule packet - bkbuf.insert(0, packet) + reschedule(bkbuf, packet) else: raise @@ -353,10 +380,8 @@ def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr if tun in rdrdy: try: while 1: - packet = os_read(tunfd,2000) # tun.read blocks until it gets 2k! + packet = tread(tunfd,2000) # tun.read blocks until it gets 2k! #rt += 1 - if with_pi: - packet = piStrip(packet) fwbuf.append(packet) if not tnonblock or len(fwbuf) >= maxfwbuf: @@ -365,18 +390,13 @@ def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr # This except handles the entire While block on PURPOSE # as an optimization (setting a try/except block is expensive) # The only operation that can raise this exception is os_read - if e.errno != os.errno.EWOULDBLOCK: + if e.errno not in retrycodes: raise if remote in rdrdy: try: try: while 1: - # Try twice, sometimes it barks the first time, - # due to ICMP Port Unreachable packets from previous writes - try: - packet = rread(remote,2000) - except socket.error: - packet = rread(remote,2000) + packet = rread(remote,2000) #rr += 1 if crypto_mode: @@ -389,7 +409,7 @@ def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr # This except handles the entire While block on PURPOSE # as an optimization (setting a try/except block is expensive) # The only operation that can raise this exception is rread - if e.errno != os.errno.EWOULDBLOCK: + if e.errno not in retrycodes: raise except Exception, e: if reconnect is not None: @@ -401,7 +421,7 @@ def tun_fwd(tun, remote, with_pi, ether_mode, cipher_key, udp, TERMINATE, stderr elif not udp: # in UDP mode, we ignore errors - packet loss man... raise - traceback.print_exc(file=sys.stderr) + #traceback.print_exc(file=sys.stderr) #print >>sys.stderr, "rr:%d\twr:%d\trt:%d\twt:%d" % (rr,wr,rt,wt) diff --git a/test/testbeds/planetlab/execute.py b/test/testbeds/planetlab/execute.py index 97683894..d21e4307 100755 --- a/test/testbeds/planetlab/execute.py +++ b/test/testbeds/planetlab/execute.py @@ -392,6 +392,10 @@ echo 'OKIDOKI' def test_tun_ping_udp(self): self._pingtest("TunInterface", "udp") + @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)") + def test_tun_ping_gre(self): + self._pingtest("TunInterface", "gre") + @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)") def test_tap_ping(self): self._pingtest("TapInterface", "tcp") @@ -400,6 +404,10 @@ echo 'OKIDOKI' def test_tap_ping_udp(self): self._pingtest("TapInterface", "udp") + @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)") + def test_tap_ping_gre(self): + self._pingtest("TapInterface", "gre") + @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)") def test_nepi_depends(self): instance = self.make_instance() diff --git a/test/testbeds/planetlab/integration_multi.py b/test/testbeds/planetlab/integration_multi.py index ff8dc7b1..2ec8df75 100755 --- a/test/testbeds/planetlab/integration_multi.py +++ b/test/testbeds/planetlab/integration_multi.py @@ -171,6 +171,11 @@ class PlanetLabMultiIntegrationTestCase(unittest.TestCase): def test_plpl_crossconnect_tcp(self): self._test_plpl_crossconnect("tcp") + @test_util.skipUnless(test_util.pl_auth() is not None, + "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)") + def test_plpl_crossconnect_gre(self): + self._test_plpl_crossconnect("gre") + @test_util.skipUnless(test_util.pl_auth() is not None, "Test requires PlanetLab authentication info (PL_USER and PL_PASS environment variables)") def test_plpl_crossconnect_udp_recover(self): -- 2.47.0