[dpdk-dev,v1] net/tap: add tun support

Message ID 1519168364-23787-1-git-send-email-vipin.varghese@intel.com
State Superseded, archived
Headers show

Checks

Context Check Description
ci/Intel-compilation success Compilation OK
ci/checkpatch warning coding style issues

Commit Message

Vipin Varghese Feb. 20, 2018, 11:12 p.m.
The change adds TUN PMD logic to the existing TAP PMD. TUN PMD can
be initialized with 'net_tunX' where 'X' represents unique id. PMD
supports argument interface, while MAC address and remote are not
supported.

Signed-off-by: Vipin Varghese <vipin.varghese@intel.com>
---
 doc/guides/nics/tap.rst       |  15 ++++-
 drivers/net/tap/rte_eth_tap.c | 132 +++++++++++++++++++++++++++++++++---------
 2 files changed, 118 insertions(+), 29 deletions(-)

Comments

Pascal Mazon Feb. 22, 2018, 12:22 p.m. | #1
Personally, I'm mostly ok with this.
I added a couple of comments inline.

On 21/02/2018 00:12, Vipin Varghese wrote:
> The change adds TUN PMD logic to the existing TAP PMD. TUN PMD can
> be initialized with 'net_tunX' where 'X' represents unique id. PMD
> supports argument interface, while MAC address and remote are not
> supported.
>
> Signed-off-by: Vipin Varghese <vipin.varghese@intel.com>
> ---
>  doc/guides/nics/tap.rst       |  15 ++++-
>  drivers/net/tap/rte_eth_tap.c | 132 +++++++++++++++++++++++++++++++++---------
>  2 files changed, 118 insertions(+), 29 deletions(-)
>
> diff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst
> index ea61be3..fe337c3 100644
> --- a/doc/guides/nics/tap.rst
> +++ b/doc/guides/nics/tap.rst
> @@ -1,8 +1,8 @@
>  ..  SPDX-License-Identifier: BSD-3-Clause
>      Copyright(c) 2016 Intel Corporation.
>  
> -Tap Poll Mode Driver
> -====================
> +Tun|Tap Poll Mode Driver
> +========================
>  
>  The ``rte_eth_tap.c`` PMD creates a device using TAP interfaces on the
>  local host. The PMD allows for DPDK and the host to communicate using a raw
> @@ -77,6 +77,17 @@ can utilize that stack to handle the network protocols. Plus you would be able
>  to address the interface using an IP address assigned to the internal
>  interface.
>  
> +The TUN PMD allows user to create a TUN device on host. The PMD allows user
> +to transmit and recieve packets via DPDK API calls with L3 header and payload.
s/recieve/receive/
> +The devices in host can be accessed via ``ifconfig`` or ``ip`` command. TUN
> +interfaces are passed to DPDK ``rte_eal_init`` arguments as ``--vdev=net_tunX``,
> +where X stands for unique id, example::
> +
> +   --vdev=net_tun0 --vdev=net_tun1,iface=foo1, ...
> +
> +Unlike TAP PMD, TUN PMD does not support user arguments as ``MAC`` or ``remote`` user
> +options. Default interface name is ``dtunX``, where X stands for unique id.
> +
>  Flow API support
>  ----------------
>  
> diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
> index f09db0e..42c9db4 100644
> --- a/drivers/net/tap/rte_eth_tap.c
> +++ b/drivers/net/tap/rte_eth_tap.c
> @@ -42,6 +42,7 @@
>  /* Linux based path to the TUN device */
>  #define TUN_TAP_DEV_PATH        "/dev/net/tun"
>  #define DEFAULT_TAP_NAME        "dtap"
> +#define DEFAULT_TUN_NAME        "dtun"
>  
>  #define ETH_TAP_IFACE_ARG       "iface"
>  #define ETH_TAP_REMOTE_ARG      "remote"
> @@ -49,6 +50,7 @@
>  #define ETH_TAP_MAC_FIXED       "fixed"
>  
>  static struct rte_vdev_driver pmd_tap_drv;
> +static struct rte_vdev_driver pmd_tun_drv;
>  
>  static const char *valid_arguments[] = {
>  	ETH_TAP_IFACE_ARG,
> @@ -58,6 +60,10 @@
>  };
>  
>  static int tap_unit;
> +static int tun_unit;
> +
> +static int tap_type;
I'm not a huge fan of considering "tap_type = 1 means it's a TAP device".
Wouldn't it be clearer to have a tuntap_type variable and an enum
TUNTAP_TYPE with the two types;
then checking  for tuntap_type == TUN_TYPE or tuntap == TAP_TYPE?
It's not a deal breaker though, just a consideration.
> +static char tuntap_name[8];
>  
>  static volatile uint32_t tap_trigger;	/* Rx trigger */
>  
> @@ -104,24 +110,26 @@ enum ioctl_mode {
>  	 * Do not set IFF_NO_PI as packet information header will be needed
>  	 * to check if a received packet has been truncated.
>  	 */
> -	ifr.ifr_flags = IFF_TAP;
> +	ifr.ifr_flags = (tap_type) ? IFF_TAP : IFF_TUN;
>  	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", pmd->name);
>  
>  	RTE_LOG(DEBUG, PMD, "ifr_name '%s'\n", ifr.ifr_name);
>  
>  	fd = open(TUN_TAP_DEV_PATH, O_RDWR);
>  	if (fd < 0) {
> -		RTE_LOG(ERR, PMD, "Unable to create TAP interface\n");
> +		RTE_LOG(ERR, PMD, "Unable to create %s interface\n",
> +			tuntap_name);
>  		goto error;
>  	}
>  
>  #ifdef IFF_MULTI_QUEUE
>  	/* Grab the TUN features to verify we can work multi-queue */
>  	if (ioctl(fd, TUNGETFEATURES, &features) < 0) {
> -		RTE_LOG(ERR, PMD, "TAP unable to get TUN/TAP features\n");
> +		RTE_LOG(ERR, PMD, "%s unable to get TUN/TAP features\n",
> +			tuntap_name);
>  		goto error;
>  	}
> -	RTE_LOG(DEBUG, PMD, "  TAP Features %08x\n", features);
> +	RTE_LOG(DEBUG, PMD, " %s Features %08x\n", tuntap_name, features);
>  
>  	if (features & IFF_MULTI_QUEUE) {
>  		RTE_LOG(DEBUG, PMD, "  Multi-queue support for %d queues\n",
> @@ -1108,7 +1116,7 @@ enum ioctl_mode {
>  		tmp = &(*tmp)->next;
>  	}
>  
> -	RTE_LOG(DEBUG, PMD, "  RX TAP device name %s, qid %d on fd %d\n",
> +	RTE_LOG(DEBUG, PMD, "  RX TUNTAP device name %s, qid %d on fd %d\n",
>  		internals->name, rx_queue_id, internals->rxq[rx_queue_id].fd);
>  
>  	return 0;
> @@ -1163,7 +1171,7 @@ enum ioctl_mode {
>  	if (ret == -1)
>  		return -1;
>  	RTE_LOG(DEBUG, PMD,
> -		"  TX TAP device name %s, qid %d on fd %d csum %s\n",
> +		"  TX TUNTAP device name %s, qid %d on fd %d csum %s\n",
>  		internals->name, tx_queue_id, internals->txq[tx_queue_id].fd,
>  		txq->csum ? "on" : "off");
>  
> @@ -1346,17 +1354,19 @@ enum ioctl_mode {
>  	struct ifreq ifr;
>  	int i;
>  
> -	RTE_LOG(DEBUG, PMD, "  TAP device on numa %u\n", rte_socket_id());
> +	RTE_LOG(DEBUG, PMD, "  %s device on numa %u\n",
> +		tuntap_name, rte_socket_id());
>  
>  	data = rte_zmalloc_socket(tap_name, sizeof(*data), 0, numa_node);
>  	if (!data) {
> -		RTE_LOG(ERR, PMD, "TAP Failed to allocate data\n");
> +		RTE_LOG(ERR, PMD, "%s Failed to allocate data\n", tuntap_name);
>  		goto error_exit_nodev;
>  	}
>  
>  	dev = rte_eth_vdev_allocate(vdev, sizeof(*pmd));
>  	if (!dev) {
> -		RTE_LOG(ERR, PMD, "TAP Unable to allocate device struct\n");
> +		RTE_LOG(ERR, PMD, "%s Unable to allocate device struct\n",
> +			tuntap_name);
>  		goto error_exit_nodev;
>  	}
>  
> @@ -1367,8 +1377,8 @@ enum ioctl_mode {
>  	pmd->ioctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
>  	if (pmd->ioctl_sock == -1) {
>  		RTE_LOG(ERR, PMD,
> -			"TAP Unable to get a socket for management: %s\n",
> -			strerror(errno));
> +			"%s Unable to get a socket for management: %s\n",
> +			tuntap_name, strerror(errno));
>  		goto error_exit;
>  	}
>  
> @@ -1399,15 +1409,17 @@ enum ioctl_mode {
>  		pmd->txq[i].fd = -1;
>  	}
>  
> -	if (fixed_mac_type) {
> -		/* fixed mac = 00:64:74:61:70:<iface_idx> */
> -		static int iface_idx;
> -		char mac[ETHER_ADDR_LEN] = "\0dtap";
> +	if (tap_type) {
> +		if (fixed_mac_type) {
> +			/* fixed mac = 00:64:74:61:70:<iface_idx> */
> +			static int iface_idx;
> +			char mac[ETHER_ADDR_LEN] = "\0dtap";
>  
> -		mac[ETHER_ADDR_LEN - 1] = iface_idx++;
> -		rte_memcpy(&pmd->eth_addr, mac, ETHER_ADDR_LEN);
> -	} else {
> -		eth_random_addr((uint8_t *)&pmd->eth_addr);
> +			mac[ETHER_ADDR_LEN - 1] = iface_idx++;
> +			rte_memcpy(&pmd->eth_addr, mac, ETHER_ADDR_LEN);
> +		} else {
> +			eth_random_addr((uint8_t *)&pmd->eth_addr);
> +		}
>  	}
>  
>  	/* Immediately create the netdevice (this will create the 1st queue). */
> @@ -1422,11 +1434,13 @@ enum ioctl_mode {
>  	if (tap_ioctl(pmd, SIOCSIFMTU, &ifr, 1, LOCAL_AND_REMOTE) < 0)
>  		goto error_exit;
>  
> -	memset(&ifr, 0, sizeof(struct ifreq));
> -	ifr.ifr_hwaddr.sa_family = AF_LOCAL;
> -	rte_memcpy(ifr.ifr_hwaddr.sa_data, &pmd->eth_addr, ETHER_ADDR_LEN);
> -	if (tap_ioctl(pmd, SIOCSIFHWADDR, &ifr, 0, LOCAL_ONLY) < 0)
> -		goto error_exit;
> +	if (tap_type) {
> +		memset(&ifr, 0, sizeof(struct ifreq));
> +		ifr.ifr_hwaddr.sa_family = AF_LOCAL;
> +		rte_memcpy(ifr.ifr_hwaddr.sa_data, &pmd->eth_addr, ETHER_ADDR_LEN);
> +		if (tap_ioctl(pmd, SIOCSIFHWADDR, &ifr, 0, LOCAL_ONLY) < 0)
> +			goto error_exit;
> +	}
>  
>  	/*
>  	 * Set up everything related to rte_flow:
> @@ -1533,8 +1547,8 @@ enum ioctl_mode {
>  	rte_eth_dev_release_port(dev);
>  
>  error_exit_nodev:
> -	RTE_LOG(ERR, PMD, "TAP Unable to initialize %s\n",
> -		rte_vdev_device_name(vdev));
> +	RTE_LOG(ERR, PMD, "%s Unable to initialize %s\n",
> +		tuntap_name, rte_vdev_device_name(vdev));
>  
>  	rte_free(data);
>  	return -EINVAL;
> @@ -1580,6 +1594,61 @@ enum ioctl_mode {
>  	return 0;
>  }
>  
> +/* Open a TUN interface device.
> + */
> +static int
> +rte_pmd_tun_probe(struct rte_vdev_device *dev)
> +{
> +	const char *name, *params;
> +	int ret;
> +	struct rte_kvargs *kvlist = NULL;
> +	char tun_name[RTE_ETH_NAME_MAX_LEN];
> +	char remote_iface[RTE_ETH_NAME_MAX_LEN];
> +
> +	tap_type = 0;
> +	strcpy(tuntap_name, "TUN");
> +
> +	name = rte_vdev_device_name(dev);
> +	params = rte_vdev_device_args(dev);
> +
> +	memset(remote_iface, 0, RTE_ETH_NAME_MAX_LEN);
> +	snprintf(tun_name, sizeof(tun_name), "%s%d",
> +		DEFAULT_TUN_NAME, tun_unit++);
> +
> +	if (params && (params[0] != '\0')) {
> +		RTE_LOG(DEBUG, PMD, "parameters (%s)\n", params);
> +
> +		kvlist = rte_kvargs_parse(params, valid_arguments);
> +		if (kvlist) {
> +			if (rte_kvargs_count(kvlist, ETH_TAP_IFACE_ARG) == 1) {
> +				ret = rte_kvargs_process(kvlist,
> +					ETH_TAP_IFACE_ARG,
> +					&set_interface_name,
> +					tun_name);
> +
> +				if (ret == -1)
> +					goto leave;
> +			}
> +		}
> +	}
> +	pmd_link.link_speed = ETH_SPEED_NUM_10G;
> +
> +	RTE_LOG(NOTICE, PMD, "Initializing pmd_tun for %s as %s\n",
> +		name, tun_name);
> +
> +	ret = eth_dev_tap_create(dev, tun_name, remote_iface, 0);
> +
> +leave:
> +	if (ret == -1) {
> +		RTE_LOG(ERR, PMD, "Failed to create pmd for %s as %s\n",
> +			name, tun_name);
> +		tun_unit--; /* Restore the unit number */
> +	}
> +	rte_kvargs_free(kvlist);
> +
> +	return ret;
> +}
> +
>  /* Open a TAP interface device.
>   */
>  static int
> @@ -1593,6 +1662,9 @@ enum ioctl_mode {
>  	char remote_iface[RTE_ETH_NAME_MAX_LEN];
>  	int fixed_mac_type = 0;
>  
> +	tap_type = 1;
> +	strcpy(tuntap_name, "TAP");
> +
>  	name = rte_vdev_device_name(dev);
>  	params = rte_vdev_device_args(dev);
>  
> @@ -1652,7 +1724,7 @@ enum ioctl_mode {
>  	return ret;
>  }
>  
> -/* detach a TAP device.
> +/* detach a TUNTAP device.
>   */
>  static int
>  rte_pmd_tap_remove(struct rte_vdev_device *dev)
> @@ -1695,11 +1767,17 @@ enum ioctl_mode {
>  	return 0;
>  }
>  
> +static struct rte_vdev_driver pmd_tun_drv = {
> +	.probe = rte_pmd_tun_probe,
> +	.remove = rte_pmd_tap_remove,
> +};
> +
>  static struct rte_vdev_driver pmd_tap_drv = {
>  	.probe = rte_pmd_tap_probe,
>  	.remove = rte_pmd_tap_remove,
>  };
>  RTE_PMD_REGISTER_VDEV(net_tap, pmd_tap_drv);
> +RTE_PMD_REGISTER_VDEV(net_tun, pmd_tun_drv);
>  RTE_PMD_REGISTER_ALIAS(net_tap, eth_tap);
>  RTE_PMD_REGISTER_PARAM_STRING(net_tap,
>  			      ETH_TAP_IFACE_ARG "=<string> "
The rest looks fine.

Regards,
Pascal
Vipin Varghese Feb. 26, 2018, 11:01 a.m. | #2
Hi Pascal,

Thanks for the comments, Please find my updates inline

> -----Original Message-----

> From: Pascal Mazon [mailto:pascal.mazon@6wind.com]

> Sent: Thursday, February 22, 2018 12:22 PM

> To: Varghese, Vipin <vipin.varghese@intel.com>; dev@dpdk.org; Mcnamara,

> John <john.mcnamara@intel.com>; Yigit, Ferruh <ferruh.yigit@intel.com>

> Cc: Kovacevic, Marko <marko.kovacevic@intel.com>

> Subject: Re: [dpdk-dev,v1] net/tap: add tun support

> 

> Personally, I'm mostly ok with this.

> I added a couple of comments inline.

> 


<snipped>

> > +The TUN PMD allows user to create a TUN device on host. The PMD

> > +allows user to transmit and recieve packets via DPDK API calls with L3

> header and payload.

> s/recieve/receive/


Thanks, making the changes for the next version

> > +The devices in host can be accessed via ``ifconfig`` or ``ip``

> > +command. TUN interfaces are passed to DPDK ``rte_eal_init`` arguments

> > +as ``--vdev=net_tunX``, where X stands for unique id, example::


<snipped>

> >

> >  static int tap_unit;

> > +static int tun_unit;

> > +

> > +static int tap_type;

> I'm not a huge fan of considering "tap_type = 1 means it's a TAP device".

> Wouldn't it be clearer to have a tuntap_type variable and an enum

> TUNTAP_TYPE with the two types; then checking  for tuntap_type ==

> TUN_TYPE or tuntap == TAP_TYPE?

> It's not a deal breaker though, just a consideration.


Thanks, my first idea was use the same. Later argued myself in using 'tap_type' since the check for assigning MAC address goes well. Hence I  hope not making the change 'tuntap_type' is ok?

> > +static char tuntap_name[8];

> >

> >  static volatile uint32_t tap_trigger;	/* Rx trigger */

> >


<snipped> 

> > RTE_PMD_REGISTER_PARAM_STRING(net_tap,

> >  			      ETH_TAP_IFACE_ARG "=<string> "

> The rest looks fine.

> 

> Regards,

> Pascal

Patch

diff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst
index ea61be3..fe337c3 100644
--- a/doc/guides/nics/tap.rst
+++ b/doc/guides/nics/tap.rst
@@ -1,8 +1,8 @@ 
 ..  SPDX-License-Identifier: BSD-3-Clause
     Copyright(c) 2016 Intel Corporation.
 
-Tap Poll Mode Driver
-====================
+Tun|Tap Poll Mode Driver
+========================
 
 The ``rte_eth_tap.c`` PMD creates a device using TAP interfaces on the
 local host. The PMD allows for DPDK and the host to communicate using a raw
@@ -77,6 +77,17 @@  can utilize that stack to handle the network protocols. Plus you would be able
 to address the interface using an IP address assigned to the internal
 interface.
 
+The TUN PMD allows user to create a TUN device on host. The PMD allows user
+to transmit and recieve packets via DPDK API calls with L3 header and payload.
+The devices in host can be accessed via ``ifconfig`` or ``ip`` command. TUN
+interfaces are passed to DPDK ``rte_eal_init`` arguments as ``--vdev=net_tunX``,
+where X stands for unique id, example::
+
+   --vdev=net_tun0 --vdev=net_tun1,iface=foo1, ...
+
+Unlike TAP PMD, TUN PMD does not support user arguments as ``MAC`` or ``remote`` user
+options. Default interface name is ``dtunX``, where X stands for unique id.
+
 Flow API support
 ----------------
 
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index f09db0e..42c9db4 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -42,6 +42,7 @@ 
 /* Linux based path to the TUN device */
 #define TUN_TAP_DEV_PATH        "/dev/net/tun"
 #define DEFAULT_TAP_NAME        "dtap"
+#define DEFAULT_TUN_NAME        "dtun"
 
 #define ETH_TAP_IFACE_ARG       "iface"
 #define ETH_TAP_REMOTE_ARG      "remote"
@@ -49,6 +50,7 @@ 
 #define ETH_TAP_MAC_FIXED       "fixed"
 
 static struct rte_vdev_driver pmd_tap_drv;
+static struct rte_vdev_driver pmd_tun_drv;
 
 static const char *valid_arguments[] = {
 	ETH_TAP_IFACE_ARG,
@@ -58,6 +60,10 @@ 
 };
 
 static int tap_unit;
+static int tun_unit;
+
+static int tap_type;
+static char tuntap_name[8];
 
 static volatile uint32_t tap_trigger;	/* Rx trigger */
 
@@ -104,24 +110,26 @@  enum ioctl_mode {
 	 * Do not set IFF_NO_PI as packet information header will be needed
 	 * to check if a received packet has been truncated.
 	 */
-	ifr.ifr_flags = IFF_TAP;
+	ifr.ifr_flags = (tap_type) ? IFF_TAP : IFF_TUN;
 	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", pmd->name);
 
 	RTE_LOG(DEBUG, PMD, "ifr_name '%s'\n", ifr.ifr_name);
 
 	fd = open(TUN_TAP_DEV_PATH, O_RDWR);
 	if (fd < 0) {
-		RTE_LOG(ERR, PMD, "Unable to create TAP interface\n");
+		RTE_LOG(ERR, PMD, "Unable to create %s interface\n",
+			tuntap_name);
 		goto error;
 	}
 
 #ifdef IFF_MULTI_QUEUE
 	/* Grab the TUN features to verify we can work multi-queue */
 	if (ioctl(fd, TUNGETFEATURES, &features) < 0) {
-		RTE_LOG(ERR, PMD, "TAP unable to get TUN/TAP features\n");
+		RTE_LOG(ERR, PMD, "%s unable to get TUN/TAP features\n",
+			tuntap_name);
 		goto error;
 	}
-	RTE_LOG(DEBUG, PMD, "  TAP Features %08x\n", features);
+	RTE_LOG(DEBUG, PMD, " %s Features %08x\n", tuntap_name, features);
 
 	if (features & IFF_MULTI_QUEUE) {
 		RTE_LOG(DEBUG, PMD, "  Multi-queue support for %d queues\n",
@@ -1108,7 +1116,7 @@  enum ioctl_mode {
 		tmp = &(*tmp)->next;
 	}
 
-	RTE_LOG(DEBUG, PMD, "  RX TAP device name %s, qid %d on fd %d\n",
+	RTE_LOG(DEBUG, PMD, "  RX TUNTAP device name %s, qid %d on fd %d\n",
 		internals->name, rx_queue_id, internals->rxq[rx_queue_id].fd);
 
 	return 0;
@@ -1163,7 +1171,7 @@  enum ioctl_mode {
 	if (ret == -1)
 		return -1;
 	RTE_LOG(DEBUG, PMD,
-		"  TX TAP device name %s, qid %d on fd %d csum %s\n",
+		"  TX TUNTAP device name %s, qid %d on fd %d csum %s\n",
 		internals->name, tx_queue_id, internals->txq[tx_queue_id].fd,
 		txq->csum ? "on" : "off");
 
@@ -1346,17 +1354,19 @@  enum ioctl_mode {
 	struct ifreq ifr;
 	int i;
 
-	RTE_LOG(DEBUG, PMD, "  TAP device on numa %u\n", rte_socket_id());
+	RTE_LOG(DEBUG, PMD, "  %s device on numa %u\n",
+		tuntap_name, rte_socket_id());
 
 	data = rte_zmalloc_socket(tap_name, sizeof(*data), 0, numa_node);
 	if (!data) {
-		RTE_LOG(ERR, PMD, "TAP Failed to allocate data\n");
+		RTE_LOG(ERR, PMD, "%s Failed to allocate data\n", tuntap_name);
 		goto error_exit_nodev;
 	}
 
 	dev = rte_eth_vdev_allocate(vdev, sizeof(*pmd));
 	if (!dev) {
-		RTE_LOG(ERR, PMD, "TAP Unable to allocate device struct\n");
+		RTE_LOG(ERR, PMD, "%s Unable to allocate device struct\n",
+			tuntap_name);
 		goto error_exit_nodev;
 	}
 
@@ -1367,8 +1377,8 @@  enum ioctl_mode {
 	pmd->ioctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
 	if (pmd->ioctl_sock == -1) {
 		RTE_LOG(ERR, PMD,
-			"TAP Unable to get a socket for management: %s\n",
-			strerror(errno));
+			"%s Unable to get a socket for management: %s\n",
+			tuntap_name, strerror(errno));
 		goto error_exit;
 	}
 
@@ -1399,15 +1409,17 @@  enum ioctl_mode {
 		pmd->txq[i].fd = -1;
 	}
 
-	if (fixed_mac_type) {
-		/* fixed mac = 00:64:74:61:70:<iface_idx> */
-		static int iface_idx;
-		char mac[ETHER_ADDR_LEN] = "\0dtap";
+	if (tap_type) {
+		if (fixed_mac_type) {
+			/* fixed mac = 00:64:74:61:70:<iface_idx> */
+			static int iface_idx;
+			char mac[ETHER_ADDR_LEN] = "\0dtap";
 
-		mac[ETHER_ADDR_LEN - 1] = iface_idx++;
-		rte_memcpy(&pmd->eth_addr, mac, ETHER_ADDR_LEN);
-	} else {
-		eth_random_addr((uint8_t *)&pmd->eth_addr);
+			mac[ETHER_ADDR_LEN - 1] = iface_idx++;
+			rte_memcpy(&pmd->eth_addr, mac, ETHER_ADDR_LEN);
+		} else {
+			eth_random_addr((uint8_t *)&pmd->eth_addr);
+		}
 	}
 
 	/* Immediately create the netdevice (this will create the 1st queue). */
@@ -1422,11 +1434,13 @@  enum ioctl_mode {
 	if (tap_ioctl(pmd, SIOCSIFMTU, &ifr, 1, LOCAL_AND_REMOTE) < 0)
 		goto error_exit;
 
-	memset(&ifr, 0, sizeof(struct ifreq));
-	ifr.ifr_hwaddr.sa_family = AF_LOCAL;
-	rte_memcpy(ifr.ifr_hwaddr.sa_data, &pmd->eth_addr, ETHER_ADDR_LEN);
-	if (tap_ioctl(pmd, SIOCSIFHWADDR, &ifr, 0, LOCAL_ONLY) < 0)
-		goto error_exit;
+	if (tap_type) {
+		memset(&ifr, 0, sizeof(struct ifreq));
+		ifr.ifr_hwaddr.sa_family = AF_LOCAL;
+		rte_memcpy(ifr.ifr_hwaddr.sa_data, &pmd->eth_addr, ETHER_ADDR_LEN);
+		if (tap_ioctl(pmd, SIOCSIFHWADDR, &ifr, 0, LOCAL_ONLY) < 0)
+			goto error_exit;
+	}
 
 	/*
 	 * Set up everything related to rte_flow:
@@ -1533,8 +1547,8 @@  enum ioctl_mode {
 	rte_eth_dev_release_port(dev);
 
 error_exit_nodev:
-	RTE_LOG(ERR, PMD, "TAP Unable to initialize %s\n",
-		rte_vdev_device_name(vdev));
+	RTE_LOG(ERR, PMD, "%s Unable to initialize %s\n",
+		tuntap_name, rte_vdev_device_name(vdev));
 
 	rte_free(data);
 	return -EINVAL;
@@ -1580,6 +1594,61 @@  enum ioctl_mode {
 	return 0;
 }
 
+/* Open a TUN interface device.
+ */
+static int
+rte_pmd_tun_probe(struct rte_vdev_device *dev)
+{
+	const char *name, *params;
+	int ret;
+	struct rte_kvargs *kvlist = NULL;
+	char tun_name[RTE_ETH_NAME_MAX_LEN];
+	char remote_iface[RTE_ETH_NAME_MAX_LEN];
+
+	tap_type = 0;
+	strcpy(tuntap_name, "TUN");
+
+	name = rte_vdev_device_name(dev);
+	params = rte_vdev_device_args(dev);
+
+	memset(remote_iface, 0, RTE_ETH_NAME_MAX_LEN);
+	snprintf(tun_name, sizeof(tun_name), "%s%d",
+		DEFAULT_TUN_NAME, tun_unit++);
+
+	if (params && (params[0] != '\0')) {
+		RTE_LOG(DEBUG, PMD, "parameters (%s)\n", params);
+
+		kvlist = rte_kvargs_parse(params, valid_arguments);
+		if (kvlist) {
+			if (rte_kvargs_count(kvlist, ETH_TAP_IFACE_ARG) == 1) {
+				ret = rte_kvargs_process(kvlist,
+					ETH_TAP_IFACE_ARG,
+					&set_interface_name,
+					tun_name);
+
+				if (ret == -1)
+					goto leave;
+			}
+		}
+	}
+	pmd_link.link_speed = ETH_SPEED_NUM_10G;
+
+	RTE_LOG(NOTICE, PMD, "Initializing pmd_tun for %s as %s\n",
+		name, tun_name);
+
+	ret = eth_dev_tap_create(dev, tun_name, remote_iface, 0);
+
+leave:
+	if (ret == -1) {
+		RTE_LOG(ERR, PMD, "Failed to create pmd for %s as %s\n",
+			name, tun_name);
+		tun_unit--; /* Restore the unit number */
+	}
+	rte_kvargs_free(kvlist);
+
+	return ret;
+}
+
 /* Open a TAP interface device.
  */
 static int
@@ -1593,6 +1662,9 @@  enum ioctl_mode {
 	char remote_iface[RTE_ETH_NAME_MAX_LEN];
 	int fixed_mac_type = 0;
 
+	tap_type = 1;
+	strcpy(tuntap_name, "TAP");
+
 	name = rte_vdev_device_name(dev);
 	params = rte_vdev_device_args(dev);
 
@@ -1652,7 +1724,7 @@  enum ioctl_mode {
 	return ret;
 }
 
-/* detach a TAP device.
+/* detach a TUNTAP device.
  */
 static int
 rte_pmd_tap_remove(struct rte_vdev_device *dev)
@@ -1695,11 +1767,17 @@  enum ioctl_mode {
 	return 0;
 }
 
+static struct rte_vdev_driver pmd_tun_drv = {
+	.probe = rte_pmd_tun_probe,
+	.remove = rte_pmd_tap_remove,
+};
+
 static struct rte_vdev_driver pmd_tap_drv = {
 	.probe = rte_pmd_tap_probe,
 	.remove = rte_pmd_tap_remove,
 };
 RTE_PMD_REGISTER_VDEV(net_tap, pmd_tap_drv);
+RTE_PMD_REGISTER_VDEV(net_tun, pmd_tun_drv);
 RTE_PMD_REGISTER_ALIAS(net_tap, eth_tap);
 RTE_PMD_REGISTER_PARAM_STRING(net_tap,
 			      ETH_TAP_IFACE_ARG "=<string> "