All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] net/tap: remote netdevice traffic capture
@ 2017-03-03 12:27 Pascal Mazon
  2017-03-03 12:27 ` [PATCH 1/4] net/tap: add " Pascal Mazon
                   ` (5 more replies)
  0 siblings, 6 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-03 12:27 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

This patchset adds the special "remote" feature to the tap PMD, that
actually enables capturing traffic from another netdevice. This is
especially useful to get packets into the DPDK app, when the remote
netdevice has no DPDK support.

The "remote" feature requires flow API support as flow rules will be
configured on the remote netdevice for redirection, using the same
mechanism.

This series applies on top of:

  [PATCH 0/4] net/tap: support flow API

Pascal Mazon (4):
  net/tap: add remote netdevice traffic capture
  net/tap: reflect tap flags on the remote
  net/tap: use the remote MAC address if available
  net/tap: set MTU on the remote

 doc/guides/nics/tap.rst       |  17 ++
 drivers/net/tap/rte_eth_tap.c | 212 ++++++++++++++++++---
 drivers/net/tap/tap.h         |   4 +
 drivers/net/tap/tap_flow.c    | 414 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 631 insertions(+), 40 deletions(-)

-- 
2.8.0.rc0

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 1/4] net/tap: add remote netdevice traffic capture
  2017-03-03 12:27 [PATCH 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
@ 2017-03-03 12:27 ` Pascal Mazon
  2017-03-03 12:27 ` [PATCH 2/4] net/tap: reflect tap flags on the remote Pascal Mazon
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-03 12:27 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

By default, a tap netdevice is of no use when not fed by a separate
process. The ability to automatically feed it from another netdevice
allows applications to capture any kind of traffic normally destined to
the kernel stack.

This patch implements this ability through a new optional "remote"
parameter.

Packets matching filtering rules created with the flow API are matched
on the remote device and redirected to the tap PMD, where the relevant
action will be performed.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 doc/guides/nics/tap.rst       |  17 ++
 drivers/net/tap/rte_eth_tap.c |  78 +++++++-
 drivers/net/tap/tap.h         |   3 +
 drivers/net/tap/tap_flow.c    | 414 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 523 insertions(+), 13 deletions(-)

diff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst
index c4f207be3b47..a937f018f7b5 100644
--- a/doc/guides/nics/tap.rst
+++ b/doc/guides/nics/tap.rst
@@ -58,6 +58,23 @@ needed, but the interface does not enforce that speed, for example::
 
    --vdev=net_tap0,iface=foo0,speed=25000
 
+It is possible to specify a remote netdevice to capture packets from by adding
+``remote=foo1``, for example::
+
+   --vdev=net_tap,iface=tap0,remote=foo1
+
+If a ``remote`` is set, then all packets with the tap PMD's local MAC coming
+in on the remote netdevice will be redirected to the tap.
+If the tap is in promiscuous mode, then all packets will be redirected.
+In allmulti mode, all multicast packets will be redirected.
+It is possible to add explicit rte_flow rules on the tap PMD to capture specific
+traffic. For instance, in testpmd, the following rte_flow rule would capture
+packets with the given MAC address from the remote, and send it to the tap RX
+QUEUE 3::
+
+   testpmd> flow create 0 ingress pattern eth src is 02:03:04:05:06:07 / \
+            end actions queue index 3 / end
+
 After the DPDK application is started you can send and receive packets on the
 interface using the standard rx_burst/tx_burst APIs in DPDK. From the host
 point of view you can use any host tool like tcpdump, Wireshark, ping, Pktgen
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index aa605f74e375..327fefd0d3a1 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -61,6 +61,7 @@
 
 #define ETH_TAP_IFACE_ARG       "iface"
 #define ETH_TAP_SPEED_ARG       "speed"
+#define ETH_TAP_REMOTE_ARG      "remote"
 
 #ifdef IFF_MULTI_QUEUE
 #define RTE_PMD_TAP_MAX_QUEUES	16
@@ -75,6 +76,7 @@ static struct rte_vdev_driver pmd_tap_drv;
 static const char *valid_arguments[] = {
 	ETH_TAP_IFACE_ARG,
 	ETH_TAP_SPEED_ARG,
+	ETH_TAP_REMOTE_ARG,
 	NULL
 };
 
@@ -475,6 +477,8 @@ tap_promisc_enable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 1;
 	tap_link_set_flags(pmd, IFF_PROMISC, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -484,6 +488,8 @@ tap_promisc_disable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 0;
 	tap_link_set_flags(pmd, IFF_PROMISC, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(dev, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -493,6 +499,8 @@ tap_allmulti_enable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 1;
 	tap_link_set_flags(pmd, IFF_ALLMULTI, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);
 }
 
 static void
@@ -502,6 +510,8 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 0;
 	tap_link_set_flags(pmd, IFF_ALLMULTI, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(dev, TAP_REMOTE_ALLMULTI);
 }
 
 static void
@@ -617,9 +627,42 @@ tap_setup_queue(struct rte_eth_dev *dev,
 				pmd->name);
 			return fd;
 		}
+		if (pmd->remote_if_index) {
+			/*
+			 * Flush usually returns negative value because it tries
+			 * to delete every QDISC (and on a running device, one
+			 * QDISC at least is needed). Ignore negative return
+			 * value.
+			 */
+			qdisc_flush(pmd->nlsk_fd, pmd->remote_if_index);
+			if (qdisc_create_ingress(pmd->nlsk_fd,
+						 pmd->remote_if_index) < 0)
+				goto remote_fail;
+			LIST_INIT(&pmd->implicit_flows);
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_LOCAL_MAC) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCAST) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCASTV6) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_TX) < 0)
+				goto remote_fail;
+		}
 	}
 
 	return fd;
+
+remote_fail:
+	RTE_LOG(ERR, PMD,
+		"Could not set up remote flow rules for %s: remote disabled.\n",
+		pmd->name);
+	pmd->remote_if_index = 0;
+	tap_flow_implicit_flush(dev, NULL);
+	return fd;
 }
 
 static int
@@ -815,7 +858,7 @@ static const struct eth_dev_ops ops = {
 };
 
 static int
-eth_dev_tap_create(const char *name, char *tap_name)
+eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 {
 	int numa_node = rte_socket_id();
 	struct rte_eth_dev *dev = NULL;
@@ -880,6 +923,13 @@ eth_dev_tap_create(const char *name, char *tap_name)
 	 * creating/destroying flow rules.
 	 */
 	pmd->nlsk_fd = nl_init();
+	if (strlen(remote_iface)) {
+		pmd->remote_if_index = if_nametoindex(remote_iface);
+		if (!pmd->remote_if_index)
+			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
+				"remote interface will remain unconfigured\n",
+				remote_iface);
+	}
 
 	return 0;
 
@@ -920,6 +970,19 @@ set_interface_speed(const char *key __rte_unused,
 	return 0;
 }
 
+static int
+set_remote_iface(const char *key __rte_unused,
+		 const char *value,
+		 void *extra_args)
+{
+	char *name = (char *)extra_args;
+
+	if (value)
+		snprintf(name, RTE_ETH_NAME_MAX_LEN, "%s", value);
+
+	return 0;
+}
+
 /* Open a TAP interface device.
  */
 static int
@@ -929,6 +992,7 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	struct rte_kvargs *kvlist = NULL;
 	int speed;
 	char tap_name[RTE_ETH_NAME_MAX_LEN];
+	char remote_iface[RTE_ETH_NAME_MAX_LEN];
 
 	speed = ETH_SPEED_NUM_10G;
 	snprintf(tap_name, sizeof(tap_name), "%s%d",
@@ -956,6 +1020,15 @@ rte_pmd_tap_probe(const char *name, const char *params)
 				if (ret == -1)
 					goto leave;
 			}
+
+			if (rte_kvargs_count(kvlist, ETH_TAP_REMOTE_ARG) == 1) {
+				ret = rte_kvargs_process(kvlist,
+							 ETH_TAP_REMOTE_ARG,
+							 &set_remote_iface,
+							 remote_iface);
+				if (ret == -1)
+					goto leave;
+			}
 		}
 	}
 	pmd_link.link_speed = speed;
@@ -963,7 +1036,7 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	RTE_LOG(NOTICE, PMD, "Initializing pmd_tap for %s as %s\n",
 		name, tap_name);
 
-	ret = eth_dev_tap_create(name, tap_name);
+	ret = eth_dev_tap_create(name, tap_name, remote_iface);
 
 leave:
 	if (ret == -1) {
@@ -994,6 +1067,7 @@ rte_pmd_tap_remove(const char *name)
 		return 0;
 
 	tap_flow_flush(eth_dev, NULL);
+	tap_flow_implicit_flush(eth_dev, NULL);
 
 	internals = eth_dev->data->dev_private;
 	if (internals->nlsk_fd)
diff --git a/drivers/net/tap/tap.h b/drivers/net/tap/tap.h
index 96d9025412b3..a5f83d4feea3 100644
--- a/drivers/net/tap/tap.h
+++ b/drivers/net/tap/tap.h
@@ -68,7 +68,10 @@ struct pmd_internals {
 	uint16_t nb_queues; /* Number of queues supported */
 	struct ether_addr eth_addr; /* Mac address of the device port */
 	int if_index; /* IF_INDEX for the port */
+	int remote_if_index; /* remote netdevice IF_INDEX */
 	LIST_HEAD(tap_flows, rte_flow) flows; /* rte_flow rules */
+	/* implicit rte_flow rules set when a remote device is active */
+	LIST_HEAD(tap_implicit_flows, rte_flow) implicit_flows;
 	struct rx_queue rxq[RTE_PMD_TAP_MAX_QUEUES]; /* List of RX queues */
 	struct tx_queue txq[RTE_PMD_TAP_MAX_QUEUES]; /* List of TX queues */
 };
diff --git a/drivers/net/tap/tap_flow.c b/drivers/net/tap/tap_flow.c
index bf989bd5be6c..ab54dcb10f33 100644
--- a/drivers/net/tap/tap_flow.c
+++ b/drivers/net/tap/tap_flow.c
@@ -43,6 +43,7 @@
 
 struct rte_flow {
 	LIST_ENTRY(rte_flow) next; /* Pointer to the next rte_flow structure */
+	struct rte_flow *remote_flow; /* associated remote flow */
 	struct nlmsg msg;
 };
 
@@ -55,6 +56,12 @@ struct convert_data {
 	struct rte_flow *flow;
 };
 
+struct remote_rule {
+	struct rte_flow_attr attr;
+	struct rte_flow_item items[2];
+	int mirred;
+};
+
 static int tap_flow_create_eth(const struct rte_flow_item *item, void *data);
 #ifdef HAVE_TC_VLAN_ID
 static int tap_flow_create_vlan(const struct rte_flow_item *item, void *data);
@@ -218,6 +225,114 @@ static const struct tap_flow_items tap_flow_items[] = {
 	},
 };
 
+static struct remote_rule implicit_rte_flows[TAP_REMOTE_MAX_IDX] = {
+	[TAP_REMOTE_LOCAL_MAC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_LOCAL_MAC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_REDIR,
+	},
+	[TAP_REMOTE_BROADCAST] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCAST,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_BROADCASTV6] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCASTV6,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_PROMISC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_PROMISC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_ALLMULTI] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_ALLMULTI,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_TX] = {
+		.attr = {
+			.group = 0,
+			.priority = TAP_REMOTE_TX,
+			.egress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+};
+
 /**
  * Make as much checks as possible on an Ethernet item, and if a flow is
  * provided, fill it appropriately with Ethernet info.
@@ -648,6 +763,47 @@ add_action_gact(struct rte_flow *flow, int action)
 }
 
 /**
+ * Transform a MIRRED action item in the provided flow for TC.
+ *
+ * @param[in, out] flow
+ *   Flow to be filled.
+ * @param[in] ifindex
+ *   Netdevice ifindex, where to mirror/redirect packet to.
+ * @param[in] action_type
+ *   Either TCA_EGRESS_REDIR for redirection or TCA_EGRESS_MIRROR for mirroring.
+ *
+ * @return
+ *   0 if checks are alright, -1 otherwise.
+ */
+static int
+add_action_mirred(struct rte_flow *flow, uint16_t ifindex, uint16_t action_type)
+{
+	struct nlmsg *msg = &flow->msg;
+	size_t act_index = 1;
+	struct tc_mirred p = {
+		.eaction = action_type,
+		.ifindex = ifindex,
+	};
+
+	if (nlattr_nested_start(msg, TCA_FLOWER_ACT) < 0)
+		return -1;
+	if (nlattr_nested_start(msg, act_index++) < 0)
+		return -1;
+	nlattr_add(&msg->nh, TCA_ACT_KIND, sizeof("mirred"), "mirred");
+	if (nlattr_nested_start(msg, TCA_ACT_OPTIONS) < 0)
+		return -1;
+	if (action_type == TCA_EGRESS_MIRROR)
+		p.action = TC_ACT_PIPE;
+	else /* REDIRECT */
+		p.action = TC_ACT_STOLEN;
+	nlattr_add(&msg->nh, TCA_MIRRED_PARMS, sizeof(p), &p);
+	nlattr_nested_finish(msg); /* nested TCA_ACT_OPTIONS */
+	nlattr_nested_finish(msg); /* nested act_index */
+	nlattr_nested_finish(msg); /* nested TCA_FLOWER_ACT */
+	return 0;
+}
+
+/**
  * Transform a QUEUE action item in the provided flow for TC.
  *
  * @param[in, out] flow
@@ -698,6 +854,15 @@ add_action_skbedit(struct rte_flow *flow, uint16_t queue)
  *   Perform verbose error reporting if not NULL.
  * @param[in, out] flow
  *   Flow structure to update.
+ * @param[in] mirred
+ *   If set to TCA_EGRESS_REDIR, provided actions will be replaced with a
+ *   redirection to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd.
+ *   If set to TCA_EGRESS_MIRROR, provided actions will be replaced with a
+ *   mirroring to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd. Matching packets will thus be duplicated.
+ *   If set to 0, the standard behavior is to be used: set correct actions for
+ *   the TC rule, and apply it on the tap netdevice.
  *
  * @return
  *   0 on success, a negative errno value otherwise and rte_errno is set.
@@ -708,7 +873,8 @@ priv_flow_process(struct pmd_internals *pmd,
 		  const struct rte_flow_item items[],
 		  const struct rte_flow_action actions[],
 		  struct rte_flow_error *error,
-		  struct rte_flow *flow)
+		  struct rte_flow *flow,
+		  int mirred)
 {
 	const struct tap_flow_items *cur_item = tap_flow_items;
 	struct convert_data data = {
@@ -735,15 +901,21 @@ priv_flow_process(struct pmd_internals *pmd,
 		flow->msg.t.tcm_info = TC_H_MAKE(prio << 16,
 						 flow->msg.t.tcm_info);
 	}
-	if (!attr->ingress) {
-		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR,
-				   NULL, "direction should be ingress");
-		return -rte_errno;
-	}
-	/* rte_flow ingress is actually egress as seen in the kernel */
-	if (attr->ingress && flow)
-		flow->msg.t.tcm_parent = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
 	if (flow) {
+		if (mirred) {
+			/*
+			 * If attr->ingress, the rule applies on remote ingress
+			 * to match incoming packets
+			 * If attr->egress, the rule applies on tap ingress (as
+			 * seen from the kernel) to deal with packets going out
+			 * from the DPDK app.
+			 */
+			flow->msg.t.tcm_parent = TC_H_MAKE(TC_H_INGRESS, 0);
+		} else {
+			/* Standard rule on tap egress (kernel standpoint). */
+			flow->msg.t.tcm_parent =
+				TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
+		}
 		/* use flower filter type */
 		nlattr_add(&flow->msg.nh, TCA_KIND, sizeof("flower"), "flower");
 		if (nlattr_nested_start(&flow->msg, TCA_OPTIONS) < 0)
@@ -795,6 +967,22 @@ priv_flow_process(struct pmd_internals *pmd,
 			nlattr_add16(&flow->msg.nh, TCA_FLOWER_KEY_ETH_TYPE,
 				     data.eth_type);
 	}
+	if (mirred && flow) {
+		uint16_t if_index = pmd->if_index;
+
+		/*
+		 * If attr->egress && mirred, then this is a special
+		 * case where the rule must be applied on the tap, to
+		 * redirect packets coming from the DPDK App, out
+		 * through the remote netdevice.
+		 */
+		if (attr->egress)
+			if_index = pmd->remote_if_index;
+		if (add_action_mirred(flow, if_index, mirred) < 0)
+			goto exit_action_not_supported;
+		else
+			goto end;
+	}
 	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {
 		int err = 0;
 
@@ -829,6 +1017,7 @@ priv_flow_process(struct pmd_internals *pmd,
 		if (err)
 			goto exit_action_not_supported;
 	}
+end:
 	if (flow)
 		nlattr_nested_finish(&flow->msg); /* nested TCA_OPTIONS */
 	return 0;
@@ -859,7 +1048,7 @@ tap_flow_validate(struct rte_eth_dev *dev,
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
 
-	return priv_flow_process(pmd, attr, items, actions, error, NULL);
+	return priv_flow_process(pmd, attr, items, actions, error, NULL, 0);
 }
 
 /**
@@ -915,6 +1104,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		struct rte_flow_error *error)
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = NULL;
 	struct rte_flow *flow = NULL;
 	struct nlmsg *msg = NULL;
 	int err;
@@ -925,6 +1115,17 @@ tap_flow_create(struct rte_eth_dev *dev,
 				   "can't create rule, ifindex not found");
 		goto fail;
 	}
+	/*
+	 * No rules configured through standard rte_flow should be set on the
+	 * priorities used by implicit rules.
+	 */
+	if ((attr->group == MAX_GROUP) &&
+	    attr->priority > (MAX_PRIORITY - TAP_REMOTE_MAX_IDX)) {
+		rte_flow_error_set(
+			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+			NULL, "priority value too big");
+		goto fail;
+	}
 	flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
 	if (!flow) {
 		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
@@ -936,7 +1137,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
 	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
 	tap_flow_set_handle(flow);
-	if (priv_flow_process(pmd, attr, items, actions, error, flow))
+	if (priv_flow_process(pmd, attr, items, actions, error, flow, 0))
 		goto fail;
 	err = nl_send(pmd->nlsk_fd, &msg->nh);
 	if (err < 0) {
@@ -951,14 +1152,183 @@ tap_flow_create(struct rte_eth_dev *dev,
 		goto fail;
 	}
 	LIST_INSERT_HEAD(&pmd->flows, flow, next);
+	/**
+	 * If a remote device is configured, a TC rule with identical items for
+	 * matching must be set on that device, with a single action: redirect
+	 * to the local pmd->if_index.
+	 */
+	if (pmd->remote_if_index) {
+		remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+		if (!remote_flow) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"cannot allocate memory for rte_flow");
+			goto fail;
+		}
+		msg = &remote_flow->msg;
+		/* set the rule if_index for the remote netdevice */
+		tc_init_msg(
+			msg, pmd->remote_if_index, RTM_NEWTFILTER,
+			NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+		msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+		tap_flow_set_handle(remote_flow);
+		if (priv_flow_process(pmd, attr, items, NULL,
+				      error, remote_flow, TCA_EGRESS_REDIR)) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "rte flow rule validation failed");
+			goto fail;
+		}
+		err = nl_send(pmd->nlsk_fd, &msg->nh);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto fail;
+		}
+		err = nl_recv_ack(pmd->nlsk_fd);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "overlapping rules");
+			goto fail;
+		}
+		flow->remote_flow = remote_flow;
+	}
 	return flow;
 fail:
+	if (remote_flow)
+		rte_free(remote_flow);
 	if (flow)
 		rte_free(flow);
 	return NULL;
 }
 
 /**
+ * Add an implicit flow rule on the remote device to make sure traffic gets to
+ * the tap netdevice from there.
+ *
+ * @param pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to apply.
+ *
+ * @return -1 if the rule couldn't be applied, 0 otherwise.
+ */
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx)
+{
+	struct rte_flow_item *items = implicit_rte_flows[idx].items;
+	struct rte_flow_attr *attr = &implicit_rte_flows[idx].attr;
+	struct rte_flow_item_eth eth_local = { .type = 0 };
+	uint16_t if_index = pmd->remote_if_index;
+	struct rte_flow *remote_flow = NULL;
+	struct nlmsg *msg = NULL;
+	int err = 0;
+	struct rte_flow_item items_local[2] = {
+		[0] = {
+			.type = items[0].type,
+			.spec = &eth_local,
+			.mask = items[0].mask,
+		},
+		[1] = {
+			.type = items[1].type,
+		}
+	};
+
+	remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+	if (!remote_flow) {
+		RTE_LOG(ERR, PMD, "Cannot allocate memory for rte_flow");
+		goto fail;
+	}
+	msg = &remote_flow->msg;
+	if (idx == TAP_REMOTE_TX) {
+		if_index = pmd->if_index;
+	} else if (idx == TAP_REMOTE_LOCAL_MAC) {
+		/*
+		 * eth addr couldn't be set in implicit_rte_flows[] as it is not
+		 * known at compile time.
+		 */
+		memcpy(&eth_local.dst, &pmd->eth_addr, sizeof(pmd->eth_addr));
+		items = items_local;
+	}
+	tc_init_msg(msg, if_index, RTM_NEWTFILTER,
+		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+	tap_flow_set_handle(remote_flow);
+	if (priv_flow_process(pmd, attr, items, NULL, NULL,
+			      remote_flow, implicit_rte_flows[idx].mirred)) {
+		RTE_LOG(ERR, PMD, "rte flow rule validation failed\n");
+		goto fail;
+	}
+	err = nl_send(pmd->nlsk_fd, &msg->nh);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "Failure sending nl request");
+		goto fail;
+	}
+	err = nl_recv_ack(pmd->nlsk_fd);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD,
+			"Kernel refused TC filter rule creation");
+		goto fail;
+	}
+	LIST_INSERT_HEAD(&pmd->implicit_flows, remote_flow, next);
+	return 0;
+fail:
+	if (remote_flow)
+		rte_free(remote_flow);
+	return -1;
+}
+
+/**
+ * Remove specific implicit flow rule on the remote device.
+ *
+ * @param pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to remove.
+ *
+ * @return -1 if one of the implicit rules couldn't be created, 0 otherwise.
+ */
+int tap_flow_implicit_destroy(struct rte_eth_dev *dev,
+			      enum implicit_rule_index idx)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow;
+	int cur_prio = -1;
+	int idx_prio = implicit_rte_flows[idx].attr.priority + PRIORITY_OFFSET;
+
+	for (remote_flow = LIST_FIRST(&pmd->implicit_flows);
+	     remote_flow;
+	     remote_flow = LIST_NEXT(remote_flow, next)) {
+		cur_prio = (remote_flow->msg.t.tcm_info >> 16) & PRIORITY_MASK;
+		if (cur_prio != idx_prio)
+			continue;
+		return tap_flow_destroy(dev, remote_flow, NULL);
+	}
+	return 0;
+}
+
+/**
+ * Destroy all implicit flows.
+ *
+ * @see rte_flow_flush()
+ */
+int
+tap_flow_implicit_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow;
+
+	while (!LIST_EMPTY(&pmd->implicit_flows)) {
+		remote_flow = LIST_FIRST(&pmd->implicit_flows);
+		if (tap_flow_destroy(dev, remote_flow, error) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+/**
  * Destroy a flow.
  *
  * @see rte_flow_destroy()
@@ -970,6 +1340,7 @@ tap_flow_destroy(struct rte_eth_dev *dev,
 		 struct rte_flow_error *error)
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = flow->remote_flow;
 	int ret = 0;
 
 	LIST_REMOVE(flow, next);
@@ -989,7 +1360,28 @@ tap_flow_destroy(struct rte_eth_dev *dev,
 				   "couldn't receive kernel ack to our request");
 		goto end;
 	}
+	if (remote_flow) {
+		remote_flow->msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+		remote_flow->msg.nh.nlmsg_type = RTM_DELTFILTER;
+
+		ret = nl_send(pmd->nlsk_fd, &remote_flow->msg.nh);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto end;
+		}
+		ret = nl_recv_ack(pmd->nlsk_fd);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure trying to receive nl ack");
+			goto end;
+		}
+	}
 end:
+	if (remote_flow)
+		rte_free(remote_flow);
 	rte_free(flow);
 	return ret;
 }
diff --git a/drivers/net/tap/tap_flow.h b/drivers/net/tap/tap_flow.h
index a05e945df523..0134cdbaeb90 100644
--- a/drivers/net/tap/tap_flow.h
+++ b/drivers/net/tap/tap_flow.h
@@ -36,6 +36,7 @@
 
 #include <rte_flow.h>
 #include <rte_flow_driver.h>
+#include <tap.h>
 
 /**
  * In TC, priority 0 means we require the kernel to allocate one for us.
@@ -49,10 +50,33 @@
 #define GROUP_SHIFT 12
 #define MAX_GROUP GROUP_MASK
 
+/**
+ * These index are actually in reversed order: their priority is processed
+ * by subtracting their value to the lowest priority (PRIORITY_MASK).
+ * Thus the first one will have the lowest priority in the end
+ * (but biggest value).
+ */
+enum implicit_rule_index {
+	TAP_REMOTE_TX,
+	TAP_REMOTE_BROADCASTV6,
+	TAP_REMOTE_BROADCAST,
+	TAP_REMOTE_ALLMULTI,
+	TAP_REMOTE_PROMISC,
+	TAP_REMOTE_LOCAL_MAC,
+	TAP_REMOTE_MAX_IDX,
+};
+
 int tap_dev_filter_ctrl(struct rte_eth_dev *dev,
 			enum rte_filter_type filter_type,
 			enum rte_filter_op filter_op,
 			void *arg);
 int tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error);
 
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx);
+int tap_flow_implicit_destroy(struct rte_eth_dev *dev,
+			      enum implicit_rule_index idx);
+int tap_flow_implicit_flush(struct rte_eth_dev *dev,
+			    struct rte_flow_error *error);
+
 #endif /* _TAP_FLOW_H_ */
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 2/4] net/tap: reflect tap flags on the remote
  2017-03-03 12:27 [PATCH 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
  2017-03-03 12:27 ` [PATCH 1/4] net/tap: add " Pascal Mazon
@ 2017-03-03 12:27 ` Pascal Mazon
  2017-03-03 12:27 ` [PATCH 3/4] net/tap: use the remote MAC address if available Pascal Mazon
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-03 12:27 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

Synchronize PROMISC and ALLMULTI flags to the remote netdevice if
possible.

Leave the IFF_UP flag as it is, however.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 drivers/net/tap/rte_eth_tap.c | 36 +++++++++++++++++++++++-------------
 drivers/net/tap/tap.h         |  1 +
 2 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index 327fefd0d3a1..c77f206b4f60 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -249,7 +249,7 @@ pmd_tx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 }
 
 static int
-tap_link_set_flags(struct pmd_internals *pmd, short flags, int add)
+tap_netdev_set_flags(const char *iface, short flags, int add)
 {
 	struct ifreq ifr;
 	int err, s;
@@ -266,11 +266,11 @@ tap_link_set_flags(struct pmd_internals *pmd, short flags, int add)
 		return -1;
 	}
 	memset(&ifr, 0, sizeof(ifr));
-	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", pmd->name);
+	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", iface);
 	err = ioctl(s, SIOCGIFFLAGS, &ifr);
 	if (err < 0) {
 		RTE_LOG(WARNING, PMD, "Unable to get %s device flags: %s\n",
-			pmd->name, strerror(errno));
+			iface, strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -288,6 +288,7 @@ tap_link_set_flags(struct pmd_internals *pmd, short flags, int add)
 	close(s);
 
 	return 0;
+
 }
 
 static int
@@ -296,7 +297,7 @@ tap_link_set_down(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->dev_link.link_status = ETH_LINK_DOWN;
-	return tap_link_set_flags(pmd, IFF_UP | IFF_NOARP, 0);
+	return tap_netdev_set_flags(pmd->name, IFF_UP | IFF_NOARP, 0);
 }
 
 static int
@@ -305,7 +306,7 @@ tap_link_set_up(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->dev_link.link_status = ETH_LINK_UP;
-	return tap_link_set_flags(pmd, IFF_UP | IFF_NOARP, 1);
+	return tap_netdev_set_flags(pmd->name, IFF_UP | IFF_NOARP, 1);
 }
 
 static int
@@ -476,9 +477,11 @@ tap_promisc_enable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->promiscuous = 1;
-	tap_link_set_flags(pmd, IFF_PROMISC, 1);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_PROMISC, 1);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_PROMISC, 1);
+	}
 }
 
 static void
@@ -487,9 +490,11 @@ tap_promisc_disable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->promiscuous = 0;
-	tap_link_set_flags(pmd, IFF_PROMISC, 0);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_PROMISC, 0);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_destroy(dev, TAP_REMOTE_PROMISC);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_PROMISC, 0);
+	}
 }
 
 static void
@@ -498,9 +503,11 @@ tap_allmulti_enable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->all_multicast = 1;
-	tap_link_set_flags(pmd, IFF_ALLMULTI, 1);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_ALLMULTI, 1);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_ALLMULTI, 1);
+	}
 }
 
 static void
@@ -509,9 +516,11 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->all_multicast = 0;
-	tap_link_set_flags(pmd, IFF_ALLMULTI, 0);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_ALLMULTI, 0);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_destroy(dev, TAP_REMOTE_ALLMULTI);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_ALLMULTI, 0);
+	}
 }
 
 static void
@@ -924,6 +933,7 @@ eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 	 */
 	pmd->nlsk_fd = nl_init();
 	if (strlen(remote_iface)) {
+		strncpy(pmd->remote_iface, remote_iface, RTE_ETH_NAME_MAX_LEN);
 		pmd->remote_if_index = if_nametoindex(remote_iface);
 		if (!pmd->remote_if_index)
 			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
diff --git a/drivers/net/tap/tap.h b/drivers/net/tap/tap.h
index a5f83d4feea3..2afd0f9a4a58 100644
--- a/drivers/net/tap/tap.h
+++ b/drivers/net/tap/tap.h
@@ -64,6 +64,7 @@ struct tx_queue {
 
 struct pmd_internals {
 	char name[RTE_ETH_NAME_MAX_LEN]; /* Internal Tap device name */
+	char remote_iface[RTE_ETH_NAME_MAX_LEN]; /* Remote netdevice name */
 	int nlsk_fd; /* Netlink socket fd */
 	uint16_t nb_queues; /* Number of queues supported */
 	struct ether_addr eth_addr; /* Mac address of the device port */
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 3/4] net/tap: use the remote MAC address if available
  2017-03-03 12:27 [PATCH 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
  2017-03-03 12:27 ` [PATCH 1/4] net/tap: add " Pascal Mazon
  2017-03-03 12:27 ` [PATCH 2/4] net/tap: reflect tap flags on the remote Pascal Mazon
@ 2017-03-03 12:27 ` Pascal Mazon
  2017-03-03 12:27 ` [PATCH 4/4] net/tap: set MTU on the remote Pascal Mazon
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-03 12:27 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

The remote on a tap is most likely used with netdevices that are not
under DPDK control. Outgoing traffic is supposed to use the source MAC
address of the remote netdevice.

This commit synchronizes the MAC address of the local tap netdevice with
the remote one.

Of course, it is still possible to change the tap MAC address, using
standard DPDK APIs. It sets that MAC address on the tap PMD and redirect
any packets matching that destination MAC to the tap PMD. It also tries
setting the MAC address directly on the remote, if supported, through
ioctl() calls.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 drivers/net/tap/rte_eth_tap.c | 77 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 65 insertions(+), 12 deletions(-)

diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index c77f206b4f60..98b466aba223 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -523,6 +523,65 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 	}
 }
 
+static int
+tap_netdev_set_mac(const char *iface, struct ether_addr *mac_addr)
+{
+	struct ifreq ifr;
+	int err, s;
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		RTE_LOG(ERR, PMD,
+			"Unable to get a socket to get MAC: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+	err = ioctl(s, SIOCGIFHWADDR, &ifr);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "%s: couldn't get current MAC address (%s)\n",
+			iface, strerror(errno));
+		close(s);
+		return -1;
+	}
+	rte_memcpy(ifr.ifr_hwaddr.sa_data, mac_addr, ETHER_ADDR_LEN);
+	err = ioctl(s, SIOCSIFHWADDR, &ifr) == -1;
+	close(s);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "%s: couldn't set current MAC address (%s)\n",
+			iface, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+tap_netdev_get_mac(const char *iface, struct ether_addr *mac_addr)
+{
+	struct ifreq ifr;
+	int err, s;
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		RTE_LOG(ERR, PMD,
+			"Unable to get a socket to get MAC: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+	err = ioctl(s, SIOCGIFHWADDR, &ifr);
+	close(s);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "%s: couldn't get MAC address (%s)\n",
+			iface, strerror(errno));
+		return -1;
+	}
+	rte_memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+	return 0;
+}
+
 static void
 tap_mac_remove(struct rte_eth_dev *dev __rte_unused,
 	       uint32_t index __rte_unused)
@@ -539,7 +598,6 @@ tap_mac_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
 {
 	struct pmd_internals *internals = dev->data->dev_private;
 	int fd = internals->rxq[0].fd;
-	struct ifreq ifr;
 
 	if (index > RTE_PMD_TAP_MAX_MAC_ADDRS - 1) {
 		RTE_LOG(ERR, PMD,
@@ -559,19 +617,11 @@ tap_mac_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
 			dev->data->name);
 		return;
 	}
-	memset(&ifr, 0, sizeof(struct ifreq));
-	if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {
-		RTE_LOG(ERR, PMD, "%s: couldn't get current MAC address (%s)\n",
-			dev->data->name, strerror(errno));
+	if (tap_netdev_set_mac(internals->name, mac_addr) < 0)
 		return;
-	}
-	rte_memcpy(ifr.ifr_hwaddr.sa_data, mac_addr, ETHER_ADDR_LEN);
-	if (ioctl(fd, SIOCSIFHWADDR, &ifr) == -1) {
-		RTE_LOG(ERR, PMD, "%s: couldn't set current MAC address (%s)\n",
-			dev->data->name, strerror(errno));
-		return;
-	}
 	rte_memcpy(&dev->data->mac_addrs[index], mac_addr, ETHER_ADDR_LEN);
+	if (internals->remote_if_index)
+		tap_netdev_set_mac(internals->remote_iface, mac_addr);
 }
 
 static void
@@ -939,6 +989,9 @@ eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
 				"remote interface will remain unconfigured\n",
 				remote_iface);
+		else
+			/* Set the local mac address to the remote mac */
+			tap_netdev_get_mac(remote_iface, &pmd->eth_addr);
 	}
 
 	return 0;
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 4/4] net/tap: set MTU on the remote
  2017-03-03 12:27 [PATCH 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
                   ` (2 preceding siblings ...)
  2017-03-03 12:27 ` [PATCH 3/4] net/tap: use the remote MAC address if available Pascal Mazon
@ 2017-03-03 12:27 ` Pascal Mazon
  2017-03-03 15:54 ` [PATCH 0/4] net/tap: remote netdevice traffic capture Wiles, Keith
  2017-03-07 16:38 ` [PATCH v2 0/4] " Pascal Mazon
  5 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-03 12:27 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
---
 drivers/net/tap/rte_eth_tap.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index 98b466aba223..141fc7944a5f 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -823,9 +823,8 @@ tap_set_mc_addr_list(struct rte_eth_dev *dev __rte_unused,
 }
 
 static int
-tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+tap_netdev_mtu_set(const char *iface, uint16_t mtu)
 {
-	struct pmd_internals *pmd = dev->data->dev_private;
 	struct ifreq ifr;
 	int err, s;
 
@@ -833,15 +832,15 @@ tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
 	if (s < 0) {
 		RTE_LOG(ERR, PMD,
 			"Unable to get a socket for %s to set flags: %s\n",
-			pmd->name, strerror(errno));
+			iface, strerror(errno));
 		return -1;
 	}
 	memset(&ifr, 0, sizeof(ifr));
-	strncpy(ifr.ifr_name, pmd->name, IFNAMSIZ);
+	strncpy(ifr.ifr_name, iface, IFNAMSIZ);
 	err = ioctl(s, SIOCGIFMTU, &ifr);
 	if (err < 0) {
 		RTE_LOG(WARNING, PMD, "Unable to get %s device MTU: %s\n",
-			pmd->name, strerror(errno));
+			iface, strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -849,11 +848,29 @@ tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
 	err = ioctl(s, SIOCSIFMTU, &ifr);
 	if (err < 0) {
 		RTE_LOG(WARNING, PMD, "Unable to set %s mtu %d: %s\n",
-			pmd->name, mtu, strerror(errno));
+			iface, mtu, strerror(errno));
 		close(s);
 		return -1;
 	}
 	close(s);
+	return 0;
+}
+
+static int
+tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+	int err;
+
+	/* First try to set mtu on the remote */
+	if (pmd->remote_if_index) {
+		err = tap_netdev_mtu_set(pmd->remote_iface, mtu);
+		if (err < 0)
+			return err;
+	}
+	err = tap_netdev_mtu_set(pmd->name, mtu);
+	if (err < 0)
+		return err;
 	dev->data->mtu = mtu;
 	return 0;
 }
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH 0/4] net/tap: remote netdevice traffic capture
  2017-03-03 12:27 [PATCH 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
                   ` (3 preceding siblings ...)
  2017-03-03 12:27 ` [PATCH 4/4] net/tap: set MTU on the remote Pascal Mazon
@ 2017-03-03 15:54 ` Wiles, Keith
  2017-03-07 16:38 ` [PATCH v2 0/4] " Pascal Mazon
  5 siblings, 0 replies; 24+ messages in thread
From: Wiles, Keith @ 2017-03-03 15:54 UTC (permalink / raw)
  To: Pascal Mazon; +Cc: dev


> On Mar 3, 2017, at 6:27 AM, Pascal Mazon <pascal.mazon@6wind.com> wrote:
> 
> This patchset adds the special "remote" feature to the tap PMD, that
> actually enables capturing traffic from another netdevice. This is
> especially useful to get packets into the DPDK app, when the remote
> netdevice has no DPDK support.
> 
> The "remote" feature requires flow API support as flow rules will be
> configured on the remote netdevice for redirection, using the same
> mechanism.
> 
> This series applies on top of:
> 
>  [PATCH 0/4] net/tap: support flow API
> 
> Pascal Mazon (4):
>  net/tap: add remote netdevice traffic capture
>  net/tap: reflect tap flags on the remote
>  net/tap: use the remote MAC address if available
>  net/tap: set MTU on the remote
> 
> doc/guides/nics/tap.rst       |  17 ++
> drivers/net/tap/rte_eth_tap.c | 212 ++++++++++++++++++---
> drivers/net/tap/tap.h         |   4 +
> drivers/net/tap/tap_flow.c    | 414 ++++++++++++++++++++++++++++++++++++++++--
> drivers/net/tap/tap_flow.h    |  24 +++
> 5 files changed, 631 insertions(+), 40 deletions(-)

Ah here is the tap.rst change, thanks.

> 
> -- 
> 2.8.0.rc0
> 

Regards,
Keith

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v2 0/4] remote netdevice traffic capture
  2017-03-03 12:27 [PATCH 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
                   ` (4 preceding siblings ...)
  2017-03-03 15:54 ` [PATCH 0/4] net/tap: remote netdevice traffic capture Wiles, Keith
@ 2017-03-07 16:38 ` Pascal Mazon
  2017-03-07 16:38   ` [PATCH v2 1/4] net/tap: add " Pascal Mazon
                     ` (4 more replies)
  5 siblings, 5 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-07 16:38 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

This patchset adds the special "remote" feature to the tap PMD, that
actually enables capturing traffic from another netdevice. This is
especially useful to get packets into the DPDK app, when the remote
netdevice has no DPDK support.

The "remote" feature requires flow API support as flow rules will be
configured on the remote netdevice for redirection, using the same
mechanism.

This series applies on top of:

  [PATCH 0/4] net/tap: support flow API

v2 changes:
  - rebase on top of updated "net/tap: support flow API"
  - fix implicit flow flush when closing the netdevices

Pascal Mazon (4):
  net/tap: add remote netdevice traffic capture
  net/tap: reflect tap flags on the remote
  net/tap: use the remote MAC address if available
  net/tap: set MTU on the remote

 doc/guides/nics/tap.rst       |  17 ++
 drivers/net/tap/rte_eth_tap.c | 213 ++++++++++++++++++---
 drivers/net/tap/tap.h         |   4 +
 drivers/net/tap/tap_flow.c    | 418 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 635 insertions(+), 41 deletions(-)

-- 
2.8.0.rc0

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v2 1/4] net/tap: add remote netdevice traffic capture
  2017-03-07 16:38 ` [PATCH v2 0/4] " Pascal Mazon
@ 2017-03-07 16:38   ` Pascal Mazon
  2017-03-07 16:38   ` [PATCH v2 2/4] net/tap: reflect tap flags on the remote Pascal Mazon
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-07 16:38 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

By default, a tap netdevice is of no use when not fed by a separate
process. The ability to automatically feed it from another netdevice
allows applications to capture any kind of traffic normally destined to
the kernel stack.

This patch implements this ability through a new optional "remote"
parameter.

Packets matching filtering rules created with the flow API are matched
on the remote device and redirected to the tap PMD, where the relevant
action will be performed.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 doc/guides/nics/tap.rst       |  17 ++
 drivers/net/tap/rte_eth_tap.c |  79 +++++++-
 drivers/net/tap/tap.h         |   2 +
 drivers/net/tap/tap_flow.c    | 418 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 526 insertions(+), 14 deletions(-)

diff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst
index cdb528b5eae4..676a569b00ca 100644
--- a/doc/guides/nics/tap.rst
+++ b/doc/guides/nics/tap.rst
@@ -58,6 +58,23 @@ needed, but the interface does not enforce that speed, for example::
 
    --vdev=net_tap0,iface=foo0,speed=25000
 
+It is possible to specify a remote netdevice to capture packets from by adding
+``remote=foo1``, for example::
+
+   --vdev=net_tap,iface=tap0,remote=foo1
+
+If a ``remote`` is set, then all packets with the tap PMD's local MAC coming
+in on the remote netdevice will be redirected to the tap.
+If the tap is in promiscuous mode, then all packets will be redirected.
+In allmulti mode, all multicast packets will be redirected.
+It is possible to add explicit rte_flow rules on the tap PMD to capture specific
+traffic. For instance, in testpmd, the following rte_flow rule would capture
+packets with the given MAC address from the remote, and send it to the tap RX
+QUEUE 3::
+
+   testpmd> flow create 0 ingress pattern eth src is 02:03:04:05:06:07 / \
+            end actions queue index 3 / end
+
 After the DPDK application is started you can send and receive packets on the
 interface using the standard rx_burst/tx_burst APIs in DPDK. From the host
 point of view you can use any host tool like tcpdump, Wireshark, ping, Pktgen
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index 5727f6228b17..b29cfbfb41f3 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -63,6 +63,7 @@
 
 #define ETH_TAP_IFACE_ARG       "iface"
 #define ETH_TAP_SPEED_ARG       "speed"
+#define ETH_TAP_REMOTE_ARG      "remote"
 
 #ifdef IFF_MULTI_QUEUE
 #define RTE_PMD_TAP_MAX_QUEUES	16
@@ -77,6 +78,7 @@ static struct rte_vdev_driver pmd_tap_drv;
 static const char *valid_arguments[] = {
 	ETH_TAP_IFACE_ARG,
 	ETH_TAP_SPEED_ARG,
+	ETH_TAP_REMOTE_ARG,
 	NULL
 };
 
@@ -435,6 +437,7 @@ tap_dev_close(struct rte_eth_dev *dev __rte_unused)
 	struct pmd_internals *internals = dev->data->dev_private;
 
 	tap_link_set_down(dev);
+	tap_flow_implicit_flush(dev, NULL);
 
 	for (i = 0; i < internals->nb_queues; i++) {
 		if (internals->rxq[i].fd != -1)
@@ -480,6 +483,8 @@ tap_promisc_enable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 1;
 	tap_link_set_flags(pmd, IFF_PROMISC, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -489,6 +494,8 @@ tap_promisc_disable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 0;
 	tap_link_set_flags(pmd, IFF_PROMISC, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(dev, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -498,6 +505,8 @@ tap_allmulti_enable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 1;
 	tap_link_set_flags(pmd, IFF_ALLMULTI, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);
 }
 
 static void
@@ -507,6 +516,8 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 0;
 	tap_link_set_flags(pmd, IFF_ALLMULTI, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(dev, TAP_REMOTE_ALLMULTI);
 }
 
 static void
@@ -632,9 +643,42 @@ tap_setup_queue(struct rte_eth_dev *dev,
 				pmd->name);
 			return fd;
 		}
+		if (pmd->remote_if_index) {
+			/*
+			 * Flush usually returns negative value because it tries
+			 * to delete every QDISC (and on a running device, one
+			 * QDISC at least is needed). Ignore negative return
+			 * value.
+			 */
+			qdisc_flush(pmd->nlsk_fd, pmd->remote_if_index);
+			if (qdisc_create_ingress(pmd->nlsk_fd,
+						 pmd->remote_if_index) < 0)
+				goto remote_fail;
+			LIST_INIT(&pmd->implicit_flows);
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_LOCAL_MAC) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCAST) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCASTV6) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_TX) < 0)
+				goto remote_fail;
+		}
 	}
 
 	return fd;
+
+remote_fail:
+	RTE_LOG(ERR, PMD,
+		"Could not set up remote flow rules for %s: remote disabled.\n",
+		pmd->name);
+	pmd->remote_if_index = 0;
+	tap_flow_implicit_flush(dev, NULL);
+	return fd;
 }
 
 static int
@@ -848,7 +892,7 @@ tap_kernel_support(struct pmd_internals *pmd)
 }
 
 static int
-eth_dev_tap_create(const char *name, char *tap_name)
+eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 {
 	int numa_node = rte_socket_id();
 	struct rte_eth_dev *dev = NULL;
@@ -917,6 +961,13 @@ eth_dev_tap_create(const char *name, char *tap_name)
 	 * creating/destroying flow rules.
 	 */
 	pmd->nlsk_fd = nl_init();
+	if (strlen(remote_iface)) {
+		pmd->remote_if_index = if_nametoindex(remote_iface);
+		if (!pmd->remote_if_index)
+			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
+				"remote interface will remain unconfigured\n",
+				remote_iface);
+	}
 
 	return 0;
 
@@ -957,6 +1008,19 @@ set_interface_speed(const char *key __rte_unused,
 	return 0;
 }
 
+static int
+set_remote_iface(const char *key __rte_unused,
+		 const char *value,
+		 void *extra_args)
+{
+	char *name = (char *)extra_args;
+
+	if (value)
+		snprintf(name, RTE_ETH_NAME_MAX_LEN, "%s", value);
+
+	return 0;
+}
+
 /* Open a TAP interface device.
  */
 static int
@@ -966,6 +1030,7 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	struct rte_kvargs *kvlist = NULL;
 	int speed;
 	char tap_name[RTE_ETH_NAME_MAX_LEN];
+	char remote_iface[RTE_ETH_NAME_MAX_LEN];
 
 	speed = ETH_SPEED_NUM_10G;
 	snprintf(tap_name, sizeof(tap_name), "%s%d",
@@ -993,6 +1058,15 @@ rte_pmd_tap_probe(const char *name, const char *params)
 				if (ret == -1)
 					goto leave;
 			}
+
+			if (rte_kvargs_count(kvlist, ETH_TAP_REMOTE_ARG) == 1) {
+				ret = rte_kvargs_process(kvlist,
+							 ETH_TAP_REMOTE_ARG,
+							 &set_remote_iface,
+							 remote_iface);
+				if (ret == -1)
+					goto leave;
+			}
 		}
 	}
 	pmd_link.link_speed = speed;
@@ -1000,7 +1074,7 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	RTE_LOG(NOTICE, PMD, "Initializing pmd_tap for %s as %s\n",
 		name, tap_name);
 
-	ret = eth_dev_tap_create(name, tap_name);
+	ret = eth_dev_tap_create(name, tap_name, remote_iface);
 
 leave:
 	if (ret == -1) {
@@ -1031,6 +1105,7 @@ rte_pmd_tap_remove(const char *name)
 		return 0;
 
 	tap_flow_flush(eth_dev, NULL);
+	tap_flow_implicit_flush(eth_dev, NULL);
 
 	internals = eth_dev->data->dev_private;
 	if (internals->flower_support && internals->nlsk_fd)
diff --git a/drivers/net/tap/tap.h b/drivers/net/tap/tap.h
index 2c8cc7d5b485..4c4de939f1cc 100644
--- a/drivers/net/tap/tap.h
+++ b/drivers/net/tap/tap.h
@@ -71,6 +71,8 @@ struct pmd_internals {
 	int flower_support;               /* 1 if kernel supports, else 0 */
 	int flower_vlan_support;          /* 1 if kernel supports, else 0 */
 	LIST_HEAD(tap_flows, rte_flow) flows;        /* rte_flow rules */
+	/* implicit rte_flow rules set when a remote device is active */
+	LIST_HEAD(tap_implicit_flows, rte_flow) implicit_flows;
 	struct rx_queue rxq[RTE_PMD_TAP_MAX_QUEUES]; /* List of RX queues */
 	struct tx_queue txq[RTE_PMD_TAP_MAX_QUEUES]; /* List of TX queues */
 };
diff --git a/drivers/net/tap/tap_flow.c b/drivers/net/tap/tap_flow.c
index 3fb28b1db917..25260570ee50 100644
--- a/drivers/net/tap/tap_flow.c
+++ b/drivers/net/tap/tap_flow.c
@@ -82,6 +82,7 @@ enum {
 
 struct rte_flow {
 	LIST_ENTRY(rte_flow) next; /* Pointer to the next rte_flow structure */
+	struct rte_flow *remote_flow; /* associated remote flow */
 	struct nlmsg msg;
 };
 
@@ -92,6 +93,12 @@ struct convert_data {
 	struct rte_flow *flow;
 };
 
+struct remote_rule {
+	struct rte_flow_attr attr;
+	struct rte_flow_item items[2];
+	int mirred;
+};
+
 static int tap_flow_create_eth(const struct rte_flow_item *item, void *data);
 static int tap_flow_create_vlan(const struct rte_flow_item *item, void *data);
 static int tap_flow_create_ipv4(const struct rte_flow_item *item, void *data);
@@ -249,6 +256,114 @@ static const struct tap_flow_items tap_flow_items[] = {
 	},
 };
 
+static struct remote_rule implicit_rte_flows[TAP_REMOTE_MAX_IDX] = {
+	[TAP_REMOTE_LOCAL_MAC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_LOCAL_MAC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_REDIR,
+	},
+	[TAP_REMOTE_BROADCAST] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCAST,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_BROADCASTV6] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCASTV6,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_PROMISC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_PROMISC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_ALLMULTI] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_ALLMULTI,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_TX] = {
+		.attr = {
+			.group = 0,
+			.priority = TAP_REMOTE_TX,
+			.egress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+};
+
 /**
  * Make as much checks as possible on an Ethernet item, and if a flow is
  * provided, fill it appropriately with Ethernet info.
@@ -673,6 +788,47 @@ add_action_gact(struct rte_flow *flow, int action)
 }
 
 /**
+ * Transform a MIRRED action item in the provided flow for TC.
+ *
+ * @param[in, out] flow
+ *   Flow to be filled.
+ * @param[in] ifindex
+ *   Netdevice ifindex, where to mirror/redirect packet to.
+ * @param[in] action_type
+ *   Either TCA_EGRESS_REDIR for redirection or TCA_EGRESS_MIRROR for mirroring.
+ *
+ * @return
+ *   0 if checks are alright, -1 otherwise.
+ */
+static int
+add_action_mirred(struct rte_flow *flow, uint16_t ifindex, uint16_t action_type)
+{
+	struct nlmsg *msg = &flow->msg;
+	size_t act_index = 1;
+	struct tc_mirred p = {
+		.eaction = action_type,
+		.ifindex = ifindex,
+	};
+
+	if (nlattr_nested_start(msg, TCA_FLOWER_ACT) < 0)
+		return -1;
+	if (nlattr_nested_start(msg, act_index++) < 0)
+		return -1;
+	nlattr_add(&msg->nh, TCA_ACT_KIND, sizeof("mirred"), "mirred");
+	if (nlattr_nested_start(msg, TCA_ACT_OPTIONS) < 0)
+		return -1;
+	if (action_type == TCA_EGRESS_MIRROR)
+		p.action = TC_ACT_PIPE;
+	else /* REDIRECT */
+		p.action = TC_ACT_STOLEN;
+	nlattr_add(&msg->nh, TCA_MIRRED_PARMS, sizeof(p), &p);
+	nlattr_nested_finish(msg); /* nested TCA_ACT_OPTIONS */
+	nlattr_nested_finish(msg); /* nested act_index */
+	nlattr_nested_finish(msg); /* nested TCA_FLOWER_ACT */
+	return 0;
+}
+
+/**
  * Transform a QUEUE action item in the provided flow for TC.
  *
  * @param[in, out] flow
@@ -723,6 +879,15 @@ add_action_skbedit(struct rte_flow *flow, uint16_t queue)
  *   Perform verbose error reporting if not NULL.
  * @param[in, out] flow
  *   Flow structure to update.
+ * @param[in] mirred
+ *   If set to TCA_EGRESS_REDIR, provided actions will be replaced with a
+ *   redirection to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd.
+ *   If set to TCA_EGRESS_MIRROR, provided actions will be replaced with a
+ *   mirroring to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd. Matching packets will thus be duplicated.
+ *   If set to 0, the standard behavior is to be used: set correct actions for
+ *   the TC rule, and apply it on the tap netdevice.
  *
  * @return
  *   0 on success, a negative errno value otherwise and rte_errno is set.
@@ -733,7 +898,8 @@ priv_flow_process(struct pmd_internals *pmd,
 		  const struct rte_flow_item items[],
 		  const struct rte_flow_action actions[],
 		  struct rte_flow_error *error,
-		  struct rte_flow *flow)
+		  struct rte_flow *flow,
+		  int mirred)
 {
 	const struct tap_flow_items *cur_item = tap_flow_items;
 	struct convert_data data = {
@@ -760,15 +926,21 @@ priv_flow_process(struct pmd_internals *pmd,
 		flow->msg.t.tcm_info = TC_H_MAKE(prio << 16,
 						 flow->msg.t.tcm_info);
 	}
-	if (!attr->ingress) {
-		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR,
-				   NULL, "direction should be ingress");
-		return -rte_errno;
-	}
-	/* rte_flow ingress is actually egress as seen in the kernel */
-	if (attr->ingress && flow)
-		flow->msg.t.tcm_parent = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
 	if (flow) {
+		if (mirred) {
+			/*
+			 * If attr->ingress, the rule applies on remote ingress
+			 * to match incoming packets
+			 * If attr->egress, the rule applies on tap ingress (as
+			 * seen from the kernel) to deal with packets going out
+			 * from the DPDK app.
+			 */
+			flow->msg.t.tcm_parent = TC_H_MAKE(TC_H_INGRESS, 0);
+		} else {
+			/* Standard rule on tap egress (kernel standpoint). */
+			flow->msg.t.tcm_parent =
+				TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
+		}
 		/* use flower filter type */
 		nlattr_add(&flow->msg.nh, TCA_KIND, sizeof("flower"), "flower");
 		if (nlattr_nested_start(&flow->msg, TCA_OPTIONS) < 0)
@@ -821,6 +993,22 @@ priv_flow_process(struct pmd_internals *pmd,
 				     data.eth_type);
 		}
 	}
+	if (mirred && flow) {
+		uint16_t if_index = pmd->if_index;
+
+		/*
+		 * If attr->egress && mirred, then this is a special
+		 * case where the rule must be applied on the tap, to
+		 * redirect packets coming from the DPDK App, out
+		 * through the remote netdevice.
+		 */
+		if (attr->egress)
+			if_index = pmd->remote_if_index;
+		if (add_action_mirred(flow, if_index, mirred) < 0)
+			goto exit_action_not_supported;
+		else
+			goto end;
+	}
 	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {
 		int err = 0;
 
@@ -855,6 +1043,7 @@ priv_flow_process(struct pmd_internals *pmd,
 		if (err)
 			goto exit_action_not_supported;
 	}
+end:
 	if (flow)
 		nlattr_nested_finish(&flow->msg); /* nested TCA_OPTIONS */
 	return 0;
@@ -885,7 +1074,7 @@ tap_flow_validate(struct rte_eth_dev *dev,
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
 
-	return priv_flow_process(pmd, attr, items, actions, error, NULL);
+	return priv_flow_process(pmd, attr, items, actions, error, NULL, 0);
 }
 
 /**
@@ -941,6 +1130,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		struct rte_flow_error *error)
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = NULL;
 	struct rte_flow *flow = NULL;
 	struct nlmsg *msg = NULL;
 	int err;
@@ -951,6 +1141,17 @@ tap_flow_create(struct rte_eth_dev *dev,
 				   "can't create rule, ifindex not found");
 		goto fail;
 	}
+	/*
+	 * No rules configured through standard rte_flow should be set on the
+	 * priorities used by implicit rules.
+	 */
+	if ((attr->group == MAX_GROUP) &&
+	    attr->priority > (MAX_PRIORITY - TAP_REMOTE_MAX_IDX)) {
+		rte_flow_error_set(
+			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+			NULL, "priority value too big");
+		goto fail;
+	}
 	flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
 	if (!flow) {
 		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
@@ -962,7 +1163,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
 	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
 	tap_flow_set_handle(flow);
-	if (priv_flow_process(pmd, attr, items, actions, error, flow))
+	if (priv_flow_process(pmd, attr, items, actions, error, flow, 0))
 		goto fail;
 	err = nl_send(pmd->nlsk_fd, &msg->nh);
 	if (err < 0) {
@@ -977,14 +1178,183 @@ tap_flow_create(struct rte_eth_dev *dev,
 		goto fail;
 	}
 	LIST_INSERT_HEAD(&pmd->flows, flow, next);
+	/**
+	 * If a remote device is configured, a TC rule with identical items for
+	 * matching must be set on that device, with a single action: redirect
+	 * to the local pmd->if_index.
+	 */
+	if (pmd->remote_if_index) {
+		remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+		if (!remote_flow) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"cannot allocate memory for rte_flow");
+			goto fail;
+		}
+		msg = &remote_flow->msg;
+		/* set the rule if_index for the remote netdevice */
+		tc_init_msg(
+			msg, pmd->remote_if_index, RTM_NEWTFILTER,
+			NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+		msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+		tap_flow_set_handle(remote_flow);
+		if (priv_flow_process(pmd, attr, items, NULL,
+				      error, remote_flow, TCA_EGRESS_REDIR)) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "rte flow rule validation failed");
+			goto fail;
+		}
+		err = nl_send(pmd->nlsk_fd, &msg->nh);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto fail;
+		}
+		err = nl_recv_ack(pmd->nlsk_fd);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "overlapping rules");
+			goto fail;
+		}
+		flow->remote_flow = remote_flow;
+	}
 	return flow;
 fail:
+	if (remote_flow)
+		rte_free(remote_flow);
 	if (flow)
 		rte_free(flow);
 	return NULL;
 }
 
 /**
+ * Add an implicit flow rule on the remote device to make sure traffic gets to
+ * the tap netdevice from there.
+ *
+ * @param pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to apply.
+ *
+ * @return -1 if the rule couldn't be applied, 0 otherwise.
+ */
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx)
+{
+	struct rte_flow_item *items = implicit_rte_flows[idx].items;
+	struct rte_flow_attr *attr = &implicit_rte_flows[idx].attr;
+	struct rte_flow_item_eth eth_local = { .type = 0 };
+	uint16_t if_index = pmd->remote_if_index;
+	struct rte_flow *remote_flow = NULL;
+	struct nlmsg *msg = NULL;
+	int err = 0;
+	struct rte_flow_item items_local[2] = {
+		[0] = {
+			.type = items[0].type,
+			.spec = &eth_local,
+			.mask = items[0].mask,
+		},
+		[1] = {
+			.type = items[1].type,
+		}
+	};
+
+	remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+	if (!remote_flow) {
+		RTE_LOG(ERR, PMD, "Cannot allocate memory for rte_flow");
+		goto fail;
+	}
+	msg = &remote_flow->msg;
+	if (idx == TAP_REMOTE_TX) {
+		if_index = pmd->if_index;
+	} else if (idx == TAP_REMOTE_LOCAL_MAC) {
+		/*
+		 * eth addr couldn't be set in implicit_rte_flows[] as it is not
+		 * known at compile time.
+		 */
+		memcpy(&eth_local.dst, &pmd->eth_addr, sizeof(pmd->eth_addr));
+		items = items_local;
+	}
+	tc_init_msg(msg, if_index, RTM_NEWTFILTER,
+		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+	tap_flow_set_handle(remote_flow);
+	if (priv_flow_process(pmd, attr, items, NULL, NULL,
+			      remote_flow, implicit_rte_flows[idx].mirred)) {
+		RTE_LOG(ERR, PMD, "rte flow rule validation failed\n");
+		goto fail;
+	}
+	err = nl_send(pmd->nlsk_fd, &msg->nh);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "Failure sending nl request");
+		goto fail;
+	}
+	err = nl_recv_ack(pmd->nlsk_fd);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD,
+			"Kernel refused TC filter rule creation");
+		goto fail;
+	}
+	LIST_INSERT_HEAD(&pmd->implicit_flows, remote_flow, next);
+	return 0;
+fail:
+	if (remote_flow)
+		rte_free(remote_flow);
+	return -1;
+}
+
+/**
+ * Remove specific implicit flow rule on the remote device.
+ *
+ * @param pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to remove.
+ *
+ * @return -1 if one of the implicit rules couldn't be created, 0 otherwise.
+ */
+int tap_flow_implicit_destroy(struct rte_eth_dev *dev,
+			      enum implicit_rule_index idx)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow;
+	int cur_prio = -1;
+	int idx_prio = implicit_rte_flows[idx].attr.priority + PRIORITY_OFFSET;
+
+	for (remote_flow = LIST_FIRST(&pmd->implicit_flows);
+	     remote_flow;
+	     remote_flow = LIST_NEXT(remote_flow, next)) {
+		cur_prio = (remote_flow->msg.t.tcm_info >> 16) & PRIORITY_MASK;
+		if (cur_prio != idx_prio)
+			continue;
+		return tap_flow_destroy(dev, remote_flow, NULL);
+	}
+	return 0;
+}
+
+/**
+ * Destroy all implicit flows.
+ *
+ * @see rte_flow_flush()
+ */
+int
+tap_flow_implicit_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow;
+
+	while (!LIST_EMPTY(&pmd->implicit_flows)) {
+		remote_flow = LIST_FIRST(&pmd->implicit_flows);
+		if (tap_flow_destroy(dev, remote_flow, error) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+/**
  * Destroy a flow.
  *
  * @see rte_flow_destroy()
@@ -996,6 +1366,7 @@ tap_flow_destroy(struct rte_eth_dev *dev,
 		 struct rte_flow_error *error)
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = flow->remote_flow;
 	int ret = 0;
 
 	LIST_REMOVE(flow, next);
@@ -1009,11 +1380,34 @@ tap_flow_destroy(struct rte_eth_dev *dev,
 		goto end;
 	}
 	ret = nl_recv_ack(pmd->nlsk_fd);
-	if (ret < 0)
+	if (ret < 0) {
 		rte_flow_error_set(
 			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
 			"couldn't receive kernel ack to our request");
+		goto end;
+	}
+	if (remote_flow) {
+		remote_flow->msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+		remote_flow->msg.nh.nlmsg_type = RTM_DELTFILTER;
+
+		ret = nl_send(pmd->nlsk_fd, &remote_flow->msg.nh);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto end;
+		}
+		ret = nl_recv_ack(pmd->nlsk_fd);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure trying to receive nl ack");
+			goto end;
+		}
+	}
 end:
+	if (remote_flow)
+		rte_free(remote_flow);
 	rte_free(flow);
 	return ret;
 }
diff --git a/drivers/net/tap/tap_flow.h b/drivers/net/tap/tap_flow.h
index a05e945df523..0134cdbaeb90 100644
--- a/drivers/net/tap/tap_flow.h
+++ b/drivers/net/tap/tap_flow.h
@@ -36,6 +36,7 @@
 
 #include <rte_flow.h>
 #include <rte_flow_driver.h>
+#include <tap.h>
 
 /**
  * In TC, priority 0 means we require the kernel to allocate one for us.
@@ -49,10 +50,33 @@
 #define GROUP_SHIFT 12
 #define MAX_GROUP GROUP_MASK
 
+/**
+ * These index are actually in reversed order: their priority is processed
+ * by subtracting their value to the lowest priority (PRIORITY_MASK).
+ * Thus the first one will have the lowest priority in the end
+ * (but biggest value).
+ */
+enum implicit_rule_index {
+	TAP_REMOTE_TX,
+	TAP_REMOTE_BROADCASTV6,
+	TAP_REMOTE_BROADCAST,
+	TAP_REMOTE_ALLMULTI,
+	TAP_REMOTE_PROMISC,
+	TAP_REMOTE_LOCAL_MAC,
+	TAP_REMOTE_MAX_IDX,
+};
+
 int tap_dev_filter_ctrl(struct rte_eth_dev *dev,
 			enum rte_filter_type filter_type,
 			enum rte_filter_op filter_op,
 			void *arg);
 int tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error);
 
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx);
+int tap_flow_implicit_destroy(struct rte_eth_dev *dev,
+			      enum implicit_rule_index idx);
+int tap_flow_implicit_flush(struct rte_eth_dev *dev,
+			    struct rte_flow_error *error);
+
 #endif /* _TAP_FLOW_H_ */
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 2/4] net/tap: reflect tap flags on the remote
  2017-03-07 16:38 ` [PATCH v2 0/4] " Pascal Mazon
  2017-03-07 16:38   ` [PATCH v2 1/4] net/tap: add " Pascal Mazon
@ 2017-03-07 16:38   ` Pascal Mazon
  2017-03-07 16:38   ` [PATCH v2 3/4] net/tap: use the remote MAC address if available Pascal Mazon
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-07 16:38 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

Synchronize PROMISC and ALLMULTI flags to the remote netdevice if
possible.

Leave the IFF_UP flag as it is, however.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 drivers/net/tap/rte_eth_tap.c | 36 +++++++++++++++++++++++-------------
 drivers/net/tap/tap.h         |  2 ++
 2 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index b29cfbfb41f3..ded8bb0f18d9 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -254,7 +254,7 @@ pmd_tx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 }
 
 static int
-tap_link_set_flags(struct pmd_internals *pmd, short flags, int add)
+tap_netdev_set_flags(const char *iface, short flags, int add)
 {
 	struct ifreq ifr;
 	int err, s;
@@ -271,11 +271,11 @@ tap_link_set_flags(struct pmd_internals *pmd, short flags, int add)
 		return -1;
 	}
 	memset(&ifr, 0, sizeof(ifr));
-	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", pmd->name);
+	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", iface);
 	err = ioctl(s, SIOCGIFFLAGS, &ifr);
 	if (err < 0) {
 		RTE_LOG(WARNING, PMD, "Unable to get %s device flags: %s\n",
-			pmd->name, strerror(errno));
+			iface, strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -293,6 +293,7 @@ tap_link_set_flags(struct pmd_internals *pmd, short flags, int add)
 	close(s);
 
 	return 0;
+
 }
 
 static int
@@ -301,7 +302,7 @@ tap_link_set_down(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->dev_link.link_status = ETH_LINK_DOWN;
-	return tap_link_set_flags(pmd, IFF_UP | IFF_NOARP, 0);
+	return tap_netdev_set_flags(pmd->name, IFF_UP | IFF_NOARP, 0);
 }
 
 static int
@@ -310,7 +311,7 @@ tap_link_set_up(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->dev_link.link_status = ETH_LINK_UP;
-	return tap_link_set_flags(pmd, IFF_UP | IFF_NOARP, 1);
+	return tap_netdev_set_flags(pmd->name, IFF_UP | IFF_NOARP, 1);
 }
 
 static int
@@ -482,9 +483,11 @@ tap_promisc_enable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->promiscuous = 1;
-	tap_link_set_flags(pmd, IFF_PROMISC, 1);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_PROMISC, 1);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_PROMISC, 1);
+	}
 }
 
 static void
@@ -493,9 +496,11 @@ tap_promisc_disable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->promiscuous = 0;
-	tap_link_set_flags(pmd, IFF_PROMISC, 0);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_PROMISC, 0);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_destroy(dev, TAP_REMOTE_PROMISC);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_PROMISC, 0);
+	}
 }
 
 static void
@@ -504,9 +509,11 @@ tap_allmulti_enable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->all_multicast = 1;
-	tap_link_set_flags(pmd, IFF_ALLMULTI, 1);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_ALLMULTI, 1);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_ALLMULTI, 1);
+	}
 }
 
 static void
@@ -515,9 +522,11 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->all_multicast = 0;
-	tap_link_set_flags(pmd, IFF_ALLMULTI, 0);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_ALLMULTI, 0);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_destroy(dev, TAP_REMOTE_ALLMULTI);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_ALLMULTI, 0);
+	}
 }
 
 static void
@@ -962,6 +971,7 @@ eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 	 */
 	pmd->nlsk_fd = nl_init();
 	if (strlen(remote_iface)) {
+		strncpy(pmd->remote_iface, remote_iface, RTE_ETH_NAME_MAX_LEN);
 		pmd->remote_if_index = if_nametoindex(remote_iface);
 		if (!pmd->remote_if_index)
 			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
diff --git a/drivers/net/tap/tap.h b/drivers/net/tap/tap.h
index 4c4de939f1cc..9811d0b0f085 100644
--- a/drivers/net/tap/tap.h
+++ b/drivers/net/tap/tap.h
@@ -63,9 +63,11 @@ struct tx_queue {
 };
 
 struct pmd_internals {
+	char remote_iface[RTE_ETH_NAME_MAX_LEN]; /* Remote netdevice name */
 	char name[RTE_ETH_NAME_MAX_LEN];  /* Internal Tap device name */
 	uint16_t nb_queues;               /* Number of queues supported */
 	struct ether_addr eth_addr;       /* Mac address of the device port */
+	int remote_if_index;              /* remote netdevice IF_INDEX */
 	int if_index;                     /* IF_INDEX for the port */
 	int nlsk_fd;                      /* Netlink socket fd */
 	int flower_support;               /* 1 if kernel supports, else 0 */
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 3/4] net/tap: use the remote MAC address if available
  2017-03-07 16:38 ` [PATCH v2 0/4] " Pascal Mazon
  2017-03-07 16:38   ` [PATCH v2 1/4] net/tap: add " Pascal Mazon
  2017-03-07 16:38   ` [PATCH v2 2/4] net/tap: reflect tap flags on the remote Pascal Mazon
@ 2017-03-07 16:38   ` Pascal Mazon
  2017-03-07 16:38   ` [PATCH v2 4/4] net/tap: set MTU on the remote Pascal Mazon
  2017-03-08 10:06   ` [PATCH v3 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
  4 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-07 16:38 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

The remote on a tap is most likely used with netdevices that are not
under DPDK control. Outgoing traffic is supposed to use the source MAC
address of the remote netdevice.

This commit synchronizes the MAC address of the local tap netdevice with
the remote one.

Of course, it is still possible to change the tap MAC address, using
standard DPDK APIs. It sets that MAC address on the tap PMD and redirect
any packets matching that destination MAC to the tap PMD. It also tries
setting the MAC address directly on the remote, if supported, through
ioctl() calls.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 drivers/net/tap/rte_eth_tap.c | 77 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 65 insertions(+), 12 deletions(-)

diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index ded8bb0f18d9..ed71cddfe0b3 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -529,6 +529,65 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 	}
 }
 
+static int
+tap_netdev_set_mac(const char *iface, struct ether_addr *mac_addr)
+{
+	struct ifreq ifr;
+	int err, s;
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		RTE_LOG(ERR, PMD,
+			"Unable to get a socket to get MAC: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+	err = ioctl(s, SIOCGIFHWADDR, &ifr);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "%s: couldn't get current MAC address (%s)\n",
+			iface, strerror(errno));
+		close(s);
+		return -1;
+	}
+	rte_memcpy(ifr.ifr_hwaddr.sa_data, mac_addr, ETHER_ADDR_LEN);
+	err = ioctl(s, SIOCSIFHWADDR, &ifr) == -1;
+	close(s);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "%s: couldn't set current MAC address (%s)\n",
+			iface, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+tap_netdev_get_mac(const char *iface, struct ether_addr *mac_addr)
+{
+	struct ifreq ifr;
+	int err, s;
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		RTE_LOG(ERR, PMD,
+			"Unable to get a socket to get MAC: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+	err = ioctl(s, SIOCGIFHWADDR, &ifr);
+	close(s);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "%s: couldn't get MAC address (%s)\n",
+			iface, strerror(errno));
+		return -1;
+	}
+	rte_memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+	return 0;
+}
+
 static void
 tap_mac_remove(struct rte_eth_dev *dev __rte_unused,
 	       uint32_t index __rte_unused)
@@ -545,7 +604,6 @@ tap_mac_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
 {
 	struct pmd_internals *internals = dev->data->dev_private;
 	int fd = internals->rxq[0].fd;
-	struct ifreq ifr;
 
 	if (index > RTE_PMD_TAP_MAX_MAC_ADDRS - 1) {
 		RTE_LOG(ERR, PMD,
@@ -565,19 +623,11 @@ tap_mac_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
 			dev->data->name);
 		return;
 	}
-	memset(&ifr, 0, sizeof(struct ifreq));
-	if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {
-		RTE_LOG(ERR, PMD, "%s: couldn't get current MAC address (%s)\n",
-			dev->data->name, strerror(errno));
+	if (tap_netdev_set_mac(internals->name, mac_addr) < 0)
 		return;
-	}
-	rte_memcpy(ifr.ifr_hwaddr.sa_data, mac_addr, ETHER_ADDR_LEN);
-	if (ioctl(fd, SIOCSIFHWADDR, &ifr) == -1) {
-		RTE_LOG(ERR, PMD, "%s: couldn't set current MAC address (%s)\n",
-			dev->data->name, strerror(errno));
-		return;
-	}
 	rte_memcpy(&dev->data->mac_addrs[index], mac_addr, ETHER_ADDR_LEN);
+	if (internals->remote_if_index)
+		tap_netdev_set_mac(internals->remote_iface, mac_addr);
 }
 
 static void
@@ -977,6 +1027,9 @@ eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
 				"remote interface will remain unconfigured\n",
 				remote_iface);
+		else
+			/* Set the local mac address to the remote mac */
+			tap_netdev_get_mac(remote_iface, &pmd->eth_addr);
 	}
 
 	return 0;
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 4/4] net/tap: set MTU on the remote
  2017-03-07 16:38 ` [PATCH v2 0/4] " Pascal Mazon
                     ` (2 preceding siblings ...)
  2017-03-07 16:38   ` [PATCH v2 3/4] net/tap: use the remote MAC address if available Pascal Mazon
@ 2017-03-07 16:38   ` Pascal Mazon
  2017-03-08 10:06   ` [PATCH v3 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
  4 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-07 16:38 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
---
 drivers/net/tap/rte_eth_tap.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index ed71cddfe0b3..34e56813cedb 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -839,9 +839,8 @@ tap_set_mc_addr_list(struct rte_eth_dev *dev __rte_unused,
 }
 
 static int
-tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+tap_netdev_mtu_set(const char *iface, uint16_t mtu)
 {
-	struct pmd_internals *pmd = dev->data->dev_private;
 	struct ifreq ifr;
 	int err, s;
 
@@ -849,15 +848,15 @@ tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
 	if (s < 0) {
 		RTE_LOG(ERR, PMD,
 			"Unable to get a socket for %s to set flags: %s\n",
-			pmd->name, strerror(errno));
+			iface, strerror(errno));
 		return -1;
 	}
 	memset(&ifr, 0, sizeof(ifr));
-	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", pmd->name);
+	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", iface);
 	err = ioctl(s, SIOCGIFMTU, &ifr);
 	if (err < 0) {
 		RTE_LOG(WARNING, PMD, "Unable to get %s device MTU: %s\n",
-			pmd->name, strerror(errno));
+			iface, strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -865,11 +864,29 @@ tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
 	err = ioctl(s, SIOCSIFMTU, &ifr);
 	if (err < 0) {
 		RTE_LOG(WARNING, PMD, "Unable to set %s mtu %d: %s\n",
-			pmd->name, mtu, strerror(errno));
+			iface, mtu, strerror(errno));
 		close(s);
 		return -1;
 	}
 	close(s);
+	return 0;
+}
+
+static int
+tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+	int err;
+
+	/* First try to set mtu on the remote */
+	if (pmd->remote_if_index) {
+		err = tap_netdev_mtu_set(pmd->remote_iface, mtu);
+		if (err < 0)
+			return err;
+	}
+	err = tap_netdev_mtu_set(pmd->name, mtu);
+	if (err < 0)
+		return err;
 	dev->data->mtu = mtu;
 	return 0;
 }
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 0/4] net/tap: remote netdevice traffic capture
  2017-03-07 16:38 ` [PATCH v2 0/4] " Pascal Mazon
                     ` (3 preceding siblings ...)
  2017-03-07 16:38   ` [PATCH v2 4/4] net/tap: set MTU on the remote Pascal Mazon
@ 2017-03-08 10:06   ` Pascal Mazon
  2017-03-08 10:06     ` [PATCH v3 1/4] net/tap: add " Pascal Mazon
                       ` (5 more replies)
  4 siblings, 6 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-08 10:06 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

This patchset adds the special "remote" feature to the tap PMD, that
actually enables capturing traffic from another netdevice. This is
especially useful to get packets into the DPDK app, when the remote
netdevice has no DPDK support.

The "remote" feature requires flow API support as flow rules will be
configured on the remote netdevice for redirection, using the same
mechanism.

This series applies on top of:

  [PATCH 0/4] net/tap: support flow API

v2 changes:
  - rebase on top of updated "net/tap: support flow API"
  - fix implicit flow flush when closing the netdevices

v3 changes:
  - memset(0) for remote_iface in rte_pmd_tap_probe()
  - use snprintf instead of strncpy to correctly handle terminating \0

Pascal Mazon (4):
  net/tap: add remote netdevice traffic capture
  net/tap: reflect tap flags on the remote
  net/tap: use the remote MAC address if available
  net/tap: set MTU on the remote

 doc/guides/nics/tap.rst       |  17 ++
 drivers/net/tap/rte_eth_tap.c | 215 +++++++++++++++++++---
 drivers/net/tap/tap.h         |   4 +
 drivers/net/tap/tap_flow.c    | 418 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 637 insertions(+), 41 deletions(-)

-- 
2.8.0.rc0

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 1/4] net/tap: add remote netdevice traffic capture
  2017-03-08 10:06   ` [PATCH v3 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
@ 2017-03-08 10:06     ` Pascal Mazon
  2017-03-08 10:06     ` [PATCH v3 2/4] net/tap: reflect tap flags on the remote Pascal Mazon
                       ` (4 subsequent siblings)
  5 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-08 10:06 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

By default, a tap netdevice is of no use when not fed by a separate
process. The ability to automatically feed it from another netdevice
allows applications to capture any kind of traffic normally destined to
the kernel stack.

This patch implements this ability through a new optional "remote"
parameter.

Packets matching filtering rules created with the flow API are matched
on the remote device and redirected to the tap PMD, where the relevant
action will be performed.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 doc/guides/nics/tap.rst       |  17 ++
 drivers/net/tap/rte_eth_tap.c |  80 +++++++-
 drivers/net/tap/tap.h         |   2 +
 drivers/net/tap/tap_flow.c    | 418 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 527 insertions(+), 14 deletions(-)

diff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst
index cdb528b5eae4..676a569b00ca 100644
--- a/doc/guides/nics/tap.rst
+++ b/doc/guides/nics/tap.rst
@@ -58,6 +58,23 @@ needed, but the interface does not enforce that speed, for example::
 
    --vdev=net_tap0,iface=foo0,speed=25000
 
+It is possible to specify a remote netdevice to capture packets from by adding
+``remote=foo1``, for example::
+
+   --vdev=net_tap,iface=tap0,remote=foo1
+
+If a ``remote`` is set, then all packets with the tap PMD's local MAC coming
+in on the remote netdevice will be redirected to the tap.
+If the tap is in promiscuous mode, then all packets will be redirected.
+In allmulti mode, all multicast packets will be redirected.
+It is possible to add explicit rte_flow rules on the tap PMD to capture specific
+traffic. For instance, in testpmd, the following rte_flow rule would capture
+packets with the given MAC address from the remote, and send it to the tap RX
+QUEUE 3::
+
+   testpmd> flow create 0 ingress pattern eth src is 02:03:04:05:06:07 / \
+            end actions queue index 3 / end
+
 After the DPDK application is started you can send and receive packets on the
 interface using the standard rx_burst/tx_burst APIs in DPDK. From the host
 point of view you can use any host tool like tcpdump, Wireshark, ping, Pktgen
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index 5727f6228b17..f3d9d8fe96aa 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -63,6 +63,7 @@
 
 #define ETH_TAP_IFACE_ARG       "iface"
 #define ETH_TAP_SPEED_ARG       "speed"
+#define ETH_TAP_REMOTE_ARG      "remote"
 
 #ifdef IFF_MULTI_QUEUE
 #define RTE_PMD_TAP_MAX_QUEUES	16
@@ -77,6 +78,7 @@ static struct rte_vdev_driver pmd_tap_drv;
 static const char *valid_arguments[] = {
 	ETH_TAP_IFACE_ARG,
 	ETH_TAP_SPEED_ARG,
+	ETH_TAP_REMOTE_ARG,
 	NULL
 };
 
@@ -435,6 +437,7 @@ tap_dev_close(struct rte_eth_dev *dev __rte_unused)
 	struct pmd_internals *internals = dev->data->dev_private;
 
 	tap_link_set_down(dev);
+	tap_flow_implicit_flush(dev, NULL);
 
 	for (i = 0; i < internals->nb_queues; i++) {
 		if (internals->rxq[i].fd != -1)
@@ -480,6 +483,8 @@ tap_promisc_enable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 1;
 	tap_link_set_flags(pmd, IFF_PROMISC, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -489,6 +494,8 @@ tap_promisc_disable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 0;
 	tap_link_set_flags(pmd, IFF_PROMISC, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(dev, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -498,6 +505,8 @@ tap_allmulti_enable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 1;
 	tap_link_set_flags(pmd, IFF_ALLMULTI, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);
 }
 
 static void
@@ -507,6 +516,8 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 0;
 	tap_link_set_flags(pmd, IFF_ALLMULTI, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(dev, TAP_REMOTE_ALLMULTI);
 }
 
 static void
@@ -632,9 +643,42 @@ tap_setup_queue(struct rte_eth_dev *dev,
 				pmd->name);
 			return fd;
 		}
+		if (pmd->remote_if_index) {
+			/*
+			 * Flush usually returns negative value because it tries
+			 * to delete every QDISC (and on a running device, one
+			 * QDISC at least is needed). Ignore negative return
+			 * value.
+			 */
+			qdisc_flush(pmd->nlsk_fd, pmd->remote_if_index);
+			if (qdisc_create_ingress(pmd->nlsk_fd,
+						 pmd->remote_if_index) < 0)
+				goto remote_fail;
+			LIST_INIT(&pmd->implicit_flows);
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_LOCAL_MAC) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCAST) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCASTV6) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_TX) < 0)
+				goto remote_fail;
+		}
 	}
 
 	return fd;
+
+remote_fail:
+	RTE_LOG(ERR, PMD,
+		"Could not set up remote flow rules for %s: remote disabled.\n",
+		pmd->name);
+	pmd->remote_if_index = 0;
+	tap_flow_implicit_flush(dev, NULL);
+	return fd;
 }
 
 static int
@@ -848,7 +892,7 @@ tap_kernel_support(struct pmd_internals *pmd)
 }
 
 static int
-eth_dev_tap_create(const char *name, char *tap_name)
+eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 {
 	int numa_node = rte_socket_id();
 	struct rte_eth_dev *dev = NULL;
@@ -917,6 +961,13 @@ eth_dev_tap_create(const char *name, char *tap_name)
 	 * creating/destroying flow rules.
 	 */
 	pmd->nlsk_fd = nl_init();
+	if (strlen(remote_iface)) {
+		pmd->remote_if_index = if_nametoindex(remote_iface);
+		if (!pmd->remote_if_index)
+			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
+				"remote interface will remain unconfigured\n",
+				remote_iface);
+	}
 
 	return 0;
 
@@ -957,6 +1008,19 @@ set_interface_speed(const char *key __rte_unused,
 	return 0;
 }
 
+static int
+set_remote_iface(const char *key __rte_unused,
+		 const char *value,
+		 void *extra_args)
+{
+	char *name = (char *)extra_args;
+
+	if (value)
+		snprintf(name, RTE_ETH_NAME_MAX_LEN, "%s", value);
+
+	return 0;
+}
+
 /* Open a TAP interface device.
  */
 static int
@@ -966,10 +1030,12 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	struct rte_kvargs *kvlist = NULL;
 	int speed;
 	char tap_name[RTE_ETH_NAME_MAX_LEN];
+	char remote_iface[RTE_ETH_NAME_MAX_LEN];
 
 	speed = ETH_SPEED_NUM_10G;
 	snprintf(tap_name, sizeof(tap_name), "%s%d",
 		 DEFAULT_TAP_NAME, tap_unit++);
+	memset(remote_iface, 0, RTE_ETH_NAME_MAX_LEN);
 
 	if (params && (params[0] != '\0')) {
 		RTE_LOG(DEBUG, PMD, "paramaters (%s)\n", params);
@@ -993,6 +1059,15 @@ rte_pmd_tap_probe(const char *name, const char *params)
 				if (ret == -1)
 					goto leave;
 			}
+
+			if (rte_kvargs_count(kvlist, ETH_TAP_REMOTE_ARG) == 1) {
+				ret = rte_kvargs_process(kvlist,
+							 ETH_TAP_REMOTE_ARG,
+							 &set_remote_iface,
+							 remote_iface);
+				if (ret == -1)
+					goto leave;
+			}
 		}
 	}
 	pmd_link.link_speed = speed;
@@ -1000,7 +1075,7 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	RTE_LOG(NOTICE, PMD, "Initializing pmd_tap for %s as %s\n",
 		name, tap_name);
 
-	ret = eth_dev_tap_create(name, tap_name);
+	ret = eth_dev_tap_create(name, tap_name, remote_iface);
 
 leave:
 	if (ret == -1) {
@@ -1031,6 +1106,7 @@ rte_pmd_tap_remove(const char *name)
 		return 0;
 
 	tap_flow_flush(eth_dev, NULL);
+	tap_flow_implicit_flush(eth_dev, NULL);
 
 	internals = eth_dev->data->dev_private;
 	if (internals->flower_support && internals->nlsk_fd)
diff --git a/drivers/net/tap/tap.h b/drivers/net/tap/tap.h
index 2c8cc7d5b485..4c4de939f1cc 100644
--- a/drivers/net/tap/tap.h
+++ b/drivers/net/tap/tap.h
@@ -71,6 +71,8 @@ struct pmd_internals {
 	int flower_support;               /* 1 if kernel supports, else 0 */
 	int flower_vlan_support;          /* 1 if kernel supports, else 0 */
 	LIST_HEAD(tap_flows, rte_flow) flows;        /* rte_flow rules */
+	/* implicit rte_flow rules set when a remote device is active */
+	LIST_HEAD(tap_implicit_flows, rte_flow) implicit_flows;
 	struct rx_queue rxq[RTE_PMD_TAP_MAX_QUEUES]; /* List of RX queues */
 	struct tx_queue txq[RTE_PMD_TAP_MAX_QUEUES]; /* List of TX queues */
 };
diff --git a/drivers/net/tap/tap_flow.c b/drivers/net/tap/tap_flow.c
index 3fb28b1db917..25260570ee50 100644
--- a/drivers/net/tap/tap_flow.c
+++ b/drivers/net/tap/tap_flow.c
@@ -82,6 +82,7 @@ enum {
 
 struct rte_flow {
 	LIST_ENTRY(rte_flow) next; /* Pointer to the next rte_flow structure */
+	struct rte_flow *remote_flow; /* associated remote flow */
 	struct nlmsg msg;
 };
 
@@ -92,6 +93,12 @@ struct convert_data {
 	struct rte_flow *flow;
 };
 
+struct remote_rule {
+	struct rte_flow_attr attr;
+	struct rte_flow_item items[2];
+	int mirred;
+};
+
 static int tap_flow_create_eth(const struct rte_flow_item *item, void *data);
 static int tap_flow_create_vlan(const struct rte_flow_item *item, void *data);
 static int tap_flow_create_ipv4(const struct rte_flow_item *item, void *data);
@@ -249,6 +256,114 @@ static const struct tap_flow_items tap_flow_items[] = {
 	},
 };
 
+static struct remote_rule implicit_rte_flows[TAP_REMOTE_MAX_IDX] = {
+	[TAP_REMOTE_LOCAL_MAC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_LOCAL_MAC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_REDIR,
+	},
+	[TAP_REMOTE_BROADCAST] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCAST,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_BROADCASTV6] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCASTV6,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_PROMISC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_PROMISC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_ALLMULTI] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_ALLMULTI,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_TX] = {
+		.attr = {
+			.group = 0,
+			.priority = TAP_REMOTE_TX,
+			.egress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+};
+
 /**
  * Make as much checks as possible on an Ethernet item, and if a flow is
  * provided, fill it appropriately with Ethernet info.
@@ -673,6 +788,47 @@ add_action_gact(struct rte_flow *flow, int action)
 }
 
 /**
+ * Transform a MIRRED action item in the provided flow for TC.
+ *
+ * @param[in, out] flow
+ *   Flow to be filled.
+ * @param[in] ifindex
+ *   Netdevice ifindex, where to mirror/redirect packet to.
+ * @param[in] action_type
+ *   Either TCA_EGRESS_REDIR for redirection or TCA_EGRESS_MIRROR for mirroring.
+ *
+ * @return
+ *   0 if checks are alright, -1 otherwise.
+ */
+static int
+add_action_mirred(struct rte_flow *flow, uint16_t ifindex, uint16_t action_type)
+{
+	struct nlmsg *msg = &flow->msg;
+	size_t act_index = 1;
+	struct tc_mirred p = {
+		.eaction = action_type,
+		.ifindex = ifindex,
+	};
+
+	if (nlattr_nested_start(msg, TCA_FLOWER_ACT) < 0)
+		return -1;
+	if (nlattr_nested_start(msg, act_index++) < 0)
+		return -1;
+	nlattr_add(&msg->nh, TCA_ACT_KIND, sizeof("mirred"), "mirred");
+	if (nlattr_nested_start(msg, TCA_ACT_OPTIONS) < 0)
+		return -1;
+	if (action_type == TCA_EGRESS_MIRROR)
+		p.action = TC_ACT_PIPE;
+	else /* REDIRECT */
+		p.action = TC_ACT_STOLEN;
+	nlattr_add(&msg->nh, TCA_MIRRED_PARMS, sizeof(p), &p);
+	nlattr_nested_finish(msg); /* nested TCA_ACT_OPTIONS */
+	nlattr_nested_finish(msg); /* nested act_index */
+	nlattr_nested_finish(msg); /* nested TCA_FLOWER_ACT */
+	return 0;
+}
+
+/**
  * Transform a QUEUE action item in the provided flow for TC.
  *
  * @param[in, out] flow
@@ -723,6 +879,15 @@ add_action_skbedit(struct rte_flow *flow, uint16_t queue)
  *   Perform verbose error reporting if not NULL.
  * @param[in, out] flow
  *   Flow structure to update.
+ * @param[in] mirred
+ *   If set to TCA_EGRESS_REDIR, provided actions will be replaced with a
+ *   redirection to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd.
+ *   If set to TCA_EGRESS_MIRROR, provided actions will be replaced with a
+ *   mirroring to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd. Matching packets will thus be duplicated.
+ *   If set to 0, the standard behavior is to be used: set correct actions for
+ *   the TC rule, and apply it on the tap netdevice.
  *
  * @return
  *   0 on success, a negative errno value otherwise and rte_errno is set.
@@ -733,7 +898,8 @@ priv_flow_process(struct pmd_internals *pmd,
 		  const struct rte_flow_item items[],
 		  const struct rte_flow_action actions[],
 		  struct rte_flow_error *error,
-		  struct rte_flow *flow)
+		  struct rte_flow *flow,
+		  int mirred)
 {
 	const struct tap_flow_items *cur_item = tap_flow_items;
 	struct convert_data data = {
@@ -760,15 +926,21 @@ priv_flow_process(struct pmd_internals *pmd,
 		flow->msg.t.tcm_info = TC_H_MAKE(prio << 16,
 						 flow->msg.t.tcm_info);
 	}
-	if (!attr->ingress) {
-		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR,
-				   NULL, "direction should be ingress");
-		return -rte_errno;
-	}
-	/* rte_flow ingress is actually egress as seen in the kernel */
-	if (attr->ingress && flow)
-		flow->msg.t.tcm_parent = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
 	if (flow) {
+		if (mirred) {
+			/*
+			 * If attr->ingress, the rule applies on remote ingress
+			 * to match incoming packets
+			 * If attr->egress, the rule applies on tap ingress (as
+			 * seen from the kernel) to deal with packets going out
+			 * from the DPDK app.
+			 */
+			flow->msg.t.tcm_parent = TC_H_MAKE(TC_H_INGRESS, 0);
+		} else {
+			/* Standard rule on tap egress (kernel standpoint). */
+			flow->msg.t.tcm_parent =
+				TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
+		}
 		/* use flower filter type */
 		nlattr_add(&flow->msg.nh, TCA_KIND, sizeof("flower"), "flower");
 		if (nlattr_nested_start(&flow->msg, TCA_OPTIONS) < 0)
@@ -821,6 +993,22 @@ priv_flow_process(struct pmd_internals *pmd,
 				     data.eth_type);
 		}
 	}
+	if (mirred && flow) {
+		uint16_t if_index = pmd->if_index;
+
+		/*
+		 * If attr->egress && mirred, then this is a special
+		 * case where the rule must be applied on the tap, to
+		 * redirect packets coming from the DPDK App, out
+		 * through the remote netdevice.
+		 */
+		if (attr->egress)
+			if_index = pmd->remote_if_index;
+		if (add_action_mirred(flow, if_index, mirred) < 0)
+			goto exit_action_not_supported;
+		else
+			goto end;
+	}
 	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {
 		int err = 0;
 
@@ -855,6 +1043,7 @@ priv_flow_process(struct pmd_internals *pmd,
 		if (err)
 			goto exit_action_not_supported;
 	}
+end:
 	if (flow)
 		nlattr_nested_finish(&flow->msg); /* nested TCA_OPTIONS */
 	return 0;
@@ -885,7 +1074,7 @@ tap_flow_validate(struct rte_eth_dev *dev,
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
 
-	return priv_flow_process(pmd, attr, items, actions, error, NULL);
+	return priv_flow_process(pmd, attr, items, actions, error, NULL, 0);
 }
 
 /**
@@ -941,6 +1130,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		struct rte_flow_error *error)
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = NULL;
 	struct rte_flow *flow = NULL;
 	struct nlmsg *msg = NULL;
 	int err;
@@ -951,6 +1141,17 @@ tap_flow_create(struct rte_eth_dev *dev,
 				   "can't create rule, ifindex not found");
 		goto fail;
 	}
+	/*
+	 * No rules configured through standard rte_flow should be set on the
+	 * priorities used by implicit rules.
+	 */
+	if ((attr->group == MAX_GROUP) &&
+	    attr->priority > (MAX_PRIORITY - TAP_REMOTE_MAX_IDX)) {
+		rte_flow_error_set(
+			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+			NULL, "priority value too big");
+		goto fail;
+	}
 	flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
 	if (!flow) {
 		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
@@ -962,7 +1163,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
 	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
 	tap_flow_set_handle(flow);
-	if (priv_flow_process(pmd, attr, items, actions, error, flow))
+	if (priv_flow_process(pmd, attr, items, actions, error, flow, 0))
 		goto fail;
 	err = nl_send(pmd->nlsk_fd, &msg->nh);
 	if (err < 0) {
@@ -977,14 +1178,183 @@ tap_flow_create(struct rte_eth_dev *dev,
 		goto fail;
 	}
 	LIST_INSERT_HEAD(&pmd->flows, flow, next);
+	/**
+	 * If a remote device is configured, a TC rule with identical items for
+	 * matching must be set on that device, with a single action: redirect
+	 * to the local pmd->if_index.
+	 */
+	if (pmd->remote_if_index) {
+		remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+		if (!remote_flow) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"cannot allocate memory for rte_flow");
+			goto fail;
+		}
+		msg = &remote_flow->msg;
+		/* set the rule if_index for the remote netdevice */
+		tc_init_msg(
+			msg, pmd->remote_if_index, RTM_NEWTFILTER,
+			NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+		msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+		tap_flow_set_handle(remote_flow);
+		if (priv_flow_process(pmd, attr, items, NULL,
+				      error, remote_flow, TCA_EGRESS_REDIR)) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "rte flow rule validation failed");
+			goto fail;
+		}
+		err = nl_send(pmd->nlsk_fd, &msg->nh);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto fail;
+		}
+		err = nl_recv_ack(pmd->nlsk_fd);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "overlapping rules");
+			goto fail;
+		}
+		flow->remote_flow = remote_flow;
+	}
 	return flow;
 fail:
+	if (remote_flow)
+		rte_free(remote_flow);
 	if (flow)
 		rte_free(flow);
 	return NULL;
 }
 
 /**
+ * Add an implicit flow rule on the remote device to make sure traffic gets to
+ * the tap netdevice from there.
+ *
+ * @param pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to apply.
+ *
+ * @return -1 if the rule couldn't be applied, 0 otherwise.
+ */
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx)
+{
+	struct rte_flow_item *items = implicit_rte_flows[idx].items;
+	struct rte_flow_attr *attr = &implicit_rte_flows[idx].attr;
+	struct rte_flow_item_eth eth_local = { .type = 0 };
+	uint16_t if_index = pmd->remote_if_index;
+	struct rte_flow *remote_flow = NULL;
+	struct nlmsg *msg = NULL;
+	int err = 0;
+	struct rte_flow_item items_local[2] = {
+		[0] = {
+			.type = items[0].type,
+			.spec = &eth_local,
+			.mask = items[0].mask,
+		},
+		[1] = {
+			.type = items[1].type,
+		}
+	};
+
+	remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+	if (!remote_flow) {
+		RTE_LOG(ERR, PMD, "Cannot allocate memory for rte_flow");
+		goto fail;
+	}
+	msg = &remote_flow->msg;
+	if (idx == TAP_REMOTE_TX) {
+		if_index = pmd->if_index;
+	} else if (idx == TAP_REMOTE_LOCAL_MAC) {
+		/*
+		 * eth addr couldn't be set in implicit_rte_flows[] as it is not
+		 * known at compile time.
+		 */
+		memcpy(&eth_local.dst, &pmd->eth_addr, sizeof(pmd->eth_addr));
+		items = items_local;
+	}
+	tc_init_msg(msg, if_index, RTM_NEWTFILTER,
+		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+	tap_flow_set_handle(remote_flow);
+	if (priv_flow_process(pmd, attr, items, NULL, NULL,
+			      remote_flow, implicit_rte_flows[idx].mirred)) {
+		RTE_LOG(ERR, PMD, "rte flow rule validation failed\n");
+		goto fail;
+	}
+	err = nl_send(pmd->nlsk_fd, &msg->nh);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "Failure sending nl request");
+		goto fail;
+	}
+	err = nl_recv_ack(pmd->nlsk_fd);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD,
+			"Kernel refused TC filter rule creation");
+		goto fail;
+	}
+	LIST_INSERT_HEAD(&pmd->implicit_flows, remote_flow, next);
+	return 0;
+fail:
+	if (remote_flow)
+		rte_free(remote_flow);
+	return -1;
+}
+
+/**
+ * Remove specific implicit flow rule on the remote device.
+ *
+ * @param pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to remove.
+ *
+ * @return -1 if one of the implicit rules couldn't be created, 0 otherwise.
+ */
+int tap_flow_implicit_destroy(struct rte_eth_dev *dev,
+			      enum implicit_rule_index idx)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow;
+	int cur_prio = -1;
+	int idx_prio = implicit_rte_flows[idx].attr.priority + PRIORITY_OFFSET;
+
+	for (remote_flow = LIST_FIRST(&pmd->implicit_flows);
+	     remote_flow;
+	     remote_flow = LIST_NEXT(remote_flow, next)) {
+		cur_prio = (remote_flow->msg.t.tcm_info >> 16) & PRIORITY_MASK;
+		if (cur_prio != idx_prio)
+			continue;
+		return tap_flow_destroy(dev, remote_flow, NULL);
+	}
+	return 0;
+}
+
+/**
+ * Destroy all implicit flows.
+ *
+ * @see rte_flow_flush()
+ */
+int
+tap_flow_implicit_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow;
+
+	while (!LIST_EMPTY(&pmd->implicit_flows)) {
+		remote_flow = LIST_FIRST(&pmd->implicit_flows);
+		if (tap_flow_destroy(dev, remote_flow, error) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+/**
  * Destroy a flow.
  *
  * @see rte_flow_destroy()
@@ -996,6 +1366,7 @@ tap_flow_destroy(struct rte_eth_dev *dev,
 		 struct rte_flow_error *error)
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = flow->remote_flow;
 	int ret = 0;
 
 	LIST_REMOVE(flow, next);
@@ -1009,11 +1380,34 @@ tap_flow_destroy(struct rte_eth_dev *dev,
 		goto end;
 	}
 	ret = nl_recv_ack(pmd->nlsk_fd);
-	if (ret < 0)
+	if (ret < 0) {
 		rte_flow_error_set(
 			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
 			"couldn't receive kernel ack to our request");
+		goto end;
+	}
+	if (remote_flow) {
+		remote_flow->msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+		remote_flow->msg.nh.nlmsg_type = RTM_DELTFILTER;
+
+		ret = nl_send(pmd->nlsk_fd, &remote_flow->msg.nh);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto end;
+		}
+		ret = nl_recv_ack(pmd->nlsk_fd);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure trying to receive nl ack");
+			goto end;
+		}
+	}
 end:
+	if (remote_flow)
+		rte_free(remote_flow);
 	rte_free(flow);
 	return ret;
 }
diff --git a/drivers/net/tap/tap_flow.h b/drivers/net/tap/tap_flow.h
index a05e945df523..0134cdbaeb90 100644
--- a/drivers/net/tap/tap_flow.h
+++ b/drivers/net/tap/tap_flow.h
@@ -36,6 +36,7 @@
 
 #include <rte_flow.h>
 #include <rte_flow_driver.h>
+#include <tap.h>
 
 /**
  * In TC, priority 0 means we require the kernel to allocate one for us.
@@ -49,10 +50,33 @@
 #define GROUP_SHIFT 12
 #define MAX_GROUP GROUP_MASK
 
+/**
+ * These index are actually in reversed order: their priority is processed
+ * by subtracting their value to the lowest priority (PRIORITY_MASK).
+ * Thus the first one will have the lowest priority in the end
+ * (but biggest value).
+ */
+enum implicit_rule_index {
+	TAP_REMOTE_TX,
+	TAP_REMOTE_BROADCASTV6,
+	TAP_REMOTE_BROADCAST,
+	TAP_REMOTE_ALLMULTI,
+	TAP_REMOTE_PROMISC,
+	TAP_REMOTE_LOCAL_MAC,
+	TAP_REMOTE_MAX_IDX,
+};
+
 int tap_dev_filter_ctrl(struct rte_eth_dev *dev,
 			enum rte_filter_type filter_type,
 			enum rte_filter_op filter_op,
 			void *arg);
 int tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error);
 
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx);
+int tap_flow_implicit_destroy(struct rte_eth_dev *dev,
+			      enum implicit_rule_index idx);
+int tap_flow_implicit_flush(struct rte_eth_dev *dev,
+			    struct rte_flow_error *error);
+
 #endif /* _TAP_FLOW_H_ */
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 2/4] net/tap: reflect tap flags on the remote
  2017-03-08 10:06   ` [PATCH v3 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
  2017-03-08 10:06     ` [PATCH v3 1/4] net/tap: add " Pascal Mazon
@ 2017-03-08 10:06     ` Pascal Mazon
  2017-03-08 10:06     ` [PATCH v3 3/4] net/tap: use the remote MAC address if available Pascal Mazon
                       ` (3 subsequent siblings)
  5 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-08 10:06 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

Synchronize PROMISC and ALLMULTI flags to the remote netdevice if
possible.

Leave the IFF_UP flag as it is, however.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 drivers/net/tap/rte_eth_tap.c | 37 ++++++++++++++++++++++++-------------
 drivers/net/tap/tap.h         |  2 ++
 2 files changed, 26 insertions(+), 13 deletions(-)

diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index f3d9d8fe96aa..b0f5ffbb8ace 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -254,7 +254,7 @@ pmd_tx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 }
 
 static int
-tap_link_set_flags(struct pmd_internals *pmd, short flags, int add)
+tap_netdev_set_flags(const char *iface, short flags, int add)
 {
 	struct ifreq ifr;
 	int err, s;
@@ -271,11 +271,11 @@ tap_link_set_flags(struct pmd_internals *pmd, short flags, int add)
 		return -1;
 	}
 	memset(&ifr, 0, sizeof(ifr));
-	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", pmd->name);
+	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", iface);
 	err = ioctl(s, SIOCGIFFLAGS, &ifr);
 	if (err < 0) {
 		RTE_LOG(WARNING, PMD, "Unable to get %s device flags: %s\n",
-			pmd->name, strerror(errno));
+			iface, strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -293,6 +293,7 @@ tap_link_set_flags(struct pmd_internals *pmd, short flags, int add)
 	close(s);
 
 	return 0;
+
 }
 
 static int
@@ -301,7 +302,7 @@ tap_link_set_down(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->dev_link.link_status = ETH_LINK_DOWN;
-	return tap_link_set_flags(pmd, IFF_UP | IFF_NOARP, 0);
+	return tap_netdev_set_flags(pmd->name, IFF_UP | IFF_NOARP, 0);
 }
 
 static int
@@ -310,7 +311,7 @@ tap_link_set_up(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->dev_link.link_status = ETH_LINK_UP;
-	return tap_link_set_flags(pmd, IFF_UP | IFF_NOARP, 1);
+	return tap_netdev_set_flags(pmd->name, IFF_UP | IFF_NOARP, 1);
 }
 
 static int
@@ -482,9 +483,11 @@ tap_promisc_enable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->promiscuous = 1;
-	tap_link_set_flags(pmd, IFF_PROMISC, 1);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_PROMISC, 1);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_PROMISC, 1);
+	}
 }
 
 static void
@@ -493,9 +496,11 @@ tap_promisc_disable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->promiscuous = 0;
-	tap_link_set_flags(pmd, IFF_PROMISC, 0);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_PROMISC, 0);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_destroy(dev, TAP_REMOTE_PROMISC);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_PROMISC, 0);
+	}
 }
 
 static void
@@ -504,9 +509,11 @@ tap_allmulti_enable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->all_multicast = 1;
-	tap_link_set_flags(pmd, IFF_ALLMULTI, 1);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_ALLMULTI, 1);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_ALLMULTI, 1);
+	}
 }
 
 static void
@@ -515,9 +522,11 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 	struct pmd_internals *pmd = dev->data->dev_private;
 
 	dev->data->all_multicast = 0;
-	tap_link_set_flags(pmd, IFF_ALLMULTI, 0);
-	if (pmd->remote_if_index)
+	tap_netdev_set_flags(pmd->name, IFF_ALLMULTI, 0);
+	if (pmd->remote_if_index) {
 		tap_flow_implicit_destroy(dev, TAP_REMOTE_ALLMULTI);
+		tap_netdev_set_flags(pmd->remote_iface, IFF_ALLMULTI, 0);
+	}
 }
 
 static void
@@ -962,6 +971,8 @@ eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 	 */
 	pmd->nlsk_fd = nl_init();
 	if (strlen(remote_iface)) {
+		snprintf(pmd->remote_iface, RTE_ETH_NAME_MAX_LEN,
+			 "%s", remote_iface);
 		pmd->remote_if_index = if_nametoindex(remote_iface);
 		if (!pmd->remote_if_index)
 			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
diff --git a/drivers/net/tap/tap.h b/drivers/net/tap/tap.h
index 4c4de939f1cc..9811d0b0f085 100644
--- a/drivers/net/tap/tap.h
+++ b/drivers/net/tap/tap.h
@@ -63,9 +63,11 @@ struct tx_queue {
 };
 
 struct pmd_internals {
+	char remote_iface[RTE_ETH_NAME_MAX_LEN]; /* Remote netdevice name */
 	char name[RTE_ETH_NAME_MAX_LEN];  /* Internal Tap device name */
 	uint16_t nb_queues;               /* Number of queues supported */
 	struct ether_addr eth_addr;       /* Mac address of the device port */
+	int remote_if_index;              /* remote netdevice IF_INDEX */
 	int if_index;                     /* IF_INDEX for the port */
 	int nlsk_fd;                      /* Netlink socket fd */
 	int flower_support;               /* 1 if kernel supports, else 0 */
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 3/4] net/tap: use the remote MAC address if available
  2017-03-08 10:06   ` [PATCH v3 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
  2017-03-08 10:06     ` [PATCH v3 1/4] net/tap: add " Pascal Mazon
  2017-03-08 10:06     ` [PATCH v3 2/4] net/tap: reflect tap flags on the remote Pascal Mazon
@ 2017-03-08 10:06     ` Pascal Mazon
  2017-03-08 10:06     ` [PATCH v3 4/4] net/tap: set MTU on the remote Pascal Mazon
                       ` (2 subsequent siblings)
  5 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-08 10:06 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

The remote on a tap is most likely used with netdevices that are not
under DPDK control. Outgoing traffic is supposed to use the source MAC
address of the remote netdevice.

This commit synchronizes the MAC address of the local tap netdevice with
the remote one.

Of course, it is still possible to change the tap MAC address, using
standard DPDK APIs. It sets that MAC address on the tap PMD and redirect
any packets matching that destination MAC to the tap PMD. It also tries
setting the MAC address directly on the remote, if supported, through
ioctl() calls.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 drivers/net/tap/rte_eth_tap.c | 77 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 65 insertions(+), 12 deletions(-)

diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index b0f5ffbb8ace..05ed0a131d63 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -529,6 +529,65 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 	}
 }
 
+static int
+tap_netdev_set_mac(const char *iface, struct ether_addr *mac_addr)
+{
+	struct ifreq ifr;
+	int err, s;
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		RTE_LOG(ERR, PMD,
+			"Unable to get a socket to get MAC: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+	err = ioctl(s, SIOCGIFHWADDR, &ifr);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "%s: couldn't get current MAC address (%s)\n",
+			iface, strerror(errno));
+		close(s);
+		return -1;
+	}
+	rte_memcpy(ifr.ifr_hwaddr.sa_data, mac_addr, ETHER_ADDR_LEN);
+	err = ioctl(s, SIOCSIFHWADDR, &ifr) == -1;
+	close(s);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "%s: couldn't set current MAC address (%s)\n",
+			iface, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+tap_netdev_get_mac(const char *iface, struct ether_addr *mac_addr)
+{
+	struct ifreq ifr;
+	int err, s;
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		RTE_LOG(ERR, PMD,
+			"Unable to get a socket to get MAC: %s\n",
+			strerror(errno));
+		return -1;
+	}
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, iface, IFNAMSIZ);
+	err = ioctl(s, SIOCGIFHWADDR, &ifr);
+	close(s);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "%s: couldn't get MAC address (%s)\n",
+			iface, strerror(errno));
+		return -1;
+	}
+	rte_memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+	return 0;
+}
+
 static void
 tap_mac_remove(struct rte_eth_dev *dev __rte_unused,
 	       uint32_t index __rte_unused)
@@ -545,7 +604,6 @@ tap_mac_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
 {
 	struct pmd_internals *internals = dev->data->dev_private;
 	int fd = internals->rxq[0].fd;
-	struct ifreq ifr;
 
 	if (index > RTE_PMD_TAP_MAX_MAC_ADDRS - 1) {
 		RTE_LOG(ERR, PMD,
@@ -565,19 +623,11 @@ tap_mac_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
 			dev->data->name);
 		return;
 	}
-	memset(&ifr, 0, sizeof(struct ifreq));
-	if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {
-		RTE_LOG(ERR, PMD, "%s: couldn't get current MAC address (%s)\n",
-			dev->data->name, strerror(errno));
+	if (tap_netdev_set_mac(internals->name, mac_addr) < 0)
 		return;
-	}
-	rte_memcpy(ifr.ifr_hwaddr.sa_data, mac_addr, ETHER_ADDR_LEN);
-	if (ioctl(fd, SIOCSIFHWADDR, &ifr) == -1) {
-		RTE_LOG(ERR, PMD, "%s: couldn't set current MAC address (%s)\n",
-			dev->data->name, strerror(errno));
-		return;
-	}
 	rte_memcpy(&dev->data->mac_addrs[index], mac_addr, ETHER_ADDR_LEN);
+	if (internals->remote_if_index)
+		tap_netdev_set_mac(internals->remote_iface, mac_addr);
 }
 
 static void
@@ -978,6 +1028,9 @@ eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
 				"remote interface will remain unconfigured\n",
 				remote_iface);
+		else
+			/* Set the local mac address to the remote mac */
+			tap_netdev_get_mac(remote_iface, &pmd->eth_addr);
 	}
 
 	return 0;
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 4/4] net/tap: set MTU on the remote
  2017-03-08 10:06   ` [PATCH v3 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
                       ` (2 preceding siblings ...)
  2017-03-08 10:06     ` [PATCH v3 3/4] net/tap: use the remote MAC address if available Pascal Mazon
@ 2017-03-08 10:06     ` Pascal Mazon
  2017-03-14  8:34     ` [PATCH v4] net/tap: remote netdevice traffic capture Pascal Mazon
  2017-03-15 15:03     ` [PATCH v5] net/tap: " Pascal Mazon
  5 siblings, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-08 10:06 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
---
 drivers/net/tap/rte_eth_tap.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index 05ed0a131d63..9edf6b355a47 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -839,9 +839,8 @@ tap_set_mc_addr_list(struct rte_eth_dev *dev __rte_unused,
 }
 
 static int
-tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+tap_netdev_mtu_set(const char *iface, uint16_t mtu)
 {
-	struct pmd_internals *pmd = dev->data->dev_private;
 	struct ifreq ifr;
 	int err, s;
 
@@ -849,15 +848,15 @@ tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
 	if (s < 0) {
 		RTE_LOG(ERR, PMD,
 			"Unable to get a socket for %s to set flags: %s\n",
-			pmd->name, strerror(errno));
+			iface, strerror(errno));
 		return -1;
 	}
 	memset(&ifr, 0, sizeof(ifr));
-	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", pmd->name);
+	snprintf(ifr.ifr_name, IFNAMSIZ, "%s", iface);
 	err = ioctl(s, SIOCGIFMTU, &ifr);
 	if (err < 0) {
 		RTE_LOG(WARNING, PMD, "Unable to get %s device MTU: %s\n",
-			pmd->name, strerror(errno));
+			iface, strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -865,11 +864,29 @@ tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
 	err = ioctl(s, SIOCSIFMTU, &ifr);
 	if (err < 0) {
 		RTE_LOG(WARNING, PMD, "Unable to set %s mtu %d: %s\n",
-			pmd->name, mtu, strerror(errno));
+			iface, mtu, strerror(errno));
 		close(s);
 		return -1;
 	}
 	close(s);
+	return 0;
+}
+
+static int
+tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+	int err;
+
+	/* First try to set mtu on the remote */
+	if (pmd->remote_if_index) {
+		err = tap_netdev_mtu_set(pmd->remote_iface, mtu);
+		if (err < 0)
+			return err;
+	}
+	err = tap_netdev_mtu_set(pmd->name, mtu);
+	if (err < 0)
+		return err;
 	dev->data->mtu = mtu;
 	return 0;
 }
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v4] net/tap: remote netdevice traffic capture
  2017-03-08 10:06   ` [PATCH v3 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
                       ` (3 preceding siblings ...)
  2017-03-08 10:06     ` [PATCH v3 4/4] net/tap: set MTU on the remote Pascal Mazon
@ 2017-03-14  8:34     ` Pascal Mazon
  2017-03-14  8:34       ` [PATCH v4] net/tap: add " Pascal Mazon
  2017-03-15 15:03     ` [PATCH v5] net/tap: " Pascal Mazon
  5 siblings, 1 reply; 24+ messages in thread
From: Pascal Mazon @ 2017-03-14  8:34 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

This patchset adds the special "remote" feature to the tap PMD, that
actually enables capturing traffic from another netdevice. This is
especially useful to get packets into the DPDK app, when the remote
netdevice has no DPDK support.

The "remote" feature requires flow API support as flow rules will be
configured on the remote netdevice for redirection, using the same
mechanism.

This series applies on top of:

  [PATCH 0/4] net/tap: support flow API

v2 changes:
  - rebase on top of updated "net/tap: support flow API"
  - fix implicit flow flush when closing the netdevices

v3 changes:
  - memset(0) for remote_iface in rte_pmd_tap_probe()
  - use snprintf instead of strncpy to correctly handle terminating \0

v4 changes:
  - rebase on top of updated "net/tap: support flow API"
  - use only a single patch now as MTU, MAC and flags can be easily managed
    with tap_ioctl()

Pascal Mazon (1):
  net/tap: add remote netdevice traffic capture

 doc/guides/nics/tap.rst       |  17 ++
 drivers/net/tap/rte_eth_tap.c | 101 +++++++++-
 drivers/net/tap/rte_eth_tap.h |   4 +
 drivers/net/tap/tap_flow.c    | 451 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 575 insertions(+), 22 deletions(-)

-- 
2.8.0.rc0

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v4] net/tap: add remote netdevice traffic capture
  2017-03-14  8:34     ` [PATCH v4] net/tap: remote netdevice traffic capture Pascal Mazon
@ 2017-03-14  8:34       ` Pascal Mazon
  2017-03-14 14:00         ` Wiles, Keith
  0 siblings, 1 reply; 24+ messages in thread
From: Pascal Mazon @ 2017-03-14  8:34 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

By default, a tap netdevice is of no use when not fed by a separate
process. The ability to automatically feed it from another netdevice
allows applications to capture any kind of traffic normally destined to
the kernel stack.

This patch implements this ability through a new optional "remote"
parameter.

Packets matching filtering rules created with the flow API are matched
on the remote device and redirected to the tap PMD, where the relevant
action will be performed.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
 doc/guides/nics/tap.rst       |  17 ++
 drivers/net/tap/rte_eth_tap.c | 101 +++++++++-
 drivers/net/tap/rte_eth_tap.h |   4 +
 drivers/net/tap/tap_flow.c    | 451 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 575 insertions(+), 22 deletions(-)

diff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst
index cdb528b5eae4..676a569b00ca 100644
--- a/doc/guides/nics/tap.rst
+++ b/doc/guides/nics/tap.rst
@@ -58,6 +58,23 @@ needed, but the interface does not enforce that speed, for example::
 
    --vdev=net_tap0,iface=foo0,speed=25000
 
+It is possible to specify a remote netdevice to capture packets from by adding
+``remote=foo1``, for example::
+
+   --vdev=net_tap,iface=tap0,remote=foo1
+
+If a ``remote`` is set, then all packets with the tap PMD's local MAC coming
+in on the remote netdevice will be redirected to the tap.
+If the tap is in promiscuous mode, then all packets will be redirected.
+In allmulti mode, all multicast packets will be redirected.
+It is possible to add explicit rte_flow rules on the tap PMD to capture specific
+traffic. For instance, in testpmd, the following rte_flow rule would capture
+packets with the given MAC address from the remote, and send it to the tap RX
+QUEUE 3::
+
+   testpmd> flow create 0 ingress pattern eth src is 02:03:04:05:06:07 / \
+            end actions queue index 3 / end
+
 After the DPDK application is started you can send and receive packets on the
 interface using the standard rx_burst/tx_burst APIs in DPDK. From the host
 point of view you can use any host tool like tcpdump, Wireshark, ping, Pktgen
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index ed2099212e2a..b4601d49fa2c 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -63,6 +63,7 @@
 
 #define ETH_TAP_IFACE_ARG       "iface"
 #define ETH_TAP_SPEED_ARG       "speed"
+#define ETH_TAP_REMOTE_ARG      "remote"
 
 #ifdef IFF_MULTI_QUEUE
 #define RTE_PMD_TAP_MAX_QUEUES	16
@@ -78,6 +79,7 @@ static struct rte_vdev_driver pmd_tap_drv;
 static const char *valid_arguments[] = {
 	ETH_TAP_IFACE_ARG,
 	ETH_TAP_SPEED_ARG,
+	ETH_TAP_REMOTE_ARG,
 	NULL
 };
 
@@ -187,10 +189,43 @@ tun_alloc(struct pmd_internals *pmd, uint16_t qid)
 				pmd->name);
 			return fd;
 		}
+		if (pmd->remote_if_index) {
+			/*
+			 * Flush usually returns negative value because it tries
+			 * to delete every QDISC (and on a running device, one
+			 * QDISC at least is needed). Ignore negative return
+			 * value.
+			 */
+			qdisc_flush(pmd->nlsk_fd, pmd->remote_if_index);
+			if (qdisc_create_ingress(pmd->nlsk_fd,
+						 pmd->remote_if_index) < 0)
+				goto remote_fail;
+			LIST_INIT(&pmd->implicit_flows);
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_LOCAL_MAC) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCAST) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCASTV6) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_TX) < 0)
+				goto remote_fail;
+		}
 	}
 
 	return fd;
 
+remote_fail:
+	RTE_LOG(ERR, PMD,
+		"Could not set up remote flow rules for %s: remote disabled.\n",
+		pmd->name);
+	pmd->remote_if_index = 0;
+	tap_flow_implicit_flush(pmd, NULL);
+	return fd;
+
 error:
 	if (fd > 0)
 		close(fd);
@@ -289,8 +324,17 @@ tap_ioctl(struct pmd_internals *pmd, unsigned long request,
 	  struct ifreq *ifr, int set)
 {
 	short req_flags = ifr->ifr_flags;
+	int remote = !!pmd->remote_if_index;
 
-	snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->name);
+	/*
+	 * If there is a remote netdevice, apply ioctl on it, then apply it on
+	 * the tap netdevice.
+	 */
+apply:
+	if (remote)
+		snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->remote_iface);
+	else
+		snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->name);
 	switch (request) {
 	case SIOCSIFFLAGS:
 		/* fetch current flags to leave other flags untouched */
@@ -302,6 +346,12 @@ tap_ioctl(struct pmd_internals *pmd, unsigned long request,
 			ifr->ifr_flags &= ~req_flags;
 		break;
 	case SIOCGIFHWADDR:
+		/* Set remote MAC on the tap netdevice */
+		if (!remote && pmd->remote_if_index) {
+			request = SIOCSIFHWADDR;
+			goto apply;
+		}
+		break;
 	case SIOCSIFHWADDR:
 	case SIOCSIFMTU:
 		break;
@@ -312,6 +362,8 @@ tap_ioctl(struct pmd_internals *pmd, unsigned long request,
 	}
 	if (ioctl(pmd->ioctl_sock, request, ifr) < 0)
 		goto error;
+	if (remote--)
+		goto apply;
 	return 0;
 
 error:
@@ -465,6 +517,7 @@ tap_dev_close(struct rte_eth_dev *dev __rte_unused)
 
 	tap_link_set_down(dev);
 	tap_flow_flush(dev, NULL);
+	tap_flow_implicit_flush(internals, NULL);
 
 	for (i = 0; i < internals->nb_queues; i++) {
 		if (internals->rxq[i].fd != -1)
@@ -511,6 +564,8 @@ tap_promisc_enable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 1;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -521,6 +576,8 @@ tap_promisc_disable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 0;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(pmd, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -531,6 +588,8 @@ tap_allmulti_enable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 1;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);
 }
 
 static void
@@ -541,6 +600,8 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 0;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(pmd, TAP_REMOTE_ALLMULTI);
 }
 
 
@@ -810,7 +871,7 @@ tap_kernel_support(struct pmd_internals *pmd)
 }
 
 static int
-eth_dev_tap_create(const char *name, char *tap_name)
+eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 {
 	int numa_node = rte_socket_id();
 	struct rte_eth_dev *dev = NULL;
@@ -887,6 +948,15 @@ eth_dev_tap_create(const char *name, char *tap_name)
 	 * creating/destroying flow rules.
 	 */
 	pmd->nlsk_fd = nl_init();
+	if (strlen(remote_iface)) {
+		pmd->remote_if_index = if_nametoindex(remote_iface);
+		snprintf(pmd->remote_iface, RTE_ETH_NAME_MAX_LEN,
+			 "%s", remote_iface);
+		if (!pmd->remote_if_index)
+			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
+				"remote interface will remain unconfigured\n",
+				remote_iface);
+	}
 
 	return 0;
 
@@ -927,6 +997,19 @@ set_interface_speed(const char *key __rte_unused,
 	return 0;
 }
 
+static int
+set_remote_iface(const char *key __rte_unused,
+		 const char *value,
+		 void *extra_args)
+{
+	char *name = (char *)extra_args;
+
+	if (value)
+		snprintf(name, RTE_ETH_NAME_MAX_LEN, "%s", value);
+
+	return 0;
+}
+
 /* Open a TAP interface device.
  */
 static int
@@ -936,10 +1019,12 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	struct rte_kvargs *kvlist = NULL;
 	int speed;
 	char tap_name[RTE_ETH_NAME_MAX_LEN];
+	char remote_iface[RTE_ETH_NAME_MAX_LEN];
 
 	speed = ETH_SPEED_NUM_10G;
 	snprintf(tap_name, sizeof(tap_name), "%s%d",
 		 DEFAULT_TAP_NAME, tap_unit++);
+	memset(remote_iface, 0, RTE_ETH_NAME_MAX_LEN);
 
 	if (params && (params[0] != '\0')) {
 		RTE_LOG(DEBUG, PMD, "paramaters (%s)\n", params);
@@ -963,6 +1048,15 @@ rte_pmd_tap_probe(const char *name, const char *params)
 				if (ret == -1)
 					goto leave;
 			}
+
+			if (rte_kvargs_count(kvlist, ETH_TAP_REMOTE_ARG) == 1) {
+				ret = rte_kvargs_process(kvlist,
+							 ETH_TAP_REMOTE_ARG,
+							 &set_remote_iface,
+							 remote_iface);
+				if (ret == -1)
+					goto leave;
+			}
 		}
 	}
 	pmd_link.link_speed = speed;
@@ -970,7 +1064,7 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	RTE_LOG(NOTICE, PMD, "Initializing pmd_tap for %s as %s\n",
 		name, tap_name);
 
-	ret = eth_dev_tap_create(name, tap_name);
+	ret = eth_dev_tap_create(name, tap_name, remote_iface);
 
 leave:
 	if (ret == -1) {
@@ -1003,6 +1097,7 @@ rte_pmd_tap_remove(const char *name)
 	internals = eth_dev->data->dev_private;
 	if (internals->flower_support && internals->nlsk_fd) {
 		tap_flow_flush(eth_dev, NULL);
+		tap_flow_implicit_flush(internals, NULL);
 		nl_final(internals->nlsk_fd);
 	}
 	for (i = 0; i < internals->nb_queues; i++)
diff --git a/drivers/net/tap/rte_eth_tap.h b/drivers/net/tap/rte_eth_tap.h
index 15ae0b980ace..bd32e93aa59c 100644
--- a/drivers/net/tap/rte_eth_tap.h
+++ b/drivers/net/tap/rte_eth_tap.h
@@ -63,15 +63,19 @@ struct tx_queue {
 };
 
 struct pmd_internals {
+	char remote_iface[RTE_ETH_NAME_MAX_LEN]; /* Remote netdevice name */
 	char name[RTE_ETH_NAME_MAX_LEN];  /* Internal Tap device name */
 	uint16_t nb_queues;               /* Number of queues supported */
 	struct ether_addr eth_addr;       /* Mac address of the device port */
+	int remote_if_index;              /* remote netdevice IF_INDEX */
 	int if_index;                     /* IF_INDEX for the port */
 	int ioctl_sock;                   /* socket for ioctl calls */
 	int nlsk_fd;                      /* Netlink socket fd */
 	int flower_support;               /* 1 if kernel supports, else 0 */
 	int flower_vlan_support;          /* 1 if kernel supports, else 0 */
 	LIST_HEAD(tap_flows, rte_flow) flows;        /* rte_flow rules */
+	/* implicit rte_flow rules set when a remote device is active */
+	LIST_HEAD(tap_implicit_flows, rte_flow) implicit_flows;
 	struct rx_queue rxq[RTE_PMD_TAP_MAX_QUEUES]; /* List of RX queues */
 	struct tx_queue txq[RTE_PMD_TAP_MAX_QUEUES]; /* List of TX queues */
 };
diff --git a/drivers/net/tap/tap_flow.c b/drivers/net/tap/tap_flow.c
index b119c31b0dea..7c9ce542a7c2 100644
--- a/drivers/net/tap/tap_flow.c
+++ b/drivers/net/tap/tap_flow.c
@@ -82,6 +82,7 @@ enum {
 
 struct rte_flow {
 	LIST_ENTRY(rte_flow) next; /* Pointer to the next rte_flow structure */
+	struct rte_flow *remote_flow; /* associated remote flow */
 	struct nlmsg msg;
 };
 
@@ -92,6 +93,12 @@ struct convert_data {
 	struct rte_flow *flow;
 };
 
+struct remote_rule {
+	struct rte_flow_attr attr;
+	struct rte_flow_item items[2];
+	int mirred;
+};
+
 static int tap_flow_create_eth(const struct rte_flow_item *item, void *data);
 static int tap_flow_create_vlan(const struct rte_flow_item *item, void *data);
 static int tap_flow_create_ipv4(const struct rte_flow_item *item, void *data);
@@ -249,6 +256,114 @@ static const struct tap_flow_items tap_flow_items[] = {
 	},
 };
 
+static struct remote_rule implicit_rte_flows[TAP_REMOTE_MAX_IDX] = {
+	[TAP_REMOTE_LOCAL_MAC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_LOCAL_MAC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_REDIR,
+	},
+	[TAP_REMOTE_BROADCAST] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCAST,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_BROADCASTV6] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCASTV6,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_PROMISC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_PROMISC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_ALLMULTI] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_ALLMULTI,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_TX] = {
+		.attr = {
+			.group = 0,
+			.priority = TAP_REMOTE_TX,
+			.egress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+};
+
 /**
  * Make as much checks as possible on an Ethernet item, and if a flow is
  * provided, fill it appropriately with Ethernet info.
@@ -673,6 +788,47 @@ add_action_gact(struct rte_flow *flow, int action)
 }
 
 /**
+ * Transform a MIRRED action item in the provided flow for TC.
+ *
+ * @param[in, out] flow
+ *   Flow to be filled.
+ * @param[in] ifindex
+ *   Netdevice ifindex, where to mirror/redirect packet to.
+ * @param[in] action_type
+ *   Either TCA_EGRESS_REDIR for redirection or TCA_EGRESS_MIRROR for mirroring.
+ *
+ * @return
+ *   0 if checks are alright, -1 otherwise.
+ */
+static int
+add_action_mirred(struct rte_flow *flow, uint16_t ifindex, uint16_t action_type)
+{
+	struct nlmsg *msg = &flow->msg;
+	size_t act_index = 1;
+	struct tc_mirred p = {
+		.eaction = action_type,
+		.ifindex = ifindex,
+	};
+
+	if (nlattr_nested_start(msg, TCA_FLOWER_ACT) < 0)
+		return -1;
+	if (nlattr_nested_start(msg, act_index++) < 0)
+		return -1;
+	nlattr_add(&msg->nh, TCA_ACT_KIND, sizeof("mirred"), "mirred");
+	if (nlattr_nested_start(msg, TCA_ACT_OPTIONS) < 0)
+		return -1;
+	if (action_type == TCA_EGRESS_MIRROR)
+		p.action = TC_ACT_PIPE;
+	else /* REDIRECT */
+		p.action = TC_ACT_STOLEN;
+	nlattr_add(&msg->nh, TCA_MIRRED_PARMS, sizeof(p), &p);
+	nlattr_nested_finish(msg); /* nested TCA_ACT_OPTIONS */
+	nlattr_nested_finish(msg); /* nested act_index */
+	nlattr_nested_finish(msg); /* nested TCA_FLOWER_ACT */
+	return 0;
+}
+
+/**
  * Transform a QUEUE action item in the provided flow for TC.
  *
  * @param[in, out] flow
@@ -723,6 +879,15 @@ add_action_skbedit(struct rte_flow *flow, uint16_t queue)
  *   Perform verbose error reporting if not NULL.
  * @param[in, out] flow
  *   Flow structure to update.
+ * @param[in] mirred
+ *   If set to TCA_EGRESS_REDIR, provided actions will be replaced with a
+ *   redirection to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd.
+ *   If set to TCA_EGRESS_MIRROR, provided actions will be replaced with a
+ *   mirroring to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd. Matching packets will thus be duplicated.
+ *   If set to 0, the standard behavior is to be used: set correct actions for
+ *   the TC rule, and apply it on the tap netdevice.
  *
  * @return
  *   0 on success, a negative errno value otherwise and rte_errno is set.
@@ -733,7 +898,8 @@ priv_flow_process(struct pmd_internals *pmd,
 		  const struct rte_flow_item items[],
 		  const struct rte_flow_action actions[],
 		  struct rte_flow_error *error,
-		  struct rte_flow *flow)
+		  struct rte_flow *flow,
+		  int mirred)
 {
 	const struct tap_flow_items *cur_item = tap_flow_items;
 	struct convert_data data = {
@@ -760,15 +926,21 @@ priv_flow_process(struct pmd_internals *pmd,
 		flow->msg.t.tcm_info = TC_H_MAKE(prio << 16,
 						 flow->msg.t.tcm_info);
 	}
-	if (!attr->ingress) {
-		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR,
-				   NULL, "direction should be ingress");
-		return -rte_errno;
-	}
-	/* rte_flow ingress is actually egress as seen in the kernel */
-	if (attr->ingress && flow)
-		flow->msg.t.tcm_parent = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
 	if (flow) {
+		if (mirred) {
+			/*
+			 * If attr->ingress, the rule applies on remote ingress
+			 * to match incoming packets
+			 * If attr->egress, the rule applies on tap ingress (as
+			 * seen from the kernel) to deal with packets going out
+			 * from the DPDK app.
+			 */
+			flow->msg.t.tcm_parent = TC_H_MAKE(TC_H_INGRESS, 0);
+		} else {
+			/* Standard rule on tap egress (kernel standpoint). */
+			flow->msg.t.tcm_parent =
+				TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
+		}
 		/* use flower filter type */
 		nlattr_add(&flow->msg.nh, TCA_KIND, sizeof("flower"), "flower");
 		if (nlattr_nested_start(&flow->msg, TCA_OPTIONS) < 0)
@@ -821,6 +993,22 @@ priv_flow_process(struct pmd_internals *pmd,
 				     data.eth_type);
 		}
 	}
+	if (mirred && flow) {
+		uint16_t if_index = pmd->if_index;
+
+		/*
+		 * If attr->egress && mirred, then this is a special
+		 * case where the rule must be applied on the tap, to
+		 * redirect packets coming from the DPDK App, out
+		 * through the remote netdevice.
+		 */
+		if (attr->egress)
+			if_index = pmd->remote_if_index;
+		if (add_action_mirred(flow, if_index, mirred) < 0)
+			goto exit_action_not_supported;
+		else
+			goto end;
+	}
 	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {
 		int err = 0;
 
@@ -855,6 +1043,7 @@ priv_flow_process(struct pmd_internals *pmd,
 		if (err)
 			goto exit_action_not_supported;
 	}
+end:
 	if (flow)
 		nlattr_nested_finish(&flow->msg); /* nested TCA_OPTIONS */
 	return 0;
@@ -885,7 +1074,7 @@ tap_flow_validate(struct rte_eth_dev *dev,
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
 
-	return priv_flow_process(pmd, attr, items, actions, error, NULL);
+	return priv_flow_process(pmd, attr, items, actions, error, NULL, 0);
 }
 
 /**
@@ -941,6 +1130,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		struct rte_flow_error *error)
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = NULL;
 	struct rte_flow *flow = NULL;
 	struct nlmsg *msg = NULL;
 	int err;
@@ -951,6 +1141,17 @@ tap_flow_create(struct rte_eth_dev *dev,
 				   "can't create rule, ifindex not found");
 		goto fail;
 	}
+	/*
+	 * No rules configured through standard rte_flow should be set on the
+	 * priorities used by implicit rules.
+	 */
+	if ((attr->group == MAX_GROUP) &&
+	    attr->priority > (MAX_PRIORITY - TAP_REMOTE_MAX_IDX)) {
+		rte_flow_error_set(
+			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+			NULL, "priority value too big");
+		goto fail;
+	}
 	flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
 	if (!flow) {
 		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
@@ -962,7 +1163,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
 	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
 	tap_flow_set_handle(flow);
-	if (priv_flow_process(pmd, attr, items, actions, error, flow))
+	if (priv_flow_process(pmd, attr, items, actions, error, flow, 0))
 		goto fail;
 	err = nl_send(pmd->nlsk_fd, &msg->nh);
 	if (err < 0) {
@@ -977,25 +1178,76 @@ tap_flow_create(struct rte_eth_dev *dev,
 		goto fail;
 	}
 	LIST_INSERT_HEAD(&pmd->flows, flow, next);
+	/**
+	 * If a remote device is configured, a TC rule with identical items for
+	 * matching must be set on that device, with a single action: redirect
+	 * to the local pmd->if_index.
+	 */
+	if (pmd->remote_if_index) {
+		remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+		if (!remote_flow) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"cannot allocate memory for rte_flow");
+			goto fail;
+		}
+		msg = &remote_flow->msg;
+		/* set the rule if_index for the remote netdevice */
+		tc_init_msg(
+			msg, pmd->remote_if_index, RTM_NEWTFILTER,
+			NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+		msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+		tap_flow_set_handle(remote_flow);
+		if (priv_flow_process(pmd, attr, items, NULL,
+				      error, remote_flow, TCA_EGRESS_REDIR)) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "rte flow rule validation failed");
+			goto fail;
+		}
+		err = nl_send(pmd->nlsk_fd, &msg->nh);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto fail;
+		}
+		err = nl_recv_ack(pmd->nlsk_fd);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "overlapping rules");
+			goto fail;
+		}
+		flow->remote_flow = remote_flow;
+	}
 	return flow;
 fail:
+	if (remote_flow)
+		rte_free(remote_flow);
 	if (flow)
 		rte_free(flow);
 	return NULL;
 }
 
 /**
- * Destroy a flow.
+ * Destroy a flow using pointer to pmd_internal.
  *
- * @see rte_flow_destroy()
- * @see rte_flow_ops
+ * @param[in, out] pmd
+ *   Pointer to private structure.
+ * @param[in] flow
+ *   Pointer to the flow to destroy.
+ * @param[in, out] error
+ *   Pointer to the flow error handler
+ *
+ * @return 0 if the flow could be destroyed, -1 otherwise.
  */
 static int
-tap_flow_destroy(struct rte_eth_dev *dev,
-		 struct rte_flow *flow,
-		 struct rte_flow_error *error)
+tap_flow_destroy_pmd(struct pmd_internals *pmd,
+		     struct rte_flow *flow,
+		     struct rte_flow_error *error)
 {
-	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = flow->remote_flow;
 	int ret = 0;
 
 	LIST_REMOVE(flow, next);
@@ -1009,16 +1261,55 @@ tap_flow_destroy(struct rte_eth_dev *dev,
 		goto end;
 	}
 	ret = nl_recv_ack(pmd->nlsk_fd);
-	if (ret < 0)
+	if (ret < 0) {
 		rte_flow_error_set(
 			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
 			"couldn't receive kernel ack to our request");
+		goto end;
+	}
+	if (remote_flow) {
+		remote_flow->msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+		remote_flow->msg.nh.nlmsg_type = RTM_DELTFILTER;
+
+		ret = nl_send(pmd->nlsk_fd, &remote_flow->msg.nh);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto end;
+		}
+		ret = nl_recv_ack(pmd->nlsk_fd);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure trying to receive nl ack");
+			goto end;
+		}
+	}
 end:
+	if (remote_flow)
+		rte_free(remote_flow);
 	rte_free(flow);
 	return ret;
 }
 
 /**
+ * Destroy a flow.
+ *
+ * @see rte_flow_destroy()
+ * @see rte_flow_ops
+ */
+static int
+tap_flow_destroy(struct rte_eth_dev *dev,
+		 struct rte_flow *flow,
+		 struct rte_flow_error *error)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+
+	return tap_flow_destroy_pmd(pmd, flow, error);
+}
+
+/**
  * Destroy all flows.
  *
  * @see rte_flow_flush()
@@ -1039,6 +1330,128 @@ tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 }
 
 /**
+ * Add an implicit flow rule on the remote device to make sure traffic gets to
+ * the tap netdevice from there.
+ *
+ * @param pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to apply.
+ *
+ * @return -1 if the rule couldn't be applied, 0 otherwise.
+ */
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx)
+{
+	struct rte_flow_item *items = implicit_rte_flows[idx].items;
+	struct rte_flow_attr *attr = &implicit_rte_flows[idx].attr;
+	struct rte_flow_item_eth eth_local = { .type = 0 };
+	uint16_t if_index = pmd->remote_if_index;
+	struct rte_flow *remote_flow = NULL;
+	struct nlmsg *msg = NULL;
+	int err = 0;
+	struct rte_flow_item items_local[2] = {
+		[0] = {
+			.type = items[0].type,
+			.spec = &eth_local,
+			.mask = items[0].mask,
+		},
+		[1] = {
+			.type = items[1].type,
+		}
+	};
+
+	remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+	if (!remote_flow) {
+		RTE_LOG(ERR, PMD, "Cannot allocate memory for rte_flow");
+		goto fail;
+	}
+	msg = &remote_flow->msg;
+	if (idx == TAP_REMOTE_TX) {
+		if_index = pmd->if_index;
+	} else if (idx == TAP_REMOTE_LOCAL_MAC) {
+		/*
+		 * eth addr couldn't be set in implicit_rte_flows[] as it is not
+		 * known at compile time.
+		 */
+		memcpy(&eth_local.dst, &pmd->eth_addr, sizeof(pmd->eth_addr));
+		items = items_local;
+	}
+	tc_init_msg(msg, if_index, RTM_NEWTFILTER,
+		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+	tap_flow_set_handle(remote_flow);
+	if (priv_flow_process(pmd, attr, items, NULL, NULL,
+			      remote_flow, implicit_rte_flows[idx].mirred)) {
+		RTE_LOG(ERR, PMD, "rte flow rule validation failed\n");
+		goto fail;
+	}
+	err = nl_send(pmd->nlsk_fd, &msg->nh);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "Failure sending nl request");
+		goto fail;
+	}
+	err = nl_recv_ack(pmd->nlsk_fd);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD,
+			"Kernel refused TC filter rule creation");
+		goto fail;
+	}
+	LIST_INSERT_HEAD(&pmd->implicit_flows, remote_flow, next);
+	return 0;
+fail:
+	if (remote_flow)
+		rte_free(remote_flow);
+	return -1;
+}
+
+/**
+ * Remove specific implicit flow rule on the remote device.
+ *
+ * @param[in, out] pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to remove.
+ *
+ * @return -1 if one of the implicit rules couldn't be created, 0 otherwise.
+ */
+int tap_flow_implicit_destroy(struct pmd_internals *pmd,
+			      enum implicit_rule_index idx)
+{
+	struct rte_flow *remote_flow;
+	int cur_prio = -1;
+	int idx_prio = implicit_rte_flows[idx].attr.priority + PRIORITY_OFFSET;
+
+	for (remote_flow = LIST_FIRST(&pmd->implicit_flows);
+	     remote_flow;
+	     remote_flow = LIST_NEXT(remote_flow, next)) {
+		cur_prio = (remote_flow->msg.t.tcm_info >> 16) & PRIORITY_MASK;
+		if (cur_prio != idx_prio)
+			continue;
+		return tap_flow_destroy_pmd(pmd, remote_flow, NULL);
+	}
+	return 0;
+}
+
+/**
+ * Destroy all implicit flows.
+ *
+ * @see rte_flow_flush()
+ */
+int
+tap_flow_implicit_flush(struct pmd_internals *pmd, struct rte_flow_error *error)
+{
+	struct rte_flow *remote_flow;
+
+	while (!LIST_EMPTY(&pmd->implicit_flows)) {
+		remote_flow = LIST_FIRST(&pmd->implicit_flows);
+		if (tap_flow_destroy_pmd(pmd, remote_flow, error) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+/**
  * Manage filter operations.
  *
  * @param dev
diff --git a/drivers/net/tap/tap_flow.h b/drivers/net/tap/tap_flow.h
index a05e945df523..94414f18f4ff 100644
--- a/drivers/net/tap/tap_flow.h
+++ b/drivers/net/tap/tap_flow.h
@@ -36,6 +36,7 @@
 
 #include <rte_flow.h>
 #include <rte_flow_driver.h>
+#include <rte_eth_tap.h>
 
 /**
  * In TC, priority 0 means we require the kernel to allocate one for us.
@@ -49,10 +50,33 @@
 #define GROUP_SHIFT 12
 #define MAX_GROUP GROUP_MASK
 
+/**
+ * These index are actually in reversed order: their priority is processed
+ * by subtracting their value to the lowest priority (PRIORITY_MASK).
+ * Thus the first one will have the lowest priority in the end
+ * (but biggest value).
+ */
+enum implicit_rule_index {
+	TAP_REMOTE_TX,
+	TAP_REMOTE_BROADCASTV6,
+	TAP_REMOTE_BROADCAST,
+	TAP_REMOTE_ALLMULTI,
+	TAP_REMOTE_PROMISC,
+	TAP_REMOTE_LOCAL_MAC,
+	TAP_REMOTE_MAX_IDX,
+};
+
 int tap_dev_filter_ctrl(struct rte_eth_dev *dev,
 			enum rte_filter_type filter_type,
 			enum rte_filter_op filter_op,
 			void *arg);
 int tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error);
 
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx);
+int tap_flow_implicit_destroy(struct pmd_internals *pmd,
+			      enum implicit_rule_index idx);
+int tap_flow_implicit_flush(struct pmd_internals *pmd,
+			    struct rte_flow_error *error);
+
 #endif /* _TAP_FLOW_H_ */
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v4] net/tap: add remote netdevice traffic capture
  2017-03-14  8:34       ` [PATCH v4] net/tap: add " Pascal Mazon
@ 2017-03-14 14:00         ` Wiles, Keith
  0 siblings, 0 replies; 24+ messages in thread
From: Wiles, Keith @ 2017-03-14 14:00 UTC (permalink / raw)
  To: Pascal Mazon; +Cc: dev


> On Mar 14, 2017, at 4:34 PM, Pascal Mazon <pascal.mazon@6wind.com> wrote:
> 
> By default, a tap netdevice is of no use when not fed by a separate
> process. The ability to automatically feed it from another netdevice
> allows applications to capture any kind of traffic normally destined to
> the kernel stack.
> 
> This patch implements this ability through a new optional "remote"
> parameter.
> 
> Packets matching filtering rules created with the flow API are matched
> on the remote device and redirected to the tap PMD, where the relevant
> action will be performed.
> 
> Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
> Acked-by: Olga Shern <olgas@mellanox.com>

Acked-by: Keith Wiles <keith.wiles@intel.com>

> ---
> doc/guides/nics/tap.rst       |  17 ++
> drivers/net/tap/rte_eth_tap.c | 101 +++++++++-
> drivers/net/tap/rte_eth_tap.h |   4 +
> drivers/net/tap/tap_flow.c    | 451 ++++++++++++++++++++++++++++++++++++++++--
> drivers/net/tap/tap_flow.h    |  24 +++
> 5 files changed, 575 insertions(+), 22 deletions(-)

Regards,
Keith

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v5] net/tap: remote netdevice traffic capture
  2017-03-08 10:06   ` [PATCH v3 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
                       ` (4 preceding siblings ...)
  2017-03-14  8:34     ` [PATCH v4] net/tap: remote netdevice traffic capture Pascal Mazon
@ 2017-03-15 15:03     ` Pascal Mazon
  2017-03-15 15:03       ` [PATCH v5] net/tap: add " Pascal Mazon
  2017-03-23  8:42       ` [PATCH v6 0/1] net/tap: " Pascal Mazon
  5 siblings, 2 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-15 15:03 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

This patchset adds the special "remote" feature to the tap PMD, that
actually enables capturing traffic from another netdevice. This is
especially useful to get packets into the DPDK app, when the remote
netdevice has no DPDK support.

The "remote" feature requires flow API support as flow rules will be
configured on the remote netdevice for redirection, using the same
mechanism.

This series applies on top of:

  [PATCH 0/4] net/tap: support flow API

v2 changes:
  - rebase on top of updated "net/tap: support flow API"
  - fix implicit flow flush when closing the netdevices

v3 changes:
  - memset(0) for remote_iface in rte_pmd_tap_probe()
  - use snprintf instead of strncpy to correctly handle terminating \0

v4 changes:
  - rebase on top of updated "net/tap: support flow API"
  - use only a single patch now as MTU, MAC and flags can be easily managed
    with tap_ioctl()

v5 changes:
  - rebase after adrien's patches on Tx poll and Rx signaling

Pascal Mazon (1):
  net/tap: add remote netdevice traffic capture

 doc/guides/nics/tap.rst       |  17 ++
 drivers/net/tap/rte_eth_tap.c | 101 +++++++++-
 drivers/net/tap/rte_eth_tap.h |   4 +
 drivers/net/tap/tap_flow.c    | 451 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 575 insertions(+), 22 deletions(-)

-- 
2.8.0.rc0

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v5] net/tap: add remote netdevice traffic capture
  2017-03-15 15:03     ` [PATCH v5] net/tap: " Pascal Mazon
@ 2017-03-15 15:03       ` Pascal Mazon
  2017-03-23  8:42       ` [PATCH v6 0/1] net/tap: " Pascal Mazon
  1 sibling, 0 replies; 24+ messages in thread
From: Pascal Mazon @ 2017-03-15 15:03 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

By default, a tap netdevice is of no use when not fed by a separate
process. The ability to automatically feed it from another netdevice
allows applications to capture any kind of traffic normally destined to
the kernel stack.

This patch implements this ability through a new optional "remote"
parameter.

Packets matching filtering rules created with the flow API are matched
on the remote device and redirected to the tap PMD, where the relevant
action will be performed.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
Acked-by: Keith Wiles <keith.wiles@intel.com>
---
 doc/guides/nics/tap.rst       |  17 ++
 drivers/net/tap/rte_eth_tap.c | 101 +++++++++-
 drivers/net/tap/rte_eth_tap.h |   4 +
 drivers/net/tap/tap_flow.c    | 451 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 575 insertions(+), 22 deletions(-)

diff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst
index cdb528b5eae4..676a569b00ca 100644
--- a/doc/guides/nics/tap.rst
+++ b/doc/guides/nics/tap.rst
@@ -58,6 +58,23 @@ needed, but the interface does not enforce that speed, for example::
 
    --vdev=net_tap0,iface=foo0,speed=25000
 
+It is possible to specify a remote netdevice to capture packets from by adding
+``remote=foo1``, for example::
+
+   --vdev=net_tap,iface=tap0,remote=foo1
+
+If a ``remote`` is set, then all packets with the tap PMD's local MAC coming
+in on the remote netdevice will be redirected to the tap.
+If the tap is in promiscuous mode, then all packets will be redirected.
+In allmulti mode, all multicast packets will be redirected.
+It is possible to add explicit rte_flow rules on the tap PMD to capture specific
+traffic. For instance, in testpmd, the following rte_flow rule would capture
+packets with the given MAC address from the remote, and send it to the tap RX
+QUEUE 3::
+
+   testpmd> flow create 0 ingress pattern eth src is 02:03:04:05:06:07 / \
+            end actions queue index 3 / end
+
 After the DPDK application is started you can send and receive packets on the
 interface using the standard rx_burst/tx_burst APIs in DPDK. From the host
 point of view you can use any host tool like tcpdump, Wireshark, ping, Pktgen
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index c711b36c3222..69fa282ca176 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -67,6 +67,7 @@
 
 #define ETH_TAP_IFACE_ARG       "iface"
 #define ETH_TAP_SPEED_ARG       "speed"
+#define ETH_TAP_REMOTE_ARG      "remote"
 
 #ifdef IFF_MULTI_QUEUE
 #define RTE_PMD_TAP_MAX_QUEUES	16
@@ -82,6 +83,7 @@ static struct rte_vdev_driver pmd_tap_drv;
 static const char *valid_arguments[] = {
 	ETH_TAP_IFACE_ARG,
 	ETH_TAP_SPEED_ARG,
+	ETH_TAP_REMOTE_ARG,
 	NULL
 };
 
@@ -237,10 +239,43 @@ tun_alloc(struct pmd_internals *pmd, uint16_t qid)
 				pmd->name);
 			return fd;
 		}
+		if (pmd->remote_if_index) {
+			/*
+			 * Flush usually returns negative value because it tries
+			 * to delete every QDISC (and on a running device, one
+			 * QDISC at least is needed). Ignore negative return
+			 * value.
+			 */
+			qdisc_flush(pmd->nlsk_fd, pmd->remote_if_index);
+			if (qdisc_create_ingress(pmd->nlsk_fd,
+						 pmd->remote_if_index) < 0)
+				goto remote_fail;
+			LIST_INIT(&pmd->implicit_flows);
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_LOCAL_MAC) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCAST) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCASTV6) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_TX) < 0)
+				goto remote_fail;
+		}
 	}
 
 	return fd;
 
+remote_fail:
+	RTE_LOG(ERR, PMD,
+		"Could not set up remote flow rules for %s: remote disabled.\n",
+		pmd->name);
+	pmd->remote_if_index = 0;
+	tap_flow_implicit_flush(pmd, NULL);
+	return fd;
+
 error:
 	if (fd > 0)
 		close(fd);
@@ -336,8 +371,17 @@ tap_ioctl(struct pmd_internals *pmd, unsigned long request,
 	  struct ifreq *ifr, int set)
 {
 	short req_flags = ifr->ifr_flags;
+	int remote = !!pmd->remote_if_index;
 
-	snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->name);
+	/*
+	 * If there is a remote netdevice, apply ioctl on it, then apply it on
+	 * the tap netdevice.
+	 */
+apply:
+	if (remote)
+		snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->remote_iface);
+	else
+		snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->name);
 	switch (request) {
 	case SIOCSIFFLAGS:
 		/* fetch current flags to leave other flags untouched */
@@ -349,6 +393,12 @@ tap_ioctl(struct pmd_internals *pmd, unsigned long request,
 			ifr->ifr_flags &= ~req_flags;
 		break;
 	case SIOCGIFHWADDR:
+		/* Set remote MAC on the tap netdevice */
+		if (!remote && pmd->remote_if_index) {
+			request = SIOCSIFHWADDR;
+			goto apply;
+		}
+		break;
 	case SIOCSIFHWADDR:
 	case SIOCSIFMTU:
 		break;
@@ -359,6 +409,8 @@ tap_ioctl(struct pmd_internals *pmd, unsigned long request,
 	}
 	if (ioctl(pmd->ioctl_sock, request, ifr) < 0)
 		goto error;
+	if (remote--)
+		goto apply;
 	return 0;
 
 error:
@@ -512,6 +564,7 @@ tap_dev_close(struct rte_eth_dev *dev __rte_unused)
 
 	tap_link_set_down(dev);
 	tap_flow_flush(dev, NULL);
+	tap_flow_implicit_flush(internals, NULL);
 
 	for (i = 0; i < internals->nb_queues; i++) {
 		if (internals->rxq[i].fd != -1)
@@ -558,6 +611,8 @@ tap_promisc_enable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 1;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -568,6 +623,8 @@ tap_promisc_disable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 0;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(pmd, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -578,6 +635,8 @@ tap_allmulti_enable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 1;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);
 }
 
 static void
@@ -588,6 +647,8 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 0;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(pmd, TAP_REMOTE_ALLMULTI);
 }
 
 
@@ -858,7 +919,7 @@ tap_kernel_support(struct pmd_internals *pmd)
 }
 
 static int
-eth_dev_tap_create(const char *name, char *tap_name)
+eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 {
 	int numa_node = rte_socket_id();
 	struct rte_eth_dev *dev = NULL;
@@ -935,6 +996,15 @@ eth_dev_tap_create(const char *name, char *tap_name)
 	 * creating/destroying flow rules.
 	 */
 	pmd->nlsk_fd = nl_init();
+	if (strlen(remote_iface)) {
+		pmd->remote_if_index = if_nametoindex(remote_iface);
+		snprintf(pmd->remote_iface, RTE_ETH_NAME_MAX_LEN,
+			 "%s", remote_iface);
+		if (!pmd->remote_if_index)
+			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
+				"remote interface will remain unconfigured\n",
+				remote_iface);
+	}
 
 	return 0;
 
@@ -975,6 +1045,19 @@ set_interface_speed(const char *key __rte_unused,
 	return 0;
 }
 
+static int
+set_remote_iface(const char *key __rte_unused,
+		 const char *value,
+		 void *extra_args)
+{
+	char *name = (char *)extra_args;
+
+	if (value)
+		snprintf(name, RTE_ETH_NAME_MAX_LEN, "%s", value);
+
+	return 0;
+}
+
 /* Open a TAP interface device.
  */
 static int
@@ -984,10 +1067,12 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	struct rte_kvargs *kvlist = NULL;
 	int speed;
 	char tap_name[RTE_ETH_NAME_MAX_LEN];
+	char remote_iface[RTE_ETH_NAME_MAX_LEN];
 
 	speed = ETH_SPEED_NUM_10G;
 	snprintf(tap_name, sizeof(tap_name), "%s%d",
 		 DEFAULT_TAP_NAME, tap_unit++);
+	memset(remote_iface, 0, RTE_ETH_NAME_MAX_LEN);
 
 	if (params && (params[0] != '\0')) {
 		RTE_LOG(DEBUG, PMD, "paramaters (%s)\n", params);
@@ -1011,6 +1096,15 @@ rte_pmd_tap_probe(const char *name, const char *params)
 				if (ret == -1)
 					goto leave;
 			}
+
+			if (rte_kvargs_count(kvlist, ETH_TAP_REMOTE_ARG) == 1) {
+				ret = rte_kvargs_process(kvlist,
+							 ETH_TAP_REMOTE_ARG,
+							 &set_remote_iface,
+							 remote_iface);
+				if (ret == -1)
+					goto leave;
+			}
 		}
 	}
 	pmd_link.link_speed = speed;
@@ -1018,7 +1112,7 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	RTE_LOG(NOTICE, PMD, "Initializing pmd_tap for %s as %s\n",
 		name, tap_name);
 
-	ret = eth_dev_tap_create(name, tap_name);
+	ret = eth_dev_tap_create(name, tap_name, remote_iface);
 
 leave:
 	if (ret == -1) {
@@ -1051,6 +1145,7 @@ rte_pmd_tap_remove(const char *name)
 	internals = eth_dev->data->dev_private;
 	if (internals->flower_support && internals->nlsk_fd) {
 		tap_flow_flush(eth_dev, NULL);
+		tap_flow_implicit_flush(internals, NULL);
 		nl_final(internals->nlsk_fd);
 	}
 	for (i = 0; i < internals->nb_queues; i++)
diff --git a/drivers/net/tap/rte_eth_tap.h b/drivers/net/tap/rte_eth_tap.h
index 741ec5350886..1ad6ad88796a 100644
--- a/drivers/net/tap/rte_eth_tap.h
+++ b/drivers/net/tap/rte_eth_tap.h
@@ -64,15 +64,19 @@ struct tx_queue {
 };
 
 struct pmd_internals {
+	char remote_iface[RTE_ETH_NAME_MAX_LEN]; /* Remote netdevice name */
 	char name[RTE_ETH_NAME_MAX_LEN];  /* Internal Tap device name */
 	uint16_t nb_queues;               /* Number of queues supported */
 	struct ether_addr eth_addr;       /* Mac address of the device port */
+	int remote_if_index;              /* remote netdevice IF_INDEX */
 	int if_index;                     /* IF_INDEX for the port */
 	int ioctl_sock;                   /* socket for ioctl calls */
 	int nlsk_fd;                      /* Netlink socket fd */
 	int flower_support;               /* 1 if kernel supports, else 0 */
 	int flower_vlan_support;          /* 1 if kernel supports, else 0 */
 	LIST_HEAD(tap_flows, rte_flow) flows;        /* rte_flow rules */
+	/* implicit rte_flow rules set when a remote device is active */
+	LIST_HEAD(tap_implicit_flows, rte_flow) implicit_flows;
 	struct rx_queue rxq[RTE_PMD_TAP_MAX_QUEUES]; /* List of RX queues */
 	struct tx_queue txq[RTE_PMD_TAP_MAX_QUEUES]; /* List of TX queues */
 };
diff --git a/drivers/net/tap/tap_flow.c b/drivers/net/tap/tap_flow.c
index b119c31b0dea..7c9ce542a7c2 100644
--- a/drivers/net/tap/tap_flow.c
+++ b/drivers/net/tap/tap_flow.c
@@ -82,6 +82,7 @@ enum {
 
 struct rte_flow {
 	LIST_ENTRY(rte_flow) next; /* Pointer to the next rte_flow structure */
+	struct rte_flow *remote_flow; /* associated remote flow */
 	struct nlmsg msg;
 };
 
@@ -92,6 +93,12 @@ struct convert_data {
 	struct rte_flow *flow;
 };
 
+struct remote_rule {
+	struct rte_flow_attr attr;
+	struct rte_flow_item items[2];
+	int mirred;
+};
+
 static int tap_flow_create_eth(const struct rte_flow_item *item, void *data);
 static int tap_flow_create_vlan(const struct rte_flow_item *item, void *data);
 static int tap_flow_create_ipv4(const struct rte_flow_item *item, void *data);
@@ -249,6 +256,114 @@ static const struct tap_flow_items tap_flow_items[] = {
 	},
 };
 
+static struct remote_rule implicit_rte_flows[TAP_REMOTE_MAX_IDX] = {
+	[TAP_REMOTE_LOCAL_MAC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_LOCAL_MAC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_REDIR,
+	},
+	[TAP_REMOTE_BROADCAST] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCAST,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_BROADCASTV6] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCASTV6,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_PROMISC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_PROMISC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_ALLMULTI] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_ALLMULTI,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_TX] = {
+		.attr = {
+			.group = 0,
+			.priority = TAP_REMOTE_TX,
+			.egress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+};
+
 /**
  * Make as much checks as possible on an Ethernet item, and if a flow is
  * provided, fill it appropriately with Ethernet info.
@@ -673,6 +788,47 @@ add_action_gact(struct rte_flow *flow, int action)
 }
 
 /**
+ * Transform a MIRRED action item in the provided flow for TC.
+ *
+ * @param[in, out] flow
+ *   Flow to be filled.
+ * @param[in] ifindex
+ *   Netdevice ifindex, where to mirror/redirect packet to.
+ * @param[in] action_type
+ *   Either TCA_EGRESS_REDIR for redirection or TCA_EGRESS_MIRROR for mirroring.
+ *
+ * @return
+ *   0 if checks are alright, -1 otherwise.
+ */
+static int
+add_action_mirred(struct rte_flow *flow, uint16_t ifindex, uint16_t action_type)
+{
+	struct nlmsg *msg = &flow->msg;
+	size_t act_index = 1;
+	struct tc_mirred p = {
+		.eaction = action_type,
+		.ifindex = ifindex,
+	};
+
+	if (nlattr_nested_start(msg, TCA_FLOWER_ACT) < 0)
+		return -1;
+	if (nlattr_nested_start(msg, act_index++) < 0)
+		return -1;
+	nlattr_add(&msg->nh, TCA_ACT_KIND, sizeof("mirred"), "mirred");
+	if (nlattr_nested_start(msg, TCA_ACT_OPTIONS) < 0)
+		return -1;
+	if (action_type == TCA_EGRESS_MIRROR)
+		p.action = TC_ACT_PIPE;
+	else /* REDIRECT */
+		p.action = TC_ACT_STOLEN;
+	nlattr_add(&msg->nh, TCA_MIRRED_PARMS, sizeof(p), &p);
+	nlattr_nested_finish(msg); /* nested TCA_ACT_OPTIONS */
+	nlattr_nested_finish(msg); /* nested act_index */
+	nlattr_nested_finish(msg); /* nested TCA_FLOWER_ACT */
+	return 0;
+}
+
+/**
  * Transform a QUEUE action item in the provided flow for TC.
  *
  * @param[in, out] flow
@@ -723,6 +879,15 @@ add_action_skbedit(struct rte_flow *flow, uint16_t queue)
  *   Perform verbose error reporting if not NULL.
  * @param[in, out] flow
  *   Flow structure to update.
+ * @param[in] mirred
+ *   If set to TCA_EGRESS_REDIR, provided actions will be replaced with a
+ *   redirection to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd.
+ *   If set to TCA_EGRESS_MIRROR, provided actions will be replaced with a
+ *   mirroring to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd. Matching packets will thus be duplicated.
+ *   If set to 0, the standard behavior is to be used: set correct actions for
+ *   the TC rule, and apply it on the tap netdevice.
  *
  * @return
  *   0 on success, a negative errno value otherwise and rte_errno is set.
@@ -733,7 +898,8 @@ priv_flow_process(struct pmd_internals *pmd,
 		  const struct rte_flow_item items[],
 		  const struct rte_flow_action actions[],
 		  struct rte_flow_error *error,
-		  struct rte_flow *flow)
+		  struct rte_flow *flow,
+		  int mirred)
 {
 	const struct tap_flow_items *cur_item = tap_flow_items;
 	struct convert_data data = {
@@ -760,15 +926,21 @@ priv_flow_process(struct pmd_internals *pmd,
 		flow->msg.t.tcm_info = TC_H_MAKE(prio << 16,
 						 flow->msg.t.tcm_info);
 	}
-	if (!attr->ingress) {
-		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR,
-				   NULL, "direction should be ingress");
-		return -rte_errno;
-	}
-	/* rte_flow ingress is actually egress as seen in the kernel */
-	if (attr->ingress && flow)
-		flow->msg.t.tcm_parent = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
 	if (flow) {
+		if (mirred) {
+			/*
+			 * If attr->ingress, the rule applies on remote ingress
+			 * to match incoming packets
+			 * If attr->egress, the rule applies on tap ingress (as
+			 * seen from the kernel) to deal with packets going out
+			 * from the DPDK app.
+			 */
+			flow->msg.t.tcm_parent = TC_H_MAKE(TC_H_INGRESS, 0);
+		} else {
+			/* Standard rule on tap egress (kernel standpoint). */
+			flow->msg.t.tcm_parent =
+				TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
+		}
 		/* use flower filter type */
 		nlattr_add(&flow->msg.nh, TCA_KIND, sizeof("flower"), "flower");
 		if (nlattr_nested_start(&flow->msg, TCA_OPTIONS) < 0)
@@ -821,6 +993,22 @@ priv_flow_process(struct pmd_internals *pmd,
 				     data.eth_type);
 		}
 	}
+	if (mirred && flow) {
+		uint16_t if_index = pmd->if_index;
+
+		/*
+		 * If attr->egress && mirred, then this is a special
+		 * case where the rule must be applied on the tap, to
+		 * redirect packets coming from the DPDK App, out
+		 * through the remote netdevice.
+		 */
+		if (attr->egress)
+			if_index = pmd->remote_if_index;
+		if (add_action_mirred(flow, if_index, mirred) < 0)
+			goto exit_action_not_supported;
+		else
+			goto end;
+	}
 	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {
 		int err = 0;
 
@@ -855,6 +1043,7 @@ priv_flow_process(struct pmd_internals *pmd,
 		if (err)
 			goto exit_action_not_supported;
 	}
+end:
 	if (flow)
 		nlattr_nested_finish(&flow->msg); /* nested TCA_OPTIONS */
 	return 0;
@@ -885,7 +1074,7 @@ tap_flow_validate(struct rte_eth_dev *dev,
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
 
-	return priv_flow_process(pmd, attr, items, actions, error, NULL);
+	return priv_flow_process(pmd, attr, items, actions, error, NULL, 0);
 }
 
 /**
@@ -941,6 +1130,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		struct rte_flow_error *error)
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = NULL;
 	struct rte_flow *flow = NULL;
 	struct nlmsg *msg = NULL;
 	int err;
@@ -951,6 +1141,17 @@ tap_flow_create(struct rte_eth_dev *dev,
 				   "can't create rule, ifindex not found");
 		goto fail;
 	}
+	/*
+	 * No rules configured through standard rte_flow should be set on the
+	 * priorities used by implicit rules.
+	 */
+	if ((attr->group == MAX_GROUP) &&
+	    attr->priority > (MAX_PRIORITY - TAP_REMOTE_MAX_IDX)) {
+		rte_flow_error_set(
+			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+			NULL, "priority value too big");
+		goto fail;
+	}
 	flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
 	if (!flow) {
 		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
@@ -962,7 +1163,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
 	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
 	tap_flow_set_handle(flow);
-	if (priv_flow_process(pmd, attr, items, actions, error, flow))
+	if (priv_flow_process(pmd, attr, items, actions, error, flow, 0))
 		goto fail;
 	err = nl_send(pmd->nlsk_fd, &msg->nh);
 	if (err < 0) {
@@ -977,25 +1178,76 @@ tap_flow_create(struct rte_eth_dev *dev,
 		goto fail;
 	}
 	LIST_INSERT_HEAD(&pmd->flows, flow, next);
+	/**
+	 * If a remote device is configured, a TC rule with identical items for
+	 * matching must be set on that device, with a single action: redirect
+	 * to the local pmd->if_index.
+	 */
+	if (pmd->remote_if_index) {
+		remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+		if (!remote_flow) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"cannot allocate memory for rte_flow");
+			goto fail;
+		}
+		msg = &remote_flow->msg;
+		/* set the rule if_index for the remote netdevice */
+		tc_init_msg(
+			msg, pmd->remote_if_index, RTM_NEWTFILTER,
+			NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+		msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+		tap_flow_set_handle(remote_flow);
+		if (priv_flow_process(pmd, attr, items, NULL,
+				      error, remote_flow, TCA_EGRESS_REDIR)) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "rte flow rule validation failed");
+			goto fail;
+		}
+		err = nl_send(pmd->nlsk_fd, &msg->nh);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto fail;
+		}
+		err = nl_recv_ack(pmd->nlsk_fd);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "overlapping rules");
+			goto fail;
+		}
+		flow->remote_flow = remote_flow;
+	}
 	return flow;
 fail:
+	if (remote_flow)
+		rte_free(remote_flow);
 	if (flow)
 		rte_free(flow);
 	return NULL;
 }
 
 /**
- * Destroy a flow.
+ * Destroy a flow using pointer to pmd_internal.
  *
- * @see rte_flow_destroy()
- * @see rte_flow_ops
+ * @param[in, out] pmd
+ *   Pointer to private structure.
+ * @param[in] flow
+ *   Pointer to the flow to destroy.
+ * @param[in, out] error
+ *   Pointer to the flow error handler
+ *
+ * @return 0 if the flow could be destroyed, -1 otherwise.
  */
 static int
-tap_flow_destroy(struct rte_eth_dev *dev,
-		 struct rte_flow *flow,
-		 struct rte_flow_error *error)
+tap_flow_destroy_pmd(struct pmd_internals *pmd,
+		     struct rte_flow *flow,
+		     struct rte_flow_error *error)
 {
-	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = flow->remote_flow;
 	int ret = 0;
 
 	LIST_REMOVE(flow, next);
@@ -1009,16 +1261,55 @@ tap_flow_destroy(struct rte_eth_dev *dev,
 		goto end;
 	}
 	ret = nl_recv_ack(pmd->nlsk_fd);
-	if (ret < 0)
+	if (ret < 0) {
 		rte_flow_error_set(
 			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
 			"couldn't receive kernel ack to our request");
+		goto end;
+	}
+	if (remote_flow) {
+		remote_flow->msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+		remote_flow->msg.nh.nlmsg_type = RTM_DELTFILTER;
+
+		ret = nl_send(pmd->nlsk_fd, &remote_flow->msg.nh);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto end;
+		}
+		ret = nl_recv_ack(pmd->nlsk_fd);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure trying to receive nl ack");
+			goto end;
+		}
+	}
 end:
+	if (remote_flow)
+		rte_free(remote_flow);
 	rte_free(flow);
 	return ret;
 }
 
 /**
+ * Destroy a flow.
+ *
+ * @see rte_flow_destroy()
+ * @see rte_flow_ops
+ */
+static int
+tap_flow_destroy(struct rte_eth_dev *dev,
+		 struct rte_flow *flow,
+		 struct rte_flow_error *error)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+
+	return tap_flow_destroy_pmd(pmd, flow, error);
+}
+
+/**
  * Destroy all flows.
  *
  * @see rte_flow_flush()
@@ -1039,6 +1330,128 @@ tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 }
 
 /**
+ * Add an implicit flow rule on the remote device to make sure traffic gets to
+ * the tap netdevice from there.
+ *
+ * @param pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to apply.
+ *
+ * @return -1 if the rule couldn't be applied, 0 otherwise.
+ */
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx)
+{
+	struct rte_flow_item *items = implicit_rte_flows[idx].items;
+	struct rte_flow_attr *attr = &implicit_rte_flows[idx].attr;
+	struct rte_flow_item_eth eth_local = { .type = 0 };
+	uint16_t if_index = pmd->remote_if_index;
+	struct rte_flow *remote_flow = NULL;
+	struct nlmsg *msg = NULL;
+	int err = 0;
+	struct rte_flow_item items_local[2] = {
+		[0] = {
+			.type = items[0].type,
+			.spec = &eth_local,
+			.mask = items[0].mask,
+		},
+		[1] = {
+			.type = items[1].type,
+		}
+	};
+
+	remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+	if (!remote_flow) {
+		RTE_LOG(ERR, PMD, "Cannot allocate memory for rte_flow");
+		goto fail;
+	}
+	msg = &remote_flow->msg;
+	if (idx == TAP_REMOTE_TX) {
+		if_index = pmd->if_index;
+	} else if (idx == TAP_REMOTE_LOCAL_MAC) {
+		/*
+		 * eth addr couldn't be set in implicit_rte_flows[] as it is not
+		 * known at compile time.
+		 */
+		memcpy(&eth_local.dst, &pmd->eth_addr, sizeof(pmd->eth_addr));
+		items = items_local;
+	}
+	tc_init_msg(msg, if_index, RTM_NEWTFILTER,
+		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+	tap_flow_set_handle(remote_flow);
+	if (priv_flow_process(pmd, attr, items, NULL, NULL,
+			      remote_flow, implicit_rte_flows[idx].mirred)) {
+		RTE_LOG(ERR, PMD, "rte flow rule validation failed\n");
+		goto fail;
+	}
+	err = nl_send(pmd->nlsk_fd, &msg->nh);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "Failure sending nl request");
+		goto fail;
+	}
+	err = nl_recv_ack(pmd->nlsk_fd);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD,
+			"Kernel refused TC filter rule creation");
+		goto fail;
+	}
+	LIST_INSERT_HEAD(&pmd->implicit_flows, remote_flow, next);
+	return 0;
+fail:
+	if (remote_flow)
+		rte_free(remote_flow);
+	return -1;
+}
+
+/**
+ * Remove specific implicit flow rule on the remote device.
+ *
+ * @param[in, out] pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to remove.
+ *
+ * @return -1 if one of the implicit rules couldn't be created, 0 otherwise.
+ */
+int tap_flow_implicit_destroy(struct pmd_internals *pmd,
+			      enum implicit_rule_index idx)
+{
+	struct rte_flow *remote_flow;
+	int cur_prio = -1;
+	int idx_prio = implicit_rte_flows[idx].attr.priority + PRIORITY_OFFSET;
+
+	for (remote_flow = LIST_FIRST(&pmd->implicit_flows);
+	     remote_flow;
+	     remote_flow = LIST_NEXT(remote_flow, next)) {
+		cur_prio = (remote_flow->msg.t.tcm_info >> 16) & PRIORITY_MASK;
+		if (cur_prio != idx_prio)
+			continue;
+		return tap_flow_destroy_pmd(pmd, remote_flow, NULL);
+	}
+	return 0;
+}
+
+/**
+ * Destroy all implicit flows.
+ *
+ * @see rte_flow_flush()
+ */
+int
+tap_flow_implicit_flush(struct pmd_internals *pmd, struct rte_flow_error *error)
+{
+	struct rte_flow *remote_flow;
+
+	while (!LIST_EMPTY(&pmd->implicit_flows)) {
+		remote_flow = LIST_FIRST(&pmd->implicit_flows);
+		if (tap_flow_destroy_pmd(pmd, remote_flow, error) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+/**
  * Manage filter operations.
  *
  * @param dev
diff --git a/drivers/net/tap/tap_flow.h b/drivers/net/tap/tap_flow.h
index a05e945df523..94414f18f4ff 100644
--- a/drivers/net/tap/tap_flow.h
+++ b/drivers/net/tap/tap_flow.h
@@ -36,6 +36,7 @@
 
 #include <rte_flow.h>
 #include <rte_flow_driver.h>
+#include <rte_eth_tap.h>
 
 /**
  * In TC, priority 0 means we require the kernel to allocate one for us.
@@ -49,10 +50,33 @@
 #define GROUP_SHIFT 12
 #define MAX_GROUP GROUP_MASK
 
+/**
+ * These index are actually in reversed order: their priority is processed
+ * by subtracting their value to the lowest priority (PRIORITY_MASK).
+ * Thus the first one will have the lowest priority in the end
+ * (but biggest value).
+ */
+enum implicit_rule_index {
+	TAP_REMOTE_TX,
+	TAP_REMOTE_BROADCASTV6,
+	TAP_REMOTE_BROADCAST,
+	TAP_REMOTE_ALLMULTI,
+	TAP_REMOTE_PROMISC,
+	TAP_REMOTE_LOCAL_MAC,
+	TAP_REMOTE_MAX_IDX,
+};
+
 int tap_dev_filter_ctrl(struct rte_eth_dev *dev,
 			enum rte_filter_type filter_type,
 			enum rte_filter_op filter_op,
 			void *arg);
 int tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error);
 
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx);
+int tap_flow_implicit_destroy(struct pmd_internals *pmd,
+			      enum implicit_rule_index idx);
+int tap_flow_implicit_flush(struct pmd_internals *pmd,
+			    struct rte_flow_error *error);
+
 #endif /* _TAP_FLOW_H_ */
-- 
2.8.0.rc0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v6 0/1] net/tap: remote netdevice traffic capture
  2017-03-15 15:03     ` [PATCH v5] net/tap: " Pascal Mazon
  2017-03-15 15:03       ` [PATCH v5] net/tap: add " Pascal Mazon
@ 2017-03-23  8:42       ` Pascal Mazon
  2017-03-23  8:42         ` [PATCH v6 1/1] net/tap: add " Pascal Mazon
  1 sibling, 1 reply; 24+ messages in thread
From: Pascal Mazon @ 2017-03-23  8:42 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

This patchset adds the special "remote" feature to the tap PMD, that
actually enables capturing traffic from another netdevice. This is
especially useful to get packets into the DPDK app, when the remote
netdevice has no DPDK support.

The "remote" feature requires flow API support as flow rules will be
configured on the remote netdevice for redirection, using the same
mechanism.

This series applies on top of:

  [PATCH 0/4] net/tap: support flow API

v6 changes:
  - update documentation with use case

v5 changes:
  - rebase after adrien's patches on Tx poll and Rx signaling

v4 changes:
  - rebase on top of updated "net/tap: support flow API"
  - use only a single patch now as MTU, MAC and flags can be easily managed
    with tap_ioctl()

v3 changes:
  - memset(0) for remote_iface in rte_pmd_tap_probe()
  - use snprintf instead of strncpy to correctly handle terminating \0

v2 changes:
  - rebase on top of updated "net/tap: support flow API"
  - fix implicit flow flush when closing the netdevices

Pascal Mazon (1):
  net/tap: add remote netdevice traffic capture

 doc/guides/nics/tap.rst       |  16 ++
 drivers/net/tap/rte_eth_tap.c | 101 +++++++++-
 drivers/net/tap/rte_eth_tap.h |   4 +
 drivers/net/tap/tap_flow.c    | 451 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 574 insertions(+), 22 deletions(-)

-- 
2.12.0.306.g4a9b9b3

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v6 1/1] net/tap: add remote netdevice traffic capture
  2017-03-23  8:42       ` [PATCH v6 0/1] net/tap: " Pascal Mazon
@ 2017-03-23  8:42         ` Pascal Mazon
  2017-03-24 15:48           ` Ferruh Yigit
  0 siblings, 1 reply; 24+ messages in thread
From: Pascal Mazon @ 2017-03-23  8:42 UTC (permalink / raw)
  To: keith.wiles; +Cc: dev, Pascal Mazon

By default, a tap netdevice is of no use when not fed by a separate
process. The ability to automatically feed it from another netdevice
allows applications to capture any kind of traffic normally destined to
the kernel stack.

This patch implements this ability through a new optional "remote"
parameter.

Packets matching filtering rules created with the flow API are matched
on the remote device and redirected to the tap PMD, where the relevant
action will be performed.

Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
Acked-by: Keith Wiles <keith.wiles@intel.com>
---
 doc/guides/nics/tap.rst       |  16 ++
 drivers/net/tap/rte_eth_tap.c | 101 +++++++++-
 drivers/net/tap/rte_eth_tap.h |   4 +
 drivers/net/tap/tap_flow.c    | 451 ++++++++++++++++++++++++++++++++++++++++--
 drivers/net/tap/tap_flow.h    |  24 +++
 5 files changed, 574 insertions(+), 22 deletions(-)

diff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst
index 4986e47e9f57..5c5ba5357bba 100644
--- a/doc/guides/nics/tap.rst
+++ b/doc/guides/nics/tap.rst
@@ -58,6 +58,22 @@ needed, but the interface does not enforce that speed, for example::
 
    --vdev=net_tap0,iface=foo0,speed=25000
 
+It is possible to specify a remote netdevice to capture packets from by adding
+``remote=foo1``, for example::
+
+   --vdev=net_tap,iface=tap0,remote=foo1
+
+If a ``remote`` is set, the tap MAC address will be set to match the remote one
+just after netdevice creation. Using TC rules, traffic from the remote netdevice
+will be redirected to the tap. If the tap is in promiscuous mode, then all
+packets will be redirected. In allmulti mode, all multicast packets will be
+redirected.
+
+Using the remote feature is especially useful for capturing traffic from a
+netdevice that has no support in the DPDK. It is possible to add explicit
+rte_flow rules on the tap PMD to capture specific traffic (see next section for
+examples).
+
 After the DPDK application is started you can send and receive packets on the
 interface using the standard rx_burst/tx_burst APIs in DPDK. From the host
 point of view you can use any host tool like tcpdump, Wireshark, ping, Pktgen
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index c711b36c3222..69fa282ca176 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -67,6 +67,7 @@
 
 #define ETH_TAP_IFACE_ARG       "iface"
 #define ETH_TAP_SPEED_ARG       "speed"
+#define ETH_TAP_REMOTE_ARG      "remote"
 
 #ifdef IFF_MULTI_QUEUE
 #define RTE_PMD_TAP_MAX_QUEUES	16
@@ -82,6 +83,7 @@ static struct rte_vdev_driver pmd_tap_drv;
 static const char *valid_arguments[] = {
 	ETH_TAP_IFACE_ARG,
 	ETH_TAP_SPEED_ARG,
+	ETH_TAP_REMOTE_ARG,
 	NULL
 };
 
@@ -237,10 +239,43 @@ tun_alloc(struct pmd_internals *pmd, uint16_t qid)
 				pmd->name);
 			return fd;
 		}
+		if (pmd->remote_if_index) {
+			/*
+			 * Flush usually returns negative value because it tries
+			 * to delete every QDISC (and on a running device, one
+			 * QDISC at least is needed). Ignore negative return
+			 * value.
+			 */
+			qdisc_flush(pmd->nlsk_fd, pmd->remote_if_index);
+			if (qdisc_create_ingress(pmd->nlsk_fd,
+						 pmd->remote_if_index) < 0)
+				goto remote_fail;
+			LIST_INIT(&pmd->implicit_flows);
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_LOCAL_MAC) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCAST) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_BROADCASTV6) < 0)
+				goto remote_fail;
+			if (tap_flow_implicit_create(
+				    pmd, TAP_REMOTE_TX) < 0)
+				goto remote_fail;
+		}
 	}
 
 	return fd;
 
+remote_fail:
+	RTE_LOG(ERR, PMD,
+		"Could not set up remote flow rules for %s: remote disabled.\n",
+		pmd->name);
+	pmd->remote_if_index = 0;
+	tap_flow_implicit_flush(pmd, NULL);
+	return fd;
+
 error:
 	if (fd > 0)
 		close(fd);
@@ -336,8 +371,17 @@ tap_ioctl(struct pmd_internals *pmd, unsigned long request,
 	  struct ifreq *ifr, int set)
 {
 	short req_flags = ifr->ifr_flags;
+	int remote = !!pmd->remote_if_index;
 
-	snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->name);
+	/*
+	 * If there is a remote netdevice, apply ioctl on it, then apply it on
+	 * the tap netdevice.
+	 */
+apply:
+	if (remote)
+		snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->remote_iface);
+	else
+		snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->name);
 	switch (request) {
 	case SIOCSIFFLAGS:
 		/* fetch current flags to leave other flags untouched */
@@ -349,6 +393,12 @@ tap_ioctl(struct pmd_internals *pmd, unsigned long request,
 			ifr->ifr_flags &= ~req_flags;
 		break;
 	case SIOCGIFHWADDR:
+		/* Set remote MAC on the tap netdevice */
+		if (!remote && pmd->remote_if_index) {
+			request = SIOCSIFHWADDR;
+			goto apply;
+		}
+		break;
 	case SIOCSIFHWADDR:
 	case SIOCSIFMTU:
 		break;
@@ -359,6 +409,8 @@ tap_ioctl(struct pmd_internals *pmd, unsigned long request,
 	}
 	if (ioctl(pmd->ioctl_sock, request, ifr) < 0)
 		goto error;
+	if (remote--)
+		goto apply;
 	return 0;
 
 error:
@@ -512,6 +564,7 @@ tap_dev_close(struct rte_eth_dev *dev __rte_unused)
 
 	tap_link_set_down(dev);
 	tap_flow_flush(dev, NULL);
+	tap_flow_implicit_flush(internals, NULL);
 
 	for (i = 0; i < internals->nb_queues; i++) {
 		if (internals->rxq[i].fd != -1)
@@ -558,6 +611,8 @@ tap_promisc_enable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 1;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -568,6 +623,8 @@ tap_promisc_disable(struct rte_eth_dev *dev)
 
 	dev->data->promiscuous = 0;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(pmd, TAP_REMOTE_PROMISC);
 }
 
 static void
@@ -578,6 +635,8 @@ tap_allmulti_enable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 1;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 1);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);
 }
 
 static void
@@ -588,6 +647,8 @@ tap_allmulti_disable(struct rte_eth_dev *dev)
 
 	dev->data->all_multicast = 0;
 	tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 0);
+	if (pmd->remote_if_index)
+		tap_flow_implicit_destroy(pmd, TAP_REMOTE_ALLMULTI);
 }
 
 
@@ -858,7 +919,7 @@ tap_kernel_support(struct pmd_internals *pmd)
 }
 
 static int
-eth_dev_tap_create(const char *name, char *tap_name)
+eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)
 {
 	int numa_node = rte_socket_id();
 	struct rte_eth_dev *dev = NULL;
@@ -935,6 +996,15 @@ eth_dev_tap_create(const char *name, char *tap_name)
 	 * creating/destroying flow rules.
 	 */
 	pmd->nlsk_fd = nl_init();
+	if (strlen(remote_iface)) {
+		pmd->remote_if_index = if_nametoindex(remote_iface);
+		snprintf(pmd->remote_iface, RTE_ETH_NAME_MAX_LEN,
+			 "%s", remote_iface);
+		if (!pmd->remote_if_index)
+			RTE_LOG(ERR, PMD, "Could not find %s ifindex: "
+				"remote interface will remain unconfigured\n",
+				remote_iface);
+	}
 
 	return 0;
 
@@ -975,6 +1045,19 @@ set_interface_speed(const char *key __rte_unused,
 	return 0;
 }
 
+static int
+set_remote_iface(const char *key __rte_unused,
+		 const char *value,
+		 void *extra_args)
+{
+	char *name = (char *)extra_args;
+
+	if (value)
+		snprintf(name, RTE_ETH_NAME_MAX_LEN, "%s", value);
+
+	return 0;
+}
+
 /* Open a TAP interface device.
  */
 static int
@@ -984,10 +1067,12 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	struct rte_kvargs *kvlist = NULL;
 	int speed;
 	char tap_name[RTE_ETH_NAME_MAX_LEN];
+	char remote_iface[RTE_ETH_NAME_MAX_LEN];
 
 	speed = ETH_SPEED_NUM_10G;
 	snprintf(tap_name, sizeof(tap_name), "%s%d",
 		 DEFAULT_TAP_NAME, tap_unit++);
+	memset(remote_iface, 0, RTE_ETH_NAME_MAX_LEN);
 
 	if (params && (params[0] != '\0')) {
 		RTE_LOG(DEBUG, PMD, "paramaters (%s)\n", params);
@@ -1011,6 +1096,15 @@ rte_pmd_tap_probe(const char *name, const char *params)
 				if (ret == -1)
 					goto leave;
 			}
+
+			if (rte_kvargs_count(kvlist, ETH_TAP_REMOTE_ARG) == 1) {
+				ret = rte_kvargs_process(kvlist,
+							 ETH_TAP_REMOTE_ARG,
+							 &set_remote_iface,
+							 remote_iface);
+				if (ret == -1)
+					goto leave;
+			}
 		}
 	}
 	pmd_link.link_speed = speed;
@@ -1018,7 +1112,7 @@ rte_pmd_tap_probe(const char *name, const char *params)
 	RTE_LOG(NOTICE, PMD, "Initializing pmd_tap for %s as %s\n",
 		name, tap_name);
 
-	ret = eth_dev_tap_create(name, tap_name);
+	ret = eth_dev_tap_create(name, tap_name, remote_iface);
 
 leave:
 	if (ret == -1) {
@@ -1051,6 +1145,7 @@ rte_pmd_tap_remove(const char *name)
 	internals = eth_dev->data->dev_private;
 	if (internals->flower_support && internals->nlsk_fd) {
 		tap_flow_flush(eth_dev, NULL);
+		tap_flow_implicit_flush(internals, NULL);
 		nl_final(internals->nlsk_fd);
 	}
 	for (i = 0; i < internals->nb_queues; i++)
diff --git a/drivers/net/tap/rte_eth_tap.h b/drivers/net/tap/rte_eth_tap.h
index 741ec5350886..1ad6ad88796a 100644
--- a/drivers/net/tap/rte_eth_tap.h
+++ b/drivers/net/tap/rte_eth_tap.h
@@ -64,15 +64,19 @@ struct tx_queue {
 };
 
 struct pmd_internals {
+	char remote_iface[RTE_ETH_NAME_MAX_LEN]; /* Remote netdevice name */
 	char name[RTE_ETH_NAME_MAX_LEN];  /* Internal Tap device name */
 	uint16_t nb_queues;               /* Number of queues supported */
 	struct ether_addr eth_addr;       /* Mac address of the device port */
+	int remote_if_index;              /* remote netdevice IF_INDEX */
 	int if_index;                     /* IF_INDEX for the port */
 	int ioctl_sock;                   /* socket for ioctl calls */
 	int nlsk_fd;                      /* Netlink socket fd */
 	int flower_support;               /* 1 if kernel supports, else 0 */
 	int flower_vlan_support;          /* 1 if kernel supports, else 0 */
 	LIST_HEAD(tap_flows, rte_flow) flows;        /* rte_flow rules */
+	/* implicit rte_flow rules set when a remote device is active */
+	LIST_HEAD(tap_implicit_flows, rte_flow) implicit_flows;
 	struct rx_queue rxq[RTE_PMD_TAP_MAX_QUEUES]; /* List of RX queues */
 	struct tx_queue txq[RTE_PMD_TAP_MAX_QUEUES]; /* List of TX queues */
 };
diff --git a/drivers/net/tap/tap_flow.c b/drivers/net/tap/tap_flow.c
index 6adacdc22d4a..7f1693d40468 100644
--- a/drivers/net/tap/tap_flow.c
+++ b/drivers/net/tap/tap_flow.c
@@ -82,6 +82,7 @@ enum {
 
 struct rte_flow {
 	LIST_ENTRY(rte_flow) next; /* Pointer to the next rte_flow structure */
+	struct rte_flow *remote_flow; /* associated remote flow */
 	struct nlmsg msg;
 };
 
@@ -92,6 +93,12 @@ struct convert_data {
 	struct rte_flow *flow;
 };
 
+struct remote_rule {
+	struct rte_flow_attr attr;
+	struct rte_flow_item items[2];
+	int mirred;
+};
+
 static int tap_flow_create_eth(const struct rte_flow_item *item, void *data);
 static int tap_flow_create_vlan(const struct rte_flow_item *item, void *data);
 static int tap_flow_create_ipv4(const struct rte_flow_item *item, void *data);
@@ -249,6 +256,114 @@ static const struct tap_flow_items tap_flow_items[] = {
 	},
 };
 
+static struct remote_rule implicit_rte_flows[TAP_REMOTE_MAX_IDX] = {
+	[TAP_REMOTE_LOCAL_MAC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_LOCAL_MAC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_REDIR,
+	},
+	[TAP_REMOTE_BROADCAST] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCAST,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_BROADCASTV6] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_BROADCASTV6,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_PROMISC] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_PROMISC,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_ALLMULTI] = {
+		.attr = {
+			.group = MAX_GROUP,
+			.priority = PRIORITY_MASK - TAP_REMOTE_ALLMULTI,
+			.ingress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.mask =  &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+			.spec = &(const struct rte_flow_item_eth){
+				.dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+			},
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+	[TAP_REMOTE_TX] = {
+		.attr = {
+			.group = 0,
+			.priority = TAP_REMOTE_TX,
+			.egress = 1,
+		},
+		.items[0] = {
+			.type = RTE_FLOW_ITEM_TYPE_VOID,
+		},
+		.items[1] = {
+			.type = RTE_FLOW_ITEM_TYPE_END,
+		},
+		.mirred = TCA_EGRESS_MIRROR,
+	},
+};
+
 /**
  * Make as much checks as possible on an Ethernet item, and if a flow is
  * provided, fill it appropriately with Ethernet info.
@@ -673,6 +788,47 @@ add_action_gact(struct rte_flow *flow, int action)
 }
 
 /**
+ * Transform a MIRRED action item in the provided flow for TC.
+ *
+ * @param[in, out] flow
+ *   Flow to be filled.
+ * @param[in] ifindex
+ *   Netdevice ifindex, where to mirror/redirect packet to.
+ * @param[in] action_type
+ *   Either TCA_EGRESS_REDIR for redirection or TCA_EGRESS_MIRROR for mirroring.
+ *
+ * @return
+ *   0 if checks are alright, -1 otherwise.
+ */
+static int
+add_action_mirred(struct rte_flow *flow, uint16_t ifindex, uint16_t action_type)
+{
+	struct nlmsg *msg = &flow->msg;
+	size_t act_index = 1;
+	struct tc_mirred p = {
+		.eaction = action_type,
+		.ifindex = ifindex,
+	};
+
+	if (nlattr_nested_start(msg, TCA_FLOWER_ACT) < 0)
+		return -1;
+	if (nlattr_nested_start(msg, act_index++) < 0)
+		return -1;
+	nlattr_add(&msg->nh, TCA_ACT_KIND, sizeof("mirred"), "mirred");
+	if (nlattr_nested_start(msg, TCA_ACT_OPTIONS) < 0)
+		return -1;
+	if (action_type == TCA_EGRESS_MIRROR)
+		p.action = TC_ACT_PIPE;
+	else /* REDIRECT */
+		p.action = TC_ACT_STOLEN;
+	nlattr_add(&msg->nh, TCA_MIRRED_PARMS, sizeof(p), &p);
+	nlattr_nested_finish(msg); /* nested TCA_ACT_OPTIONS */
+	nlattr_nested_finish(msg); /* nested act_index */
+	nlattr_nested_finish(msg); /* nested TCA_FLOWER_ACT */
+	return 0;
+}
+
+/**
  * Transform a QUEUE action item in the provided flow for TC.
  *
  * @param[in, out] flow
@@ -723,6 +879,15 @@ add_action_skbedit(struct rte_flow *flow, uint16_t queue)
  *   Perform verbose error reporting if not NULL.
  * @param[in, out] flow
  *   Flow structure to update.
+ * @param[in] mirred
+ *   If set to TCA_EGRESS_REDIR, provided actions will be replaced with a
+ *   redirection to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd.
+ *   If set to TCA_EGRESS_MIRROR, provided actions will be replaced with a
+ *   mirroring to the tap netdevice, and the TC rule will be configured
+ *   on the remote netdevice in pmd. Matching packets will thus be duplicated.
+ *   If set to 0, the standard behavior is to be used: set correct actions for
+ *   the TC rule, and apply it on the tap netdevice.
  *
  * @return
  *   0 on success, a negative errno value otherwise and rte_errno is set.
@@ -733,7 +898,8 @@ priv_flow_process(struct pmd_internals *pmd,
 		  const struct rte_flow_item items[],
 		  const struct rte_flow_action actions[],
 		  struct rte_flow_error *error,
-		  struct rte_flow *flow)
+		  struct rte_flow *flow,
+		  int mirred)
 {
 	const struct tap_flow_items *cur_item = tap_flow_items;
 	struct convert_data data = {
@@ -760,15 +926,21 @@ priv_flow_process(struct pmd_internals *pmd,
 		flow->msg.t.tcm_info = TC_H_MAKE(prio << 16,
 						 flow->msg.t.tcm_info);
 	}
-	if (!attr->ingress) {
-		rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR,
-				   NULL, "direction should be ingress");
-		return -rte_errno;
-	}
-	/* rte_flow ingress is actually egress as seen in the kernel */
-	if (attr->ingress && flow)
-		flow->msg.t.tcm_parent = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
 	if (flow) {
+		if (mirred) {
+			/*
+			 * If attr->ingress, the rule applies on remote ingress
+			 * to match incoming packets
+			 * If attr->egress, the rule applies on tap ingress (as
+			 * seen from the kernel) to deal with packets going out
+			 * from the DPDK app.
+			 */
+			flow->msg.t.tcm_parent = TC_H_MAKE(TC_H_INGRESS, 0);
+		} else {
+			/* Standard rule on tap egress (kernel standpoint). */
+			flow->msg.t.tcm_parent =
+				TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
+		}
 		/* use flower filter type */
 		nlattr_add(&flow->msg.nh, TCA_KIND, sizeof("flower"), "flower");
 		if (nlattr_nested_start(&flow->msg, TCA_OPTIONS) < 0)
@@ -821,6 +993,22 @@ priv_flow_process(struct pmd_internals *pmd,
 				     data.eth_type);
 		}
 	}
+	if (mirred && flow) {
+		uint16_t if_index = pmd->if_index;
+
+		/*
+		 * If attr->egress && mirred, then this is a special
+		 * case where the rule must be applied on the tap, to
+		 * redirect packets coming from the DPDK App, out
+		 * through the remote netdevice.
+		 */
+		if (attr->egress)
+			if_index = pmd->remote_if_index;
+		if (add_action_mirred(flow, if_index, mirred) < 0)
+			goto exit_action_not_supported;
+		else
+			goto end;
+	}
 	for (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {
 		int err = 0;
 
@@ -855,6 +1043,7 @@ priv_flow_process(struct pmd_internals *pmd,
 		if (err)
 			goto exit_action_not_supported;
 	}
+end:
 	if (flow)
 		nlattr_nested_finish(&flow->msg); /* nested TCA_OPTIONS */
 	return 0;
@@ -885,7 +1074,7 @@ tap_flow_validate(struct rte_eth_dev *dev,
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
 
-	return priv_flow_process(pmd, attr, items, actions, error, NULL);
+	return priv_flow_process(pmd, attr, items, actions, error, NULL, 0);
 }
 
 /**
@@ -933,6 +1122,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		struct rte_flow_error *error)
 {
 	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = NULL;
 	struct rte_flow *flow = NULL;
 	struct nlmsg *msg = NULL;
 	int err;
@@ -943,6 +1133,17 @@ tap_flow_create(struct rte_eth_dev *dev,
 				   "can't create rule, ifindex not found");
 		goto fail;
 	}
+	/*
+	 * No rules configured through standard rte_flow should be set on the
+	 * priorities used by implicit rules.
+	 */
+	if ((attr->group == MAX_GROUP) &&
+	    attr->priority > (MAX_PRIORITY - TAP_REMOTE_MAX_IDX)) {
+		rte_flow_error_set(
+			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+			NULL, "priority value too big");
+		goto fail;
+	}
 	flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
 	if (!flow) {
 		rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
@@ -954,7 +1155,7 @@ tap_flow_create(struct rte_eth_dev *dev,
 		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
 	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
 	tap_flow_set_handle(flow);
-	if (priv_flow_process(pmd, attr, items, actions, error, flow))
+	if (priv_flow_process(pmd, attr, items, actions, error, flow, 0))
 		goto fail;
 	err = nl_send(pmd->nlsk_fd, &msg->nh);
 	if (err < 0) {
@@ -969,25 +1170,76 @@ tap_flow_create(struct rte_eth_dev *dev,
 		goto fail;
 	}
 	LIST_INSERT_HEAD(&pmd->flows, flow, next);
+	/**
+	 * If a remote device is configured, a TC rule with identical items for
+	 * matching must be set on that device, with a single action: redirect
+	 * to the local pmd->if_index.
+	 */
+	if (pmd->remote_if_index) {
+		remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+		if (!remote_flow) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+				"cannot allocate memory for rte_flow");
+			goto fail;
+		}
+		msg = &remote_flow->msg;
+		/* set the rule if_index for the remote netdevice */
+		tc_init_msg(
+			msg, pmd->remote_if_index, RTM_NEWTFILTER,
+			NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+		msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+		tap_flow_set_handle(remote_flow);
+		if (priv_flow_process(pmd, attr, items, NULL,
+				      error, remote_flow, TCA_EGRESS_REDIR)) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "rte flow rule validation failed");
+			goto fail;
+		}
+		err = nl_send(pmd->nlsk_fd, &msg->nh);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto fail;
+		}
+		err = nl_recv_ack(pmd->nlsk_fd);
+		if (err < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "overlapping rules");
+			goto fail;
+		}
+		flow->remote_flow = remote_flow;
+	}
 	return flow;
 fail:
+	if (remote_flow)
+		rte_free(remote_flow);
 	if (flow)
 		rte_free(flow);
 	return NULL;
 }
 
 /**
- * Destroy a flow.
+ * Destroy a flow using pointer to pmd_internal.
  *
- * @see rte_flow_destroy()
- * @see rte_flow_ops
+ * @param[in, out] pmd
+ *   Pointer to private structure.
+ * @param[in] flow
+ *   Pointer to the flow to destroy.
+ * @param[in, out] error
+ *   Pointer to the flow error handler
+ *
+ * @return 0 if the flow could be destroyed, -1 otherwise.
  */
 static int
-tap_flow_destroy(struct rte_eth_dev *dev,
-		 struct rte_flow *flow,
-		 struct rte_flow_error *error)
+tap_flow_destroy_pmd(struct pmd_internals *pmd,
+		     struct rte_flow *flow,
+		     struct rte_flow_error *error)
 {
-	struct pmd_internals *pmd = dev->data->dev_private;
+	struct rte_flow *remote_flow = flow->remote_flow;
 	int ret = 0;
 
 	LIST_REMOVE(flow, next);
@@ -1001,16 +1253,55 @@ tap_flow_destroy(struct rte_eth_dev *dev,
 		goto end;
 	}
 	ret = nl_recv_ack(pmd->nlsk_fd);
-	if (ret < 0)
+	if (ret < 0) {
 		rte_flow_error_set(
 			error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
 			"couldn't receive kernel ack to our request");
+		goto end;
+	}
+	if (remote_flow) {
+		remote_flow->msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+		remote_flow->msg.nh.nlmsg_type = RTM_DELTFILTER;
+
+		ret = nl_send(pmd->nlsk_fd, &remote_flow->msg.nh);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure sending nl request");
+			goto end;
+		}
+		ret = nl_recv_ack(pmd->nlsk_fd);
+		if (ret < 0) {
+			rte_flow_error_set(
+				error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failure trying to receive nl ack");
+			goto end;
+		}
+	}
 end:
+	if (remote_flow)
+		rte_free(remote_flow);
 	rte_free(flow);
 	return ret;
 }
 
 /**
+ * Destroy a flow.
+ *
+ * @see rte_flow_destroy()
+ * @see rte_flow_ops
+ */
+static int
+tap_flow_destroy(struct rte_eth_dev *dev,
+		 struct rte_flow *flow,
+		 struct rte_flow_error *error)
+{
+	struct pmd_internals *pmd = dev->data->dev_private;
+
+	return tap_flow_destroy_pmd(pmd, flow, error);
+}
+
+/**
  * Destroy all flows.
  *
  * @see rte_flow_flush()
@@ -1031,6 +1322,128 @@ tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 }
 
 /**
+ * Add an implicit flow rule on the remote device to make sure traffic gets to
+ * the tap netdevice from there.
+ *
+ * @param pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to apply.
+ *
+ * @return -1 if the rule couldn't be applied, 0 otherwise.
+ */
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx)
+{
+	struct rte_flow_item *items = implicit_rte_flows[idx].items;
+	struct rte_flow_attr *attr = &implicit_rte_flows[idx].attr;
+	struct rte_flow_item_eth eth_local = { .type = 0 };
+	uint16_t if_index = pmd->remote_if_index;
+	struct rte_flow *remote_flow = NULL;
+	struct nlmsg *msg = NULL;
+	int err = 0;
+	struct rte_flow_item items_local[2] = {
+		[0] = {
+			.type = items[0].type,
+			.spec = &eth_local,
+			.mask = items[0].mask,
+		},
+		[1] = {
+			.type = items[1].type,
+		}
+	};
+
+	remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+	if (!remote_flow) {
+		RTE_LOG(ERR, PMD, "Cannot allocate memory for rte_flow");
+		goto fail;
+	}
+	msg = &remote_flow->msg;
+	if (idx == TAP_REMOTE_TX) {
+		if_index = pmd->if_index;
+	} else if (idx == TAP_REMOTE_LOCAL_MAC) {
+		/*
+		 * eth addr couldn't be set in implicit_rte_flows[] as it is not
+		 * known at compile time.
+		 */
+		memcpy(&eth_local.dst, &pmd->eth_addr, sizeof(pmd->eth_addr));
+		items = items_local;
+	}
+	tc_init_msg(msg, if_index, RTM_NEWTFILTER,
+		    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+	msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+	tap_flow_set_handle(remote_flow);
+	if (priv_flow_process(pmd, attr, items, NULL, NULL,
+			      remote_flow, implicit_rte_flows[idx].mirred)) {
+		RTE_LOG(ERR, PMD, "rte flow rule validation failed\n");
+		goto fail;
+	}
+	err = nl_send(pmd->nlsk_fd, &msg->nh);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD, "Failure sending nl request");
+		goto fail;
+	}
+	err = nl_recv_ack(pmd->nlsk_fd);
+	if (err < 0) {
+		RTE_LOG(ERR, PMD,
+			"Kernel refused TC filter rule creation");
+		goto fail;
+	}
+	LIST_INSERT_HEAD(&pmd->implicit_flows, remote_flow, next);
+	return 0;
+fail:
+	if (remote_flow)
+		rte_free(remote_flow);
+	return -1;
+}
+
+/**
+ * Remove specific implicit flow rule on the remote device.
+ *
+ * @param[in, out] pmd
+ *   Pointer to private structure.
+ * @param[in] idx
+ *   The idx in the implicit_rte_flows array specifying which rule to remove.
+ *
+ * @return -1 if one of the implicit rules couldn't be created, 0 otherwise.
+ */
+int tap_flow_implicit_destroy(struct pmd_internals *pmd,
+			      enum implicit_rule_index idx)
+{
+	struct rte_flow *remote_flow;
+	int cur_prio = -1;
+	int idx_prio = implicit_rte_flows[idx].attr.priority + PRIORITY_OFFSET;
+
+	for (remote_flow = LIST_FIRST(&pmd->implicit_flows);
+	     remote_flow;
+	     remote_flow = LIST_NEXT(remote_flow, next)) {
+		cur_prio = (remote_flow->msg.t.tcm_info >> 16) & PRIORITY_MASK;
+		if (cur_prio != idx_prio)
+			continue;
+		return tap_flow_destroy_pmd(pmd, remote_flow, NULL);
+	}
+	return 0;
+}
+
+/**
+ * Destroy all implicit flows.
+ *
+ * @see rte_flow_flush()
+ */
+int
+tap_flow_implicit_flush(struct pmd_internals *pmd, struct rte_flow_error *error)
+{
+	struct rte_flow *remote_flow;
+
+	while (!LIST_EMPTY(&pmd->implicit_flows)) {
+		remote_flow = LIST_FIRST(&pmd->implicit_flows);
+		if (tap_flow_destroy_pmd(pmd, remote_flow, error) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+/**
  * Manage filter operations.
  *
  * @param dev
diff --git a/drivers/net/tap/tap_flow.h b/drivers/net/tap/tap_flow.h
index a05e945df523..94414f18f4ff 100644
--- a/drivers/net/tap/tap_flow.h
+++ b/drivers/net/tap/tap_flow.h
@@ -36,6 +36,7 @@
 
 #include <rte_flow.h>
 #include <rte_flow_driver.h>
+#include <rte_eth_tap.h>
 
 /**
  * In TC, priority 0 means we require the kernel to allocate one for us.
@@ -49,10 +50,33 @@
 #define GROUP_SHIFT 12
 #define MAX_GROUP GROUP_MASK
 
+/**
+ * These index are actually in reversed order: their priority is processed
+ * by subtracting their value to the lowest priority (PRIORITY_MASK).
+ * Thus the first one will have the lowest priority in the end
+ * (but biggest value).
+ */
+enum implicit_rule_index {
+	TAP_REMOTE_TX,
+	TAP_REMOTE_BROADCASTV6,
+	TAP_REMOTE_BROADCAST,
+	TAP_REMOTE_ALLMULTI,
+	TAP_REMOTE_PROMISC,
+	TAP_REMOTE_LOCAL_MAC,
+	TAP_REMOTE_MAX_IDX,
+};
+
 int tap_dev_filter_ctrl(struct rte_eth_dev *dev,
 			enum rte_filter_type filter_type,
 			enum rte_filter_op filter_op,
 			void *arg);
 int tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error);
 
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+			     enum implicit_rule_index idx);
+int tap_flow_implicit_destroy(struct pmd_internals *pmd,
+			      enum implicit_rule_index idx);
+int tap_flow_implicit_flush(struct pmd_internals *pmd,
+			    struct rte_flow_error *error);
+
 #endif /* _TAP_FLOW_H_ */
-- 
2.12.0.306.g4a9b9b3

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v6 1/1] net/tap: add remote netdevice traffic capture
  2017-03-23  8:42         ` [PATCH v6 1/1] net/tap: add " Pascal Mazon
@ 2017-03-24 15:48           ` Ferruh Yigit
  0 siblings, 0 replies; 24+ messages in thread
From: Ferruh Yigit @ 2017-03-24 15:48 UTC (permalink / raw)
  To: Pascal Mazon, keith.wiles; +Cc: dev

On 3/23/2017 8:42 AM, Pascal Mazon wrote:
> By default, a tap netdevice is of no use when not fed by a separate
> process. The ability to automatically feed it from another netdevice
> allows applications to capture any kind of traffic normally destined to
> the kernel stack.
> 
> This patch implements this ability through a new optional "remote"
> parameter.
> 
> Packets matching filtering rules created with the flow API are matched
> on the remote device and redirected to the tap PMD, where the relevant
> action will be performed.
> 
> Signed-off-by: Pascal Mazon <pascal.mazon@6wind.com>
> Acked-by: Olga Shern <olgas@mellanox.com>
> Acked-by: Keith Wiles <keith.wiles@intel.com>

Applied to dpdk-next-net/master, thanks.

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2017-03-24 15:48 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-03 12:27 [PATCH 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
2017-03-03 12:27 ` [PATCH 1/4] net/tap: add " Pascal Mazon
2017-03-03 12:27 ` [PATCH 2/4] net/tap: reflect tap flags on the remote Pascal Mazon
2017-03-03 12:27 ` [PATCH 3/4] net/tap: use the remote MAC address if available Pascal Mazon
2017-03-03 12:27 ` [PATCH 4/4] net/tap: set MTU on the remote Pascal Mazon
2017-03-03 15:54 ` [PATCH 0/4] net/tap: remote netdevice traffic capture Wiles, Keith
2017-03-07 16:38 ` [PATCH v2 0/4] " Pascal Mazon
2017-03-07 16:38   ` [PATCH v2 1/4] net/tap: add " Pascal Mazon
2017-03-07 16:38   ` [PATCH v2 2/4] net/tap: reflect tap flags on the remote Pascal Mazon
2017-03-07 16:38   ` [PATCH v2 3/4] net/tap: use the remote MAC address if available Pascal Mazon
2017-03-07 16:38   ` [PATCH v2 4/4] net/tap: set MTU on the remote Pascal Mazon
2017-03-08 10:06   ` [PATCH v3 0/4] net/tap: remote netdevice traffic capture Pascal Mazon
2017-03-08 10:06     ` [PATCH v3 1/4] net/tap: add " Pascal Mazon
2017-03-08 10:06     ` [PATCH v3 2/4] net/tap: reflect tap flags on the remote Pascal Mazon
2017-03-08 10:06     ` [PATCH v3 3/4] net/tap: use the remote MAC address if available Pascal Mazon
2017-03-08 10:06     ` [PATCH v3 4/4] net/tap: set MTU on the remote Pascal Mazon
2017-03-14  8:34     ` [PATCH v4] net/tap: remote netdevice traffic capture Pascal Mazon
2017-03-14  8:34       ` [PATCH v4] net/tap: add " Pascal Mazon
2017-03-14 14:00         ` Wiles, Keith
2017-03-15 15:03     ` [PATCH v5] net/tap: " Pascal Mazon
2017-03-15 15:03       ` [PATCH v5] net/tap: add " Pascal Mazon
2017-03-23  8:42       ` [PATCH v6 0/1] net/tap: " Pascal Mazon
2017-03-23  8:42         ` [PATCH v6 1/1] net/tap: add " Pascal Mazon
2017-03-24 15:48           ` Ferruh Yigit

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.