get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/21291/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 21291,
    "url": "https://patches.dpdk.org/api/patches/21291/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/85945a2e82ee31d82c46db35de8519ca783eb3de.1488542158.git.pascal.mazon@6wind.com/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<85945a2e82ee31d82c46db35de8519ca783eb3de.1488542158.git.pascal.mazon@6wind.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/85945a2e82ee31d82c46db35de8519ca783eb3de.1488542158.git.pascal.mazon@6wind.com",
    "date": "2017-03-03T12:27:38",
    "name": "[dpdk-dev,1/4] net/tap: add remote netdevice traffic capture",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "8bf48b5c29be7b58160da98e41c9cbc1092089d4",
    "submitter": {
        "id": 657,
        "url": "https://patches.dpdk.org/api/people/657/?format=api",
        "name": "Pascal Mazon",
        "email": "pascal.mazon@6wind.com"
    },
    "delegate": {
        "id": 319,
        "url": "https://patches.dpdk.org/api/users/319/?format=api",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@amd.com"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/85945a2e82ee31d82c46db35de8519ca783eb3de.1488542158.git.pascal.mazon@6wind.com/mbox/",
    "series": [],
    "comments": "https://patches.dpdk.org/api/patches/21291/comments/",
    "check": "fail",
    "checks": "https://patches.dpdk.org/api/patches/21291/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@dpdk.org",
        "Delivered-To": "patchwork@dpdk.org",
        "Received": [
            "from [92.243.14.124] (localhost [IPv6:::1])\n\tby dpdk.org (Postfix) with ESMTP id 08E2BBD2C;\n\tFri,  3 Mar 2017 13:28:48 +0100 (CET)",
            "from proxy.6wind.com (host.76.145.23.62.rev.coltfrance.com\n\t[62.23.145.76]) by dpdk.org (Postfix) with ESMTP id 9416037AA\n\tfor <dev@dpdk.org>; Fri,  3 Mar 2017 13:28:33 +0100 (CET)",
            "from 6wind.com (unknown [10.16.0.184])\n\tby proxy.6wind.com (Postfix) with SMTP id E293D24D72;\n\tFri,  3 Mar 2017 13:28:27 +0100 (CET)",
            "by 6wind.com (sSMTP sendmail emulation);\n\tFri, 03 Mar 2017 13:28:26 +0100"
        ],
        "From": "Pascal Mazon <pascal.mazon@6wind.com>",
        "To": "keith.wiles@intel.com",
        "Cc": "dev@dpdk.org,\n\tPascal Mazon <pascal.mazon@6wind.com>",
        "Date": "Fri,  3 Mar 2017 13:27:38 +0100",
        "Message-Id": "<85945a2e82ee31d82c46db35de8519ca783eb3de.1488542158.git.pascal.mazon@6wind.com>",
        "X-Mailer": "git-send-email 2.8.0.rc0",
        "In-Reply-To": "<cover.1488542158.git.pascal.mazon@6wind.com>",
        "References": "<cover.1488542158.git.pascal.mazon@6wind.com>",
        "Subject": "[dpdk-dev] [PATCH 1/4] net/tap: add remote netdevice traffic capture",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<http://dpdk.org/ml/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://dpdk.org/ml/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<http://dpdk.org/ml/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "By default, a tap netdevice is of no use when not fed by a separate\nprocess. The ability to automatically feed it from another netdevice\nallows applications to capture any kind of traffic normally destined to\nthe kernel stack.\n\nThis patch implements this ability through a new optional \"remote\"\nparameter.\n\nPackets matching filtering rules created with the flow API are matched\non the remote device and redirected to the tap PMD, where the relevant\naction will be performed.\n\nSigned-off-by: Pascal Mazon <pascal.mazon@6wind.com>\nAcked-by: Olga Shern <olgas@mellanox.com>\n---\n doc/guides/nics/tap.rst       |  17 ++\n drivers/net/tap/rte_eth_tap.c |  78 +++++++-\n drivers/net/tap/tap.h         |   3 +\n drivers/net/tap/tap_flow.c    | 414 ++++++++++++++++++++++++++++++++++++++++--\n drivers/net/tap/tap_flow.h    |  24 +++\n 5 files changed, 523 insertions(+), 13 deletions(-)",
    "diff": "diff --git a/doc/guides/nics/tap.rst b/doc/guides/nics/tap.rst\nindex c4f207be3b47..a937f018f7b5 100644\n--- a/doc/guides/nics/tap.rst\n+++ b/doc/guides/nics/tap.rst\n@@ -58,6 +58,23 @@ needed, but the interface does not enforce that speed, for example::\n \n    --vdev=net_tap0,iface=foo0,speed=25000\n \n+It is possible to specify a remote netdevice to capture packets from by adding\n+``remote=foo1``, for example::\n+\n+   --vdev=net_tap,iface=tap0,remote=foo1\n+\n+If a ``remote`` is set, then all packets with the tap PMD's local MAC coming\n+in on the remote netdevice will be redirected to the tap.\n+If the tap is in promiscuous mode, then all packets will be redirected.\n+In allmulti mode, all multicast packets will be redirected.\n+It is possible to add explicit rte_flow rules on the tap PMD to capture specific\n+traffic. For instance, in testpmd, the following rte_flow rule would capture\n+packets with the given MAC address from the remote, and send it to the tap RX\n+QUEUE 3::\n+\n+   testpmd> flow create 0 ingress pattern eth src is 02:03:04:05:06:07 / \\\n+            end actions queue index 3 / end\n+\n After the DPDK application is started you can send and receive packets on the\n interface using the standard rx_burst/tx_burst APIs in DPDK. From the host\n point of view you can use any host tool like tcpdump, Wireshark, ping, Pktgen\ndiff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c\nindex aa605f74e375..327fefd0d3a1 100644\n--- a/drivers/net/tap/rte_eth_tap.c\n+++ b/drivers/net/tap/rte_eth_tap.c\n@@ -61,6 +61,7 @@\n \n #define ETH_TAP_IFACE_ARG       \"iface\"\n #define ETH_TAP_SPEED_ARG       \"speed\"\n+#define ETH_TAP_REMOTE_ARG      \"remote\"\n \n #ifdef IFF_MULTI_QUEUE\n #define RTE_PMD_TAP_MAX_QUEUES\t16\n@@ -75,6 +76,7 @@ static struct rte_vdev_driver pmd_tap_drv;\n static const char *valid_arguments[] = {\n \tETH_TAP_IFACE_ARG,\n \tETH_TAP_SPEED_ARG,\n+\tETH_TAP_REMOTE_ARG,\n \tNULL\n };\n \n@@ -475,6 +477,8 @@ tap_promisc_enable(struct rte_eth_dev *dev)\n \n \tdev->data->promiscuous = 1;\n \ttap_link_set_flags(pmd, IFF_PROMISC, 1);\n+\tif (pmd->remote_if_index)\n+\t\ttap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);\n }\n \n static void\n@@ -484,6 +488,8 @@ tap_promisc_disable(struct rte_eth_dev *dev)\n \n \tdev->data->promiscuous = 0;\n \ttap_link_set_flags(pmd, IFF_PROMISC, 0);\n+\tif (pmd->remote_if_index)\n+\t\ttap_flow_implicit_destroy(dev, TAP_REMOTE_PROMISC);\n }\n \n static void\n@@ -493,6 +499,8 @@ tap_allmulti_enable(struct rte_eth_dev *dev)\n \n \tdev->data->all_multicast = 1;\n \ttap_link_set_flags(pmd, IFF_ALLMULTI, 1);\n+\tif (pmd->remote_if_index)\n+\t\ttap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);\n }\n \n static void\n@@ -502,6 +510,8 @@ tap_allmulti_disable(struct rte_eth_dev *dev)\n \n \tdev->data->all_multicast = 0;\n \ttap_link_set_flags(pmd, IFF_ALLMULTI, 0);\n+\tif (pmd->remote_if_index)\n+\t\ttap_flow_implicit_destroy(dev, TAP_REMOTE_ALLMULTI);\n }\n \n static void\n@@ -617,9 +627,42 @@ tap_setup_queue(struct rte_eth_dev *dev,\n \t\t\t\tpmd->name);\n \t\t\treturn fd;\n \t\t}\n+\t\tif (pmd->remote_if_index) {\n+\t\t\t/*\n+\t\t\t * Flush usually returns negative value because it tries\n+\t\t\t * to delete every QDISC (and on a running device, one\n+\t\t\t * QDISC at least is needed). Ignore negative return\n+\t\t\t * value.\n+\t\t\t */\n+\t\t\tqdisc_flush(pmd->nlsk_fd, pmd->remote_if_index);\n+\t\t\tif (qdisc_create_ingress(pmd->nlsk_fd,\n+\t\t\t\t\t\t pmd->remote_if_index) < 0)\n+\t\t\t\tgoto remote_fail;\n+\t\t\tLIST_INIT(&pmd->implicit_flows);\n+\t\t\tif (tap_flow_implicit_create(\n+\t\t\t\t    pmd, TAP_REMOTE_LOCAL_MAC) < 0)\n+\t\t\t\tgoto remote_fail;\n+\t\t\tif (tap_flow_implicit_create(\n+\t\t\t\t    pmd, TAP_REMOTE_BROADCAST) < 0)\n+\t\t\t\tgoto remote_fail;\n+\t\t\tif (tap_flow_implicit_create(\n+\t\t\t\t    pmd, TAP_REMOTE_BROADCASTV6) < 0)\n+\t\t\t\tgoto remote_fail;\n+\t\t\tif (tap_flow_implicit_create(\n+\t\t\t\t    pmd, TAP_REMOTE_TX) < 0)\n+\t\t\t\tgoto remote_fail;\n+\t\t}\n \t}\n \n \treturn fd;\n+\n+remote_fail:\n+\tRTE_LOG(ERR, PMD,\n+\t\t\"Could not set up remote flow rules for %s: remote disabled.\\n\",\n+\t\tpmd->name);\n+\tpmd->remote_if_index = 0;\n+\ttap_flow_implicit_flush(dev, NULL);\n+\treturn fd;\n }\n \n static int\n@@ -815,7 +858,7 @@ static const struct eth_dev_ops ops = {\n };\n \n static int\n-eth_dev_tap_create(const char *name, char *tap_name)\n+eth_dev_tap_create(const char *name, char *tap_name, char *remote_iface)\n {\n \tint numa_node = rte_socket_id();\n \tstruct rte_eth_dev *dev = NULL;\n@@ -880,6 +923,13 @@ eth_dev_tap_create(const char *name, char *tap_name)\n \t * creating/destroying flow rules.\n \t */\n \tpmd->nlsk_fd = nl_init();\n+\tif (strlen(remote_iface)) {\n+\t\tpmd->remote_if_index = if_nametoindex(remote_iface);\n+\t\tif (!pmd->remote_if_index)\n+\t\t\tRTE_LOG(ERR, PMD, \"Could not find %s ifindex: \"\n+\t\t\t\t\"remote interface will remain unconfigured\\n\",\n+\t\t\t\tremote_iface);\n+\t}\n \n \treturn 0;\n \n@@ -920,6 +970,19 @@ set_interface_speed(const char *key __rte_unused,\n \treturn 0;\n }\n \n+static int\n+set_remote_iface(const char *key __rte_unused,\n+\t\t const char *value,\n+\t\t void *extra_args)\n+{\n+\tchar *name = (char *)extra_args;\n+\n+\tif (value)\n+\t\tsnprintf(name, RTE_ETH_NAME_MAX_LEN, \"%s\", value);\n+\n+\treturn 0;\n+}\n+\n /* Open a TAP interface device.\n  */\n static int\n@@ -929,6 +992,7 @@ rte_pmd_tap_probe(const char *name, const char *params)\n \tstruct rte_kvargs *kvlist = NULL;\n \tint speed;\n \tchar tap_name[RTE_ETH_NAME_MAX_LEN];\n+\tchar remote_iface[RTE_ETH_NAME_MAX_LEN];\n \n \tspeed = ETH_SPEED_NUM_10G;\n \tsnprintf(tap_name, sizeof(tap_name), \"%s%d\",\n@@ -956,6 +1020,15 @@ rte_pmd_tap_probe(const char *name, const char *params)\n \t\t\t\tif (ret == -1)\n \t\t\t\t\tgoto leave;\n \t\t\t}\n+\n+\t\t\tif (rte_kvargs_count(kvlist, ETH_TAP_REMOTE_ARG) == 1) {\n+\t\t\t\tret = rte_kvargs_process(kvlist,\n+\t\t\t\t\t\t\t ETH_TAP_REMOTE_ARG,\n+\t\t\t\t\t\t\t &set_remote_iface,\n+\t\t\t\t\t\t\t remote_iface);\n+\t\t\t\tif (ret == -1)\n+\t\t\t\t\tgoto leave;\n+\t\t\t}\n \t\t}\n \t}\n \tpmd_link.link_speed = speed;\n@@ -963,7 +1036,7 @@ rte_pmd_tap_probe(const char *name, const char *params)\n \tRTE_LOG(NOTICE, PMD, \"Initializing pmd_tap for %s as %s\\n\",\n \t\tname, tap_name);\n \n-\tret = eth_dev_tap_create(name, tap_name);\n+\tret = eth_dev_tap_create(name, tap_name, remote_iface);\n \n leave:\n \tif (ret == -1) {\n@@ -994,6 +1067,7 @@ rte_pmd_tap_remove(const char *name)\n \t\treturn 0;\n \n \ttap_flow_flush(eth_dev, NULL);\n+\ttap_flow_implicit_flush(eth_dev, NULL);\n \n \tinternals = eth_dev->data->dev_private;\n \tif (internals->nlsk_fd)\ndiff --git a/drivers/net/tap/tap.h b/drivers/net/tap/tap.h\nindex 96d9025412b3..a5f83d4feea3 100644\n--- a/drivers/net/tap/tap.h\n+++ b/drivers/net/tap/tap.h\n@@ -68,7 +68,10 @@ struct pmd_internals {\n \tuint16_t nb_queues; /* Number of queues supported */\n \tstruct ether_addr eth_addr; /* Mac address of the device port */\n \tint if_index; /* IF_INDEX for the port */\n+\tint remote_if_index; /* remote netdevice IF_INDEX */\n \tLIST_HEAD(tap_flows, rte_flow) flows; /* rte_flow rules */\n+\t/* implicit rte_flow rules set when a remote device is active */\n+\tLIST_HEAD(tap_implicit_flows, rte_flow) implicit_flows;\n \tstruct rx_queue rxq[RTE_PMD_TAP_MAX_QUEUES]; /* List of RX queues */\n \tstruct tx_queue txq[RTE_PMD_TAP_MAX_QUEUES]; /* List of TX queues */\n };\ndiff --git a/drivers/net/tap/tap_flow.c b/drivers/net/tap/tap_flow.c\nindex bf989bd5be6c..ab54dcb10f33 100644\n--- a/drivers/net/tap/tap_flow.c\n+++ b/drivers/net/tap/tap_flow.c\n@@ -43,6 +43,7 @@\n \n struct rte_flow {\n \tLIST_ENTRY(rte_flow) next; /* Pointer to the next rte_flow structure */\n+\tstruct rte_flow *remote_flow; /* associated remote flow */\n \tstruct nlmsg msg;\n };\n \n@@ -55,6 +56,12 @@ struct convert_data {\n \tstruct rte_flow *flow;\n };\n \n+struct remote_rule {\n+\tstruct rte_flow_attr attr;\n+\tstruct rte_flow_item items[2];\n+\tint mirred;\n+};\n+\n static int tap_flow_create_eth(const struct rte_flow_item *item, void *data);\n #ifdef HAVE_TC_VLAN_ID\n static int tap_flow_create_vlan(const struct rte_flow_item *item, void *data);\n@@ -218,6 +225,114 @@ static const struct tap_flow_items tap_flow_items[] = {\n \t},\n };\n \n+static struct remote_rule implicit_rte_flows[TAP_REMOTE_MAX_IDX] = {\n+\t[TAP_REMOTE_LOCAL_MAC] = {\n+\t\t.attr = {\n+\t\t\t.group = MAX_GROUP,\n+\t\t\t.priority = PRIORITY_MASK - TAP_REMOTE_LOCAL_MAC,\n+\t\t\t.ingress = 1,\n+\t\t},\n+\t\t.items[0] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_ETH,\n+\t\t\t.mask =  &(const struct rte_flow_item_eth){\n+\t\t\t\t.dst.addr_bytes = \"\\xff\\xff\\xff\\xff\\xff\\xff\",\n+\t\t\t},\n+\t\t},\n+\t\t.items[1] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_END,\n+\t\t},\n+\t\t.mirred = TCA_EGRESS_REDIR,\n+\t},\n+\t[TAP_REMOTE_BROADCAST] = {\n+\t\t.attr = {\n+\t\t\t.group = MAX_GROUP,\n+\t\t\t.priority = PRIORITY_MASK - TAP_REMOTE_BROADCAST,\n+\t\t\t.ingress = 1,\n+\t\t},\n+\t\t.items[0] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_ETH,\n+\t\t\t.mask =  &(const struct rte_flow_item_eth){\n+\t\t\t\t.dst.addr_bytes = \"\\xff\\xff\\xff\\xff\\xff\\xff\",\n+\t\t\t},\n+\t\t\t.spec = &(const struct rte_flow_item_eth){\n+\t\t\t\t.dst.addr_bytes = \"\\xff\\xff\\xff\\xff\\xff\\xff\",\n+\t\t\t},\n+\t\t},\n+\t\t.items[1] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_END,\n+\t\t},\n+\t\t.mirred = TCA_EGRESS_MIRROR,\n+\t},\n+\t[TAP_REMOTE_BROADCASTV6] = {\n+\t\t.attr = {\n+\t\t\t.group = MAX_GROUP,\n+\t\t\t.priority = PRIORITY_MASK - TAP_REMOTE_BROADCASTV6,\n+\t\t\t.ingress = 1,\n+\t\t},\n+\t\t.items[0] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_ETH,\n+\t\t\t.mask =  &(const struct rte_flow_item_eth){\n+\t\t\t\t.dst.addr_bytes = \"\\x33\\x33\\x00\\x00\\x00\\x00\",\n+\t\t\t},\n+\t\t\t.spec = &(const struct rte_flow_item_eth){\n+\t\t\t\t.dst.addr_bytes = \"\\x33\\x33\\x00\\x00\\x00\\x00\",\n+\t\t\t},\n+\t\t},\n+\t\t.items[1] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_END,\n+\t\t},\n+\t\t.mirred = TCA_EGRESS_MIRROR,\n+\t},\n+\t[TAP_REMOTE_PROMISC] = {\n+\t\t.attr = {\n+\t\t\t.group = MAX_GROUP,\n+\t\t\t.priority = PRIORITY_MASK - TAP_REMOTE_PROMISC,\n+\t\t\t.ingress = 1,\n+\t\t},\n+\t\t.items[0] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_VOID,\n+\t\t},\n+\t\t.items[1] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_END,\n+\t\t},\n+\t\t.mirred = TCA_EGRESS_MIRROR,\n+\t},\n+\t[TAP_REMOTE_ALLMULTI] = {\n+\t\t.attr = {\n+\t\t\t.group = MAX_GROUP,\n+\t\t\t.priority = PRIORITY_MASK - TAP_REMOTE_ALLMULTI,\n+\t\t\t.ingress = 1,\n+\t\t},\n+\t\t.items[0] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_ETH,\n+\t\t\t.mask =  &(const struct rte_flow_item_eth){\n+\t\t\t\t.dst.addr_bytes = \"\\x01\\x00\\x00\\x00\\x00\\x00\",\n+\t\t\t},\n+\t\t\t.spec = &(const struct rte_flow_item_eth){\n+\t\t\t\t.dst.addr_bytes = \"\\x01\\x00\\x00\\x00\\x00\\x00\",\n+\t\t\t},\n+\t\t},\n+\t\t.items[1] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_END,\n+\t\t},\n+\t\t.mirred = TCA_EGRESS_MIRROR,\n+\t},\n+\t[TAP_REMOTE_TX] = {\n+\t\t.attr = {\n+\t\t\t.group = 0,\n+\t\t\t.priority = TAP_REMOTE_TX,\n+\t\t\t.egress = 1,\n+\t\t},\n+\t\t.items[0] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_VOID,\n+\t\t},\n+\t\t.items[1] = {\n+\t\t\t.type = RTE_FLOW_ITEM_TYPE_END,\n+\t\t},\n+\t\t.mirred = TCA_EGRESS_MIRROR,\n+\t},\n+};\n+\n /**\n  * Make as much checks as possible on an Ethernet item, and if a flow is\n  * provided, fill it appropriately with Ethernet info.\n@@ -648,6 +763,47 @@ add_action_gact(struct rte_flow *flow, int action)\n }\n \n /**\n+ * Transform a MIRRED action item in the provided flow for TC.\n+ *\n+ * @param[in, out] flow\n+ *   Flow to be filled.\n+ * @param[in] ifindex\n+ *   Netdevice ifindex, where to mirror/redirect packet to.\n+ * @param[in] action_type\n+ *   Either TCA_EGRESS_REDIR for redirection or TCA_EGRESS_MIRROR for mirroring.\n+ *\n+ * @return\n+ *   0 if checks are alright, -1 otherwise.\n+ */\n+static int\n+add_action_mirred(struct rte_flow *flow, uint16_t ifindex, uint16_t action_type)\n+{\n+\tstruct nlmsg *msg = &flow->msg;\n+\tsize_t act_index = 1;\n+\tstruct tc_mirred p = {\n+\t\t.eaction = action_type,\n+\t\t.ifindex = ifindex,\n+\t};\n+\n+\tif (nlattr_nested_start(msg, TCA_FLOWER_ACT) < 0)\n+\t\treturn -1;\n+\tif (nlattr_nested_start(msg, act_index++) < 0)\n+\t\treturn -1;\n+\tnlattr_add(&msg->nh, TCA_ACT_KIND, sizeof(\"mirred\"), \"mirred\");\n+\tif (nlattr_nested_start(msg, TCA_ACT_OPTIONS) < 0)\n+\t\treturn -1;\n+\tif (action_type == TCA_EGRESS_MIRROR)\n+\t\tp.action = TC_ACT_PIPE;\n+\telse /* REDIRECT */\n+\t\tp.action = TC_ACT_STOLEN;\n+\tnlattr_add(&msg->nh, TCA_MIRRED_PARMS, sizeof(p), &p);\n+\tnlattr_nested_finish(msg); /* nested TCA_ACT_OPTIONS */\n+\tnlattr_nested_finish(msg); /* nested act_index */\n+\tnlattr_nested_finish(msg); /* nested TCA_FLOWER_ACT */\n+\treturn 0;\n+}\n+\n+/**\n  * Transform a QUEUE action item in the provided flow for TC.\n  *\n  * @param[in, out] flow\n@@ -698,6 +854,15 @@ add_action_skbedit(struct rte_flow *flow, uint16_t queue)\n  *   Perform verbose error reporting if not NULL.\n  * @param[in, out] flow\n  *   Flow structure to update.\n+ * @param[in] mirred\n+ *   If set to TCA_EGRESS_REDIR, provided actions will be replaced with a\n+ *   redirection to the tap netdevice, and the TC rule will be configured\n+ *   on the remote netdevice in pmd.\n+ *   If set to TCA_EGRESS_MIRROR, provided actions will be replaced with a\n+ *   mirroring to the tap netdevice, and the TC rule will be configured\n+ *   on the remote netdevice in pmd. Matching packets will thus be duplicated.\n+ *   If set to 0, the standard behavior is to be used: set correct actions for\n+ *   the TC rule, and apply it on the tap netdevice.\n  *\n  * @return\n  *   0 on success, a negative errno value otherwise and rte_errno is set.\n@@ -708,7 +873,8 @@ priv_flow_process(struct pmd_internals *pmd,\n \t\t  const struct rte_flow_item items[],\n \t\t  const struct rte_flow_action actions[],\n \t\t  struct rte_flow_error *error,\n-\t\t  struct rte_flow *flow)\n+\t\t  struct rte_flow *flow,\n+\t\t  int mirred)\n {\n \tconst struct tap_flow_items *cur_item = tap_flow_items;\n \tstruct convert_data data = {\n@@ -735,15 +901,21 @@ priv_flow_process(struct pmd_internals *pmd,\n \t\tflow->msg.t.tcm_info = TC_H_MAKE(prio << 16,\n \t\t\t\t\t\t flow->msg.t.tcm_info);\n \t}\n-\tif (!attr->ingress) {\n-\t\trte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR,\n-\t\t\t\t   NULL, \"direction should be ingress\");\n-\t\treturn -rte_errno;\n-\t}\n-\t/* rte_flow ingress is actually egress as seen in the kernel */\n-\tif (attr->ingress && flow)\n-\t\tflow->msg.t.tcm_parent = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);\n \tif (flow) {\n+\t\tif (mirred) {\n+\t\t\t/*\n+\t\t\t * If attr->ingress, the rule applies on remote ingress\n+\t\t\t * to match incoming packets\n+\t\t\t * If attr->egress, the rule applies on tap ingress (as\n+\t\t\t * seen from the kernel) to deal with packets going out\n+\t\t\t * from the DPDK app.\n+\t\t\t */\n+\t\t\tflow->msg.t.tcm_parent = TC_H_MAKE(TC_H_INGRESS, 0);\n+\t\t} else {\n+\t\t\t/* Standard rule on tap egress (kernel standpoint). */\n+\t\t\tflow->msg.t.tcm_parent =\n+\t\t\t\tTC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);\n+\t\t}\n \t\t/* use flower filter type */\n \t\tnlattr_add(&flow->msg.nh, TCA_KIND, sizeof(\"flower\"), \"flower\");\n \t\tif (nlattr_nested_start(&flow->msg, TCA_OPTIONS) < 0)\n@@ -795,6 +967,22 @@ priv_flow_process(struct pmd_internals *pmd,\n \t\t\tnlattr_add16(&flow->msg.nh, TCA_FLOWER_KEY_ETH_TYPE,\n \t\t\t\t     data.eth_type);\n \t}\n+\tif (mirred && flow) {\n+\t\tuint16_t if_index = pmd->if_index;\n+\n+\t\t/*\n+\t\t * If attr->egress && mirred, then this is a special\n+\t\t * case where the rule must be applied on the tap, to\n+\t\t * redirect packets coming from the DPDK App, out\n+\t\t * through the remote netdevice.\n+\t\t */\n+\t\tif (attr->egress)\n+\t\t\tif_index = pmd->remote_if_index;\n+\t\tif (add_action_mirred(flow, if_index, mirred) < 0)\n+\t\t\tgoto exit_action_not_supported;\n+\t\telse\n+\t\t\tgoto end;\n+\t}\n \tfor (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {\n \t\tint err = 0;\n \n@@ -829,6 +1017,7 @@ priv_flow_process(struct pmd_internals *pmd,\n \t\tif (err)\n \t\t\tgoto exit_action_not_supported;\n \t}\n+end:\n \tif (flow)\n \t\tnlattr_nested_finish(&flow->msg); /* nested TCA_OPTIONS */\n \treturn 0;\n@@ -859,7 +1048,7 @@ tap_flow_validate(struct rte_eth_dev *dev,\n {\n \tstruct pmd_internals *pmd = dev->data->dev_private;\n \n-\treturn priv_flow_process(pmd, attr, items, actions, error, NULL);\n+\treturn priv_flow_process(pmd, attr, items, actions, error, NULL, 0);\n }\n \n /**\n@@ -915,6 +1104,7 @@ tap_flow_create(struct rte_eth_dev *dev,\n \t\tstruct rte_flow_error *error)\n {\n \tstruct pmd_internals *pmd = dev->data->dev_private;\n+\tstruct rte_flow *remote_flow = NULL;\n \tstruct rte_flow *flow = NULL;\n \tstruct nlmsg *msg = NULL;\n \tint err;\n@@ -925,6 +1115,17 @@ tap_flow_create(struct rte_eth_dev *dev,\n \t\t\t\t   \"can't create rule, ifindex not found\");\n \t\tgoto fail;\n \t}\n+\t/*\n+\t * No rules configured through standard rte_flow should be set on the\n+\t * priorities used by implicit rules.\n+\t */\n+\tif ((attr->group == MAX_GROUP) &&\n+\t    attr->priority > (MAX_PRIORITY - TAP_REMOTE_MAX_IDX)) {\n+\t\trte_flow_error_set(\n+\t\t\terror, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,\n+\t\t\tNULL, \"priority value too big\");\n+\t\tgoto fail;\n+\t}\n \tflow = rte_malloc(__func__, sizeof(struct rte_flow), 0);\n \tif (!flow) {\n \t\trte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,\n@@ -936,7 +1137,7 @@ tap_flow_create(struct rte_eth_dev *dev,\n \t\t    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);\n \tmsg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));\n \ttap_flow_set_handle(flow);\n-\tif (priv_flow_process(pmd, attr, items, actions, error, flow))\n+\tif (priv_flow_process(pmd, attr, items, actions, error, flow, 0))\n \t\tgoto fail;\n \terr = nl_send(pmd->nlsk_fd, &msg->nh);\n \tif (err < 0) {\n@@ -951,14 +1152,183 @@ tap_flow_create(struct rte_eth_dev *dev,\n \t\tgoto fail;\n \t}\n \tLIST_INSERT_HEAD(&pmd->flows, flow, next);\n+\t/**\n+\t * If a remote device is configured, a TC rule with identical items for\n+\t * matching must be set on that device, with a single action: redirect\n+\t * to the local pmd->if_index.\n+\t */\n+\tif (pmd->remote_if_index) {\n+\t\tremote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);\n+\t\tif (!remote_flow) {\n+\t\t\trte_flow_error_set(\n+\t\t\t\terror, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,\n+\t\t\t\t\"cannot allocate memory for rte_flow\");\n+\t\t\tgoto fail;\n+\t\t}\n+\t\tmsg = &remote_flow->msg;\n+\t\t/* set the rule if_index for the remote netdevice */\n+\t\ttc_init_msg(\n+\t\t\tmsg, pmd->remote_if_index, RTM_NEWTFILTER,\n+\t\t\tNLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);\n+\t\tmsg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));\n+\t\ttap_flow_set_handle(remote_flow);\n+\t\tif (priv_flow_process(pmd, attr, items, NULL,\n+\t\t\t\t      error, remote_flow, TCA_EGRESS_REDIR)) {\n+\t\t\trte_flow_error_set(\n+\t\t\t\terror, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,\n+\t\t\t\tNULL, \"rte flow rule validation failed\");\n+\t\t\tgoto fail;\n+\t\t}\n+\t\terr = nl_send(pmd->nlsk_fd, &msg->nh);\n+\t\tif (err < 0) {\n+\t\t\trte_flow_error_set(\n+\t\t\t\terror, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,\n+\t\t\t\tNULL, \"Failure sending nl request\");\n+\t\t\tgoto fail;\n+\t\t}\n+\t\terr = nl_recv_ack(pmd->nlsk_fd);\n+\t\tif (err < 0) {\n+\t\t\trte_flow_error_set(\n+\t\t\t\terror, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,\n+\t\t\t\tNULL, \"overlapping rules\");\n+\t\t\tgoto fail;\n+\t\t}\n+\t\tflow->remote_flow = remote_flow;\n+\t}\n \treturn flow;\n fail:\n+\tif (remote_flow)\n+\t\trte_free(remote_flow);\n \tif (flow)\n \t\trte_free(flow);\n \treturn NULL;\n }\n \n /**\n+ * Add an implicit flow rule on the remote device to make sure traffic gets to\n+ * the tap netdevice from there.\n+ *\n+ * @param pmd\n+ *   Pointer to private structure.\n+ * @param[in] idx\n+ *   The idx in the implicit_rte_flows array specifying which rule to apply.\n+ *\n+ * @return -1 if the rule couldn't be applied, 0 otherwise.\n+ */\n+int tap_flow_implicit_create(struct pmd_internals *pmd,\n+\t\t\t     enum implicit_rule_index idx)\n+{\n+\tstruct rte_flow_item *items = implicit_rte_flows[idx].items;\n+\tstruct rte_flow_attr *attr = &implicit_rte_flows[idx].attr;\n+\tstruct rte_flow_item_eth eth_local = { .type = 0 };\n+\tuint16_t if_index = pmd->remote_if_index;\n+\tstruct rte_flow *remote_flow = NULL;\n+\tstruct nlmsg *msg = NULL;\n+\tint err = 0;\n+\tstruct rte_flow_item items_local[2] = {\n+\t\t[0] = {\n+\t\t\t.type = items[0].type,\n+\t\t\t.spec = &eth_local,\n+\t\t\t.mask = items[0].mask,\n+\t\t},\n+\t\t[1] = {\n+\t\t\t.type = items[1].type,\n+\t\t}\n+\t};\n+\n+\tremote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);\n+\tif (!remote_flow) {\n+\t\tRTE_LOG(ERR, PMD, \"Cannot allocate memory for rte_flow\");\n+\t\tgoto fail;\n+\t}\n+\tmsg = &remote_flow->msg;\n+\tif (idx == TAP_REMOTE_TX) {\n+\t\tif_index = pmd->if_index;\n+\t} else if (idx == TAP_REMOTE_LOCAL_MAC) {\n+\t\t/*\n+\t\t * eth addr couldn't be set in implicit_rte_flows[] as it is not\n+\t\t * known at compile time.\n+\t\t */\n+\t\tmemcpy(&eth_local.dst, &pmd->eth_addr, sizeof(pmd->eth_addr));\n+\t\titems = items_local;\n+\t}\n+\ttc_init_msg(msg, if_index, RTM_NEWTFILTER,\n+\t\t    NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);\n+\tmsg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));\n+\ttap_flow_set_handle(remote_flow);\n+\tif (priv_flow_process(pmd, attr, items, NULL, NULL,\n+\t\t\t      remote_flow, implicit_rte_flows[idx].mirred)) {\n+\t\tRTE_LOG(ERR, PMD, \"rte flow rule validation failed\\n\");\n+\t\tgoto fail;\n+\t}\n+\terr = nl_send(pmd->nlsk_fd, &msg->nh);\n+\tif (err < 0) {\n+\t\tRTE_LOG(ERR, PMD, \"Failure sending nl request\");\n+\t\tgoto fail;\n+\t}\n+\terr = nl_recv_ack(pmd->nlsk_fd);\n+\tif (err < 0) {\n+\t\tRTE_LOG(ERR, PMD,\n+\t\t\t\"Kernel refused TC filter rule creation\");\n+\t\tgoto fail;\n+\t}\n+\tLIST_INSERT_HEAD(&pmd->implicit_flows, remote_flow, next);\n+\treturn 0;\n+fail:\n+\tif (remote_flow)\n+\t\trte_free(remote_flow);\n+\treturn -1;\n+}\n+\n+/**\n+ * Remove specific implicit flow rule on the remote device.\n+ *\n+ * @param pmd\n+ *   Pointer to private structure.\n+ * @param[in] idx\n+ *   The idx in the implicit_rte_flows array specifying which rule to remove.\n+ *\n+ * @return -1 if one of the implicit rules couldn't be created, 0 otherwise.\n+ */\n+int tap_flow_implicit_destroy(struct rte_eth_dev *dev,\n+\t\t\t      enum implicit_rule_index idx)\n+{\n+\tstruct pmd_internals *pmd = dev->data->dev_private;\n+\tstruct rte_flow *remote_flow;\n+\tint cur_prio = -1;\n+\tint idx_prio = implicit_rte_flows[idx].attr.priority + PRIORITY_OFFSET;\n+\n+\tfor (remote_flow = LIST_FIRST(&pmd->implicit_flows);\n+\t     remote_flow;\n+\t     remote_flow = LIST_NEXT(remote_flow, next)) {\n+\t\tcur_prio = (remote_flow->msg.t.tcm_info >> 16) & PRIORITY_MASK;\n+\t\tif (cur_prio != idx_prio)\n+\t\t\tcontinue;\n+\t\treturn tap_flow_destroy(dev, remote_flow, NULL);\n+\t}\n+\treturn 0;\n+}\n+\n+/**\n+ * Destroy all implicit flows.\n+ *\n+ * @see rte_flow_flush()\n+ */\n+int\n+tap_flow_implicit_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)\n+{\n+\tstruct pmd_internals *pmd = dev->data->dev_private;\n+\tstruct rte_flow *remote_flow;\n+\n+\twhile (!LIST_EMPTY(&pmd->implicit_flows)) {\n+\t\tremote_flow = LIST_FIRST(&pmd->implicit_flows);\n+\t\tif (tap_flow_destroy(dev, remote_flow, error) < 0)\n+\t\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n+\n+/**\n  * Destroy a flow.\n  *\n  * @see rte_flow_destroy()\n@@ -970,6 +1340,7 @@ tap_flow_destroy(struct rte_eth_dev *dev,\n \t\t struct rte_flow_error *error)\n {\n \tstruct pmd_internals *pmd = dev->data->dev_private;\n+\tstruct rte_flow *remote_flow = flow->remote_flow;\n \tint ret = 0;\n \n \tLIST_REMOVE(flow, next);\n@@ -989,7 +1360,28 @@ tap_flow_destroy(struct rte_eth_dev *dev,\n \t\t\t\t   \"couldn't receive kernel ack to our request\");\n \t\tgoto end;\n \t}\n+\tif (remote_flow) {\n+\t\tremote_flow->msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;\n+\t\tremote_flow->msg.nh.nlmsg_type = RTM_DELTFILTER;\n+\n+\t\tret = nl_send(pmd->nlsk_fd, &remote_flow->msg.nh);\n+\t\tif (ret < 0) {\n+\t\t\trte_flow_error_set(\n+\t\t\t\terror, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,\n+\t\t\t\tNULL, \"Failure sending nl request\");\n+\t\t\tgoto end;\n+\t\t}\n+\t\tret = nl_recv_ack(pmd->nlsk_fd);\n+\t\tif (ret < 0) {\n+\t\t\trte_flow_error_set(\n+\t\t\t\terror, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,\n+\t\t\t\tNULL, \"Failure trying to receive nl ack\");\n+\t\t\tgoto end;\n+\t\t}\n+\t}\n end:\n+\tif (remote_flow)\n+\t\trte_free(remote_flow);\n \trte_free(flow);\n \treturn ret;\n }\ndiff --git a/drivers/net/tap/tap_flow.h b/drivers/net/tap/tap_flow.h\nindex a05e945df523..0134cdbaeb90 100644\n--- a/drivers/net/tap/tap_flow.h\n+++ b/drivers/net/tap/tap_flow.h\n@@ -36,6 +36,7 @@\n \n #include <rte_flow.h>\n #include <rte_flow_driver.h>\n+#include <tap.h>\n \n /**\n  * In TC, priority 0 means we require the kernel to allocate one for us.\n@@ -49,10 +50,33 @@\n #define GROUP_SHIFT 12\n #define MAX_GROUP GROUP_MASK\n \n+/**\n+ * These index are actually in reversed order: their priority is processed\n+ * by subtracting their value to the lowest priority (PRIORITY_MASK).\n+ * Thus the first one will have the lowest priority in the end\n+ * (but biggest value).\n+ */\n+enum implicit_rule_index {\n+\tTAP_REMOTE_TX,\n+\tTAP_REMOTE_BROADCASTV6,\n+\tTAP_REMOTE_BROADCAST,\n+\tTAP_REMOTE_ALLMULTI,\n+\tTAP_REMOTE_PROMISC,\n+\tTAP_REMOTE_LOCAL_MAC,\n+\tTAP_REMOTE_MAX_IDX,\n+};\n+\n int tap_dev_filter_ctrl(struct rte_eth_dev *dev,\n \t\t\tenum rte_filter_type filter_type,\n \t\t\tenum rte_filter_op filter_op,\n \t\t\tvoid *arg);\n int tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error);\n \n+int tap_flow_implicit_create(struct pmd_internals *pmd,\n+\t\t\t     enum implicit_rule_index idx);\n+int tap_flow_implicit_destroy(struct rte_eth_dev *dev,\n+\t\t\t      enum implicit_rule_index idx);\n+int tap_flow_implicit_flush(struct rte_eth_dev *dev,\n+\t\t\t    struct rte_flow_error *error);\n+\n #endif /* _TAP_FLOW_H_ */\n",
    "prefixes": [
        "dpdk-dev",
        "1/4"
    ]
}