created from Alina's mercurial repo -- hg clone http://nepi.inria.fr/code/python...
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Mon, 3 Sep 2012 14:02:01 +0000 (16:02 +0200)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Mon, 3 Sep 2012 14:02:01 +0000 (16:02 +0200)
slice-context/Makefile [new file with mode: 0644]
slice-context/setup.cfg [new file with mode: 0644]
slice-context/setup.py [new file with mode: 0755]
slice-context/src/vsys.c [new file with mode: 0644]
slice-context/src/vsys.py [new file with mode: 0644]
slice-context/test/test_vsys.py [new file with mode: 0755]

diff --git a/slice-context/Makefile b/slice-context/Makefile
new file mode 100644 (file)
index 0000000..0dacbf0
--- /dev/null
@@ -0,0 +1,44 @@
+SRC = src
+TEST = test
+BUILDDIR = build
+DISTDIR = dist
+
+SUBBUILDDIR = $(shell python -c 'import distutils.util, sys; print "lib.%s-%s" % (distutils.util.get_platform(), sys.version[0:3])')
+BUILDDIR := $(BUILDDIR)/$(SUBBUILDDIR)
+
+COVERAGE = $(or $(shell which coverage), $(shell which python-coverage), \
+          coverage)
+
+all:
+       ./setup.py build
+
+install: all
+       ./setup.py install
+
+test: all
+       retval=0; \
+       for i in `find "$(TEST)" -perm -u+x -type f`; do \
+               echo $$i; \
+               PYTHONPATH="$(BUILDDIR):$$PYTHONPATH" $$i || retval=$$?; \
+               done; exit $$retval
+
+coverage: all
+       rm -f .coverage
+       for i in `find "$(TEST)" -perm -u+x -type f`; do \
+               set -e; \
+               PYTHONPATH="$(BUILDDIR):$$PYTHONPATH" $(COVERAGE) -x $$i; \
+               done
+       $(COVERAGE) -r -m `find "$(BUILDDIR)" -name \\*.py -type f`
+       rm -f .coverage
+
+clean:
+       ./setup.py clean
+       rm -f `find -name \*.pyc` .coverage
+
+distclean: clean
+       rm -rf "$(DISTDIR)"
+
+dist:
+       ./setup.py sdist
+
+.PHONY: clean distclean dist test coverage install
diff --git a/slice-context/setup.cfg b/slice-context/setup.cfg
new file mode 100644 (file)
index 0000000..e1b8322
--- /dev/null
@@ -0,0 +1,2 @@
+[clean]
+all = 1
diff --git a/slice-context/setup.py b/slice-context/setup.py
new file mode 100755 (executable)
index 0000000..87236e3
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+from distutils.core import setup, Extension
+import sys
+sys.path.append("src")
+import vsys
+
+ext = Extension('_vsys', sources = ['src/vsys.c'])
+
+setup(  name        = 'python-vsys',
+        version     = '0.1',
+        description = 'Python functions to wrap PlanetLab vsys API',
+        long_description = vsys.__doc__,
+        author      = 'Alina Quereilhac',
+        author_email = 'alina.quereilhac@inria.fr',
+        url         = 'http://nepi.inria.fr/code/python-vsys',
+        platforms   = 'Linux',
+        package_dir = {'': 'src'},
+        ext_modules = [ext],
+        py_modules  = ['vsys'])
diff --git a/slice-context/src/vsys.c b/slice-context/src/vsys.c
new file mode 100644 (file)
index 0000000..186e47f
--- /dev/null
@@ -0,0 +1,428 @@
+/* 
+ * vsys.c: CPython functions to wrap PlanetLab vsys API'
+ *   Alina Quereilhac - 02/09/2012
+ *
+ * Copyright (c) 2012 INRIA
+ *
+ */
+
+#include <Python.h>
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
+#include <linux/ioctl.h>
+#include <net/if.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#define VSYS_TUNTAP "/vsys/fd_tuntap.control"
+#define VSYS_VIFUP_IN "/vsys/vif_up.in"
+#define VSYS_VIFUP_OUT "/vsys/vif_up.out"
+#define VSYS_VIFDOWN_IN "/vsys/vif_down.in"
+#define VSYS_VIFDOWN_OUT "/vsys/vif_down.out"
+#define VSYS_VROUTE_IN "/vsys/vroute.in"
+#define VSYS_VROUTE_OUT "/vsys/vroute.out"
+
+const size_t SIZE = 4096; 
+
+int _fd_tuntap(int if_type, int no_pi, char *if_name);
+int _vif_up(const char *if_name, const char *ip, const char *prefix, int snat,
+        char *msg);
+int _vif_down(const char *if_name, char *msg);
+int _vroute (const char *action, const char *network, const char *prefix, 
+        const char *host, const char *device, char *msg);
+
+/* Python wrapper for _fd_tuntap */
+static PyObject *
+fd_tuntap(PyObject *self, PyObject *args) {
+    int if_type, fd, no_pi = 0;
+    char *if_name;
+    PyObject *retval;
+
+    if(!PyArg_ParseTuple(args, "i|i", &if_type, &no_pi))
+        return NULL;
+
+    if((if_name = malloc(SIZE)) == NULL)
+        return PyErr_SetFromErrno(PyExc_OSError);
+
+    // just in case, initialize the buffer to 0
+    memset(if_name, 0, SIZE);
+
+    Py_BEGIN_ALLOW_THREADS;
+    fd = _fd_tuntap(if_type, no_pi, if_name);
+    Py_END_ALLOW_THREADS;
+
+    retval = Py_BuildValue("is", fd, if_name);
+    free(if_name);
+    return retval;
+}
+
+/* Python wrapper for _vif_up */
+static PyObject *
+vif_up(PyObject *self, PyObject *args) {
+    int ret, snat = 0; //false
+    char *if_name, *ip, *prefix, *msg;
+    PyObject *retval;
+
+    if(!PyArg_ParseTuple(args, "sss|i", &if_name, &ip, &prefix, &snat))
+        return NULL;
+
+    if((msg = malloc(SIZE)) == NULL)
+        return PyErr_SetFromErrno(PyExc_OSError);
+
+    // just in case, initialize the buffer to 0
+    memset(msg, 0, SIZE);
+
+    Py_BEGIN_ALLOW_THREADS;
+    ret = _vif_up(if_name, ip, prefix, snat, msg);
+    Py_END_ALLOW_THREADS;
+
+    retval = Py_BuildValue("is", ret, msg);
+    free(msg);
+    return retval;
+}
+
+/* Python wrapper for _vif_down */
+static PyObject *
+vif_down(PyObject *self, PyObject *args) {
+    int ret;
+    char *if_name, *msg;
+    PyObject *retval;
+
+    if(!PyArg_ParseTuple(args, "s", &if_name))
+        return NULL;
+
+    if((msg = malloc(SIZE)) == NULL)
+        return PyErr_SetFromErrno(PyExc_OSError);
+
+    // just in case, initialize the buffer to 0
+    memset(msg, 0, SIZE);
+
+    Py_BEGIN_ALLOW_THREADS;
+    ret = _vif_down(if_name, msg);
+    Py_END_ALLOW_THREADS;
+
+    retval = Py_BuildValue("is", ret, msg);
+    free(msg);
+    return retval;
+}
+
+/* Python wrapper for _vroute */
+static PyObject *
+vroute(PyObject *self, PyObject *args) {
+    int ret;
+    char *action, *network, *prefix, *host, *device, *msg;
+    PyObject *retval;
+
+    if(!PyArg_ParseTuple(args, "sssss", &action, &network, &prefix, &host, &device))
+        return NULL;
+
+    if((msg = malloc(SIZE)) == NULL)
+        return PyErr_SetFromErrno(PyExc_OSError);
+
+    // just in case, initialize the buffer to 0
+    memset(msg, 0, SIZE);
+
+    Py_BEGIN_ALLOW_THREADS;
+    ret = _vroute(action, network, prefix, host, device, msg);
+    Py_END_ALLOW_THREADS;
+
+    retval = Py_BuildValue("is", ret, msg);
+    free(msg);
+    return retval;
+}
+
+static PyMethodDef methods[] = {
+    {"fd_tuntap", fd_tuntap, METH_VARARGS, "(fd, if_name) = fd_tuntap(if_type, no_pi = False)"},
+    {"vif_up", vif_up, METH_VARARGS, "(code, msg) = vif_up(if_name, ip, prefix, snat = False)"},
+    {"vif_down", vif_down, METH_VARARGS, " (code, msg) = vif_up(if_name)"},
+    {"vroute", vroute, METH_VARARGS, "(code, msg) = vroute(action, network, prefix, host, device)"},
+    {NULL, NULL, 0, NULL}
+};
+
+PyMODINIT_FUNC init_vsys(void) {
+    PyObject *m;
+    m = Py_InitModule("_vsys", methods);
+    if (m == NULL)
+        return;
+}
+
+/*
+ * _fifo_push(): Pushes parameters into a fifo.
+ *   
+ *   Parameters:
+ *     fifo_in:    the name of the fifo to write parameters to
+ *     fifo_out:   the name of the fifo to read error from 
+ *     input:      parameters formated as a string to push into the input fifo
+ *     msg:        buffer to return error to user
+ *
+ *  Return value:
+ *    On success, _fifo_push returns 0.
+ *    On error, a negative integer is returned.
+ *
+ */
+static int _fifo_push (const char *fifo_in, const char *fifo_out, 
+        const char *input, char *msg) {
+  FILE *in;
+  FILE *out;
+  int nbytes;
+
+  in = fopen (fifo_in, "a");
+
+  if (in == NULL){
+    snprintf (msg, SIZE, "Failed to open FIFO %s", fifo_in);
+    return  -1;
+  }
+
+  out = fopen (fifo_out, "r");
+
+  if (out == NULL){
+    snprintf (msg, SIZE, "Failed to open FIFO %s", fifo_out);
+    return  -2;
+  }
+
+  // send inputs to the fifo_in
+  fprintf (in, input);
+
+  // force flush and close the fifo_in so the program on the other side
+  // can process input.
+  fclose (in);
+
+  nbytes = fread(msg, SIZE, 1, out);
+  // the error buffer will not be empty if we read an error
+  if (strcmp(msg, "") != 0){
+      // an errror was read from the fifo_out ...
+      return -3;
+  }
+
+  fclose (out);
+
+  return 0;
+}
+
+/*
+ * _receive_fd(): Receives the file descriptor associated to the virtual
+ *  device
+ *   
+ *  Parameters:
+ *    vsys_sock:    the vsys control socket which send the fd
+ *    if_name:      the buffer where the device name assigned by PlanetLab 
+ *                      will be stored after creating the virtual interface 
+ *
+ *  Return value:
+ *    On success, _receive_fd returns the file descriptor associated to the device.
+ *    On error, a negative integer is returned.
+ *
+ */
+static int _receive_fd(int vsys_sock, char *if_name) {
+  struct msghdr msg;
+  struct iovec iov;
+  size_t ccmsg[CMSG_SPACE (sizeof(int)) / sizeof(size_t)];
+  struct cmsghdr *cmsg;
+  int rv;
+
+  // Use IOV to read interface name
+  iov.iov_base = if_name;
+  iov.iov_len = IFNAMSIZ;
+
+  msg.msg_name = 0;
+  msg.msg_namelen = 0;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+
+ // old BSD implementations should use msg_accrights instead of msg_control.
+ // the interface is different.
+  msg.msg_control = ccmsg;
+  msg.msg_controllen = sizeof(ccmsg);
+
+  while (((rv = recvmsg (vsys_sock, &msg, 0)) == -1) && errno == EINTR)
+    {}
+
+  cmsg = CMSG_FIRSTHDR (&msg);
+  if (cmsg->cmsg_type != SCM_RIGHTS){
+    snprintf (if_name, SIZE, "Got control message of unknown type");
+    return -1;
+  }
+
+  int* retfd  = (int*)CMSG_DATA (cmsg);
+  return *retfd;
+}
+
+/*
+ * _fd_tuntap(): Creates a TAP or TUN device in PlanetLab, and returns the
+ * device name and the associated file descriptor.
+ *   
+ * Parameters:
+ *   if_type:     the type of virtual device. Either IFF_TAP (0x0001) or
+ *                      IFF_TUN (0x0002)
+ *   no_pi:      set flag IFF_NO_PI
+ *   if_name:     the buffer where the device name assigned by PlanetLab 
+ *                      will be stored after creating the virtual interface.
+ *                      When an error ocurrs, if_name is used as message buffer
+ *
+ * Return value:
+ *   On success, _fd_tuntap returns the file descriptor associated to the device.
+ *   On error, a negative integer is returned.
+ *
+ */
+int _fd_tuntap(int if_type, int no_pi, char *if_name) {
+  int vsys_sock, fd;
+  struct sockaddr_un addr;
+
+  vsys_sock = socket (AF_UNIX, SOCK_STREAM, 0);
+
+  if (vsys_sock == -1){
+    snprintf (if_name, SIZE, strerror(errno));
+    return -2;
+  }
+
+  memset (&addr, 0, sizeof(struct sockaddr_un));
+
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, VSYS_TUNTAP, sizeof(addr.sun_path) - 1);
+
+  int ret = connect (vsys_sock, (struct sockaddr *) &addr, 
+    sizeof(struct sockaddr_un));
+
+  if (ret == -1){
+    snprintf (if_name, SIZE, strerror(errno));
+    return -3;
+  }
+  // send vif type (either TAP or TUN)
+  ret = send (vsys_sock, &if_type, sizeof(if_type), 0);
+
+  if (ret != sizeof(if_type)){
+    snprintf (if_name, SIZE, "Could not send parameter to vsys control socket");
+    return -4; 
+  }
+
+  // receive the file descriptor associated to the virtual interface
+  fd = _receive_fd (vsys_sock, if_name);
+
+  if (fd < 0){
+    snprintf (if_name, SIZE, "Invalid file descriptor");
+    return -5;
+  }
+/**
+*  XXX: For now unsupported functionality
+*
+  struct ifreq ifr;
+  ret = ioctl (fd, TUNGETIFF, (void *)&ifr);
+  if (ret == -1){
+    close (fd);
+    snprintf (if_name, SIZE, strerror(errno));
+    return -6;
+  }
+  
+  // flag IFF_NO_PI: don't add the extra PI header
+  if (no_pi){
+    ifr.ifr_flags |= IFF_NO_PI;
+  }
+
+  // flag IFF_ONE_QUEUE: disable normal network device queue and use only the
+  // tun/tap driver internal queue. QoS tools will not be
+  // able to control queueing for the device in this case.
+  ifr.ifr_flags |= IFF_ONE_QUEUE;
+
+  ret = ioctl (fd, TUNSETIFF, (void *)&ifr);
+  if (ret == -1){
+    close (fd);
+    snprintf (if_name, SIZE, strerror(errno));
+    return -7;
+  }
+*/
+
+  return fd;
+
+}
+
+/*
+ * _vif_up(): Configures a virtual interface with the values given by the user
+ * and sets its state UP 
+ *
+ * Parameters:
+ *   if_name:   the name of the virtual interface 
+ *   ip:        the IP address to be assigned to the interface 
+ *   prefix:    the network prefix associated to the IP address
+ *   snat:      whether to enable SNAT on the virtual interface.
+ *   msg:       buffer to return error to user
+ *
+ * Return value:
+ *   On success, return 0. 
+ *   On error, a negative integer is returned.
+ *
+ */
+int _vif_up (const char *if_name, const char *ip, const char *prefix, 
+        int snat, char *msg) {
+
+  char input[SIZE]; // big buffer
+
+  snprintf (input, SIZE, "%s\n%s\n%s\nsnat=%d\n", if_name, ip, prefix, snat);
+
+  return _fifo_push (VSYS_VIFUP_IN, VSYS_VIFUP_OUT, input, msg);
+
+}
+
+/*
+ * _vif_down(): Sets the state of a virtual interface DOWN 
+ *
+ * Parameters:
+ *   if_name:   the name of the virtual interface 
+ *   msg:       buffer to return error to user
+ *
+ * Return value:
+ *   On success, return 0. 
+ *   On error, a negative integer is returned.
+ *
+ */
+int _vif_down (const char *if_name, char *msg) {
+
+  char input[SIZE];
+
+  snprintf(input, SIZE, "%s\n", if_name);
+
+  return _fifo_push (VSYS_VIFDOWN_IN, VSYS_VIFDOWN_OUT, input, msg);
+
+}
+
+/*
+ * vroute() : Adds or removes routes on PlanetLab virtual interfaces (TAP/TUN).
+ *
+ *   Note that all networks and gateways must belong to the virtual 
+ *   network segment associated to the vsys_vnet tag for the slice.
+ *
+ *  Parameters:
+ *    action:     either 'add' or 'del'
+ *    network:    destintation network
+ *    prefix:     destination network prefix
+ *    host:       IP of gateway virtual interface
+ *    device:     name of the gateway virtual interface
+ *    msg:        buffer to return error to user
+ *
+ *  Return value:
+ *    On success, vroute returns 0.
+ *    On error, a negative integer is returned.
+ *
+ */
+int _vroute (const char *action, const char *network, const char *prefix, 
+    const char *host, const char *device, char *msg) {
+
+  char input[SIZE];
+
+  snprintf(input, SIZE, "%s %s/%s gw %s %s\n", action, network, prefix, host, device);
+
+  return _fifo_push (VSYS_VROUTE_IN, VSYS_VROUTE_OUT, input, msg);
+
+}
+
diff --git a/slice-context/src/vsys.py b/slice-context/src/vsys.py
new file mode 100644 (file)
index 0000000..aefc560
--- /dev/null
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+
+# vsys.py: Python functions to wrap PlanetLab vsys API'
+#   Alina Quereilhac - 02/09/2012
+#
+# Copyright (c) 202 INRIA
+#
+
+''' This extension provides easy to use Python functions to 
+interact with the PlanetLab vsys API.
+
+The vsys API is presented in the following publication:
+
+Vsys: A programmable sudo
+S Bhatia, G Di Stasi, T Haddow, A Bavier, S Muir, L Peterson
+In USENIX 2011
+
+'''
+
+IFF_TUN = 0x0001
+IFF_TAP = 0x0002
+
+def vif_up(if_name, ip, prefix, snat = False):
+    """ Configures a virtual interface with the values given by the user and 
+    sets its state UP 
+
+    Parameters:
+     if_name:  the name of the virtual interface 
+     ip:        the IP address to be assigned to the interface 
+     prefix:    the network prefix associated to the IP address
+     snat:      whether to enable SNAT on the virtual interface.
+
+    Return value:
+    On success, return 0. 
+    On error, a RuntimeError is raised."""
+    
+    import _vsys
+    (code, msg) = _vsys.vif_up(if_name, ip, str(prefix), snat)
+    
+    if code < 0:
+        raise RuntimeError(msg)
+
+
+def vif_down(if_name):
+    """ Sets the state of a virtual interface DOWN 
+
+    Parameters:
+     if_name:  the name of the virtual interface 
+
+    Return value:
+    On success, return 0. 
+    On error, a RuntimeError is raised."""
+    
+    import _vsys
+    (code, msg) = _vsys.vif_down(if_name)
+    
+    if code < 0:
+        raise RuntimeError(msg)
+
+def fd_tuntap(if_type, no_pi = False):
+    """Creates a TAP or TUN device in PlanetLab, and returns the device name and
+    the associated file descriptor.
+    Parameters:
+        if_type:   the type of virtual device. Either IFF_TAP (0x0001) or
+                    IFF_TUN (0x0002)
+        no_pi:     set flag IFF_NO_PI
+
+    Return value:
+    On success, fd_tuntap returns a tuple containing the file descriptor 
+    associated to the device and the device name assigned by the PlanetLab 
+    vsys script.
+    On error, a RuntimeError is raised."""
+
+    import _vsys
+    (fd, if_name) = _vsys.fd_tuntap(if_type, no_pi)
+
+    if fd < 0:
+        raise RuntimeError(if_name)
+
+    return (fd, if_name)
+
+def vroute(action, network, prefix, host, device):
+    """ Adds or removes routes on PlanetLab virtual interfaces (TAP/TUN).
+
+    Note that all networks and gateways must belong to the virtual 
+    network segment associated to the vsys_vnet tag for the slice.
+
+    Parameters:
+        action:     either 'add' or 'del'
+        network:    destintation network
+        prefix:     destination network prefix
+        host:       IP of gateway virtual interface
+        device:     name of the gateway virtual interface
+
+    Return value:
+    On success, vroute returns 0.
+    On error, a RuntimeError is raised."""
+
+    import _vsys
+    (code, msg) = _vsys.vroute(action, network, str(prefix), host, device)
+
+    if code < 0:
+        raise RuntimeError(msg)
+
+
diff --git a/slice-context/test/test_vsys.py b/slice-context/test/test_vsys.py
new file mode 100755 (executable)
index 0000000..fc68803
--- /dev/null
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2012 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 2 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, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+#
+
+import subprocess
+import unittest
+import time
+
+from vsys import fd_tuntap, vif_up, vif_down, vroute, IFF_TAP, IFF_TUN
+import _vsys
+
+class TestVsys(unittest.TestCase):
+    def setUp(self):
+        self._network = "192.168.2.0"
+        self._prefix  = 30
+        self._ip = "192.168.2.2"
+        self._remote_net = "192.168.2.4"
+
+    def _create_vif(self, if_type, no_pi = False):
+        ####### create virtual device 
+        (fd, if_name) = fd_tuntap(if_type, no_pi)
+        self.assertTrue(fd > 0)
+       
+        ###### configure virtual device
+        vif_up(if_name, self._ip, self._prefix)
+
+       # wait for prcocess to see the new configuration...
+       time.sleep(5)
+
+       ###### test ip responds to pings
+        p = subprocess.Popen(["ping", "-qc3", self._ip], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
+        out, err = p.communicate()
+        
+        self.assertFalse(err)
+
+        expected = """PING %(ip)s (%(ip)s) 56(84) bytes of data.
+
+--- %(ip)s ping statistics ---
+3 packets transmitted, 3 received, 0%% packet loss""" % {'ip': self._ip}
+        
+        self.assertTrue(out.startswith(expected), out)
+
+       ###### add route
+        vroute ("add", self._remote_net, self._prefix, self._ip, if_name)
+
+       # wait for prcocess to see the new configuration...
+       time.sleep(5)
+
+        ###### test routes
+        p = subprocess.Popen(["ip", "r"], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
+        out, err = p.communicate()
+        
+        self.assertFalse(err)
+       self.assertTrue(out.find(self._remote_net) >= 0 )
+
+       ###### del route
+        vroute ("del", self._remote_net, self._prefix, self._ip, if_name)
+
+       # wait for prcocess to see the new configuration...
+       time.sleep(5)
+
+        ##### test routes
+        p = subprocess.Popen(["ip", "r"], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
+        out, err = p.communicate()
+        
+        self.assertFalse(err)
+       self.assertTrue(out.find(self._remote_net) < 0 )
+
+        ##### delete interface
+        vif_down(if_name)
+
+       # wait for prcocess to see the new configuration...
+       time.sleep(5)
+
+       ###### test ip NOT responds to pings
+        p = subprocess.Popen(["ping", "-qc3", self._ip], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
+        out, err = p.communicate()
+        
+        self.assertFalse(err)
+
+        expected = """PING %(ip)s (%(ip)s) 56(84) bytes of data.
+
+--- %(ip)s ping statistics ---
+3 packets transmitted, 0 received, 100%% packet loss""" % {'ip': self._ip}
+        
+        self.assertTrue(out.startswith(expected), out)
+
+
+    def test_create_tun(self):
+        self._create_vif(IFF_TUN)
+
+    def test_create_tap(self):
+        self._create_vif(IFF_TAP)
+
+    def test_create_tun_no_pi(self):
+        self._create_vif(IFF_TUN, no_pi = True)
+
+    def test_create_tap_no_pi(self):
+        self._create_vif(IFF_TAP, no_pi = True)
+
+
+if __name__ == '__main__':
+    unittest.main()
+