get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 66504,
    "url": "http://patches.dpdk.org/api/patches/66504/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20200310111037.31451-2-aostruszka@marvell.com/",
    "project": {
        "id": 1,
        "url": "http://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": "<20200310111037.31451-2-aostruszka@marvell.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200310111037.31451-2-aostruszka@marvell.com",
    "date": "2020-03-10T11:10:34",
    "name": "[v2,1/4] lib: introduce IF Proxy library",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "a8b0f5122e931ff0c26ad96b8d474eb242722794",
    "submitter": {
        "id": 1429,
        "url": "http://patches.dpdk.org/api/people/1429/?format=api",
        "name": "Andrzej Ostruszka [C]",
        "email": "aostruszka@marvell.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20200310111037.31451-2-aostruszka@marvell.com/mbox/",
    "series": [
        {
            "id": 8868,
            "url": "http://patches.dpdk.org/api/series/8868/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=8868",
            "date": "2020-03-10T11:10:33",
            "name": "Introduce IF proxy library",
            "version": 2,
            "mbox": "http://patches.dpdk.org/series/8868/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/66504/comments/",
    "check": "fail",
    "checks": "http://patches.dpdk.org/api/patches/66504/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id BAAE7A0565;\n\tTue, 10 Mar 2020 12:10:55 +0100 (CET)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id AF8B81C001;\n\tTue, 10 Mar 2020 12:10:47 +0100 (CET)",
            "from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com\n [67.231.156.173]) by dpdk.org (Postfix) with ESMTP id A34E01BFFC\n for <dev@dpdk.org>; Tue, 10 Mar 2020 12:10:46 +0100 (CET)",
            "from pps.filterd (m0045851.ppops.net [127.0.0.1])\n by mx0b-0016f401.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id\n 02AB5gBc007525; Tue, 10 Mar 2020 04:10:46 -0700",
            "from sc-exch01.marvell.com ([199.233.58.181])\n by mx0b-0016f401.pphosted.com with ESMTP id 2yp04fjt4c-1\n (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT);\n Tue, 10 Mar 2020 04:10:45 -0700",
            "from DC5-EXCH02.marvell.com (10.69.176.39) by SC-EXCH01.marvell.com\n (10.93.176.81) with Microsoft SMTP Server (TLS) id 15.0.1497.2;\n Tue, 10 Mar 2020 04:10:43 -0700",
            "from SC-EXCH03.marvell.com (10.93.176.83) by DC5-EXCH02.marvell.com\n (10.69.176.39) with Microsoft SMTP Server (TLS) id 15.0.1497.2;\n Tue, 10 Mar 2020 04:10:42 -0700",
            "from maili.marvell.com (10.93.176.43) by SC-EXCH03.marvell.com\n (10.93.176.83) with Microsoft SMTP Server id 15.0.1497.2 via Frontend\n Transport; Tue, 10 Mar 2020 04:10:41 -0700",
            "from amok.marvell.com (unknown [10.95.130.113])\n by maili.marvell.com (Postfix) with ESMTP id 7EC9B3F7040;\n Tue, 10 Mar 2020 04:10:40 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com;\n h=from : to : subject\n : date : message-id : in-reply-to : references : mime-version :\n content-transfer-encoding : content-type; s=pfpt0818;\n bh=CgUCevlhUoYl43LFRPMvltMfrC2w+nzreB606PKUe3s=;\n b=yZykNSnbxOgXBY2cOOeb61zXm1OdOsSa/BfXryoDQbxQprEEK2gqx5kzsesgMw3DrNQ6\n u0+pKzjrxdXsZZTO0jPh/cBPR78RnOp28Hr4RTPR5G3k0x116J7PYC4fm7KHd7twO16W\n W7i75IXAKjedqF6KojGjhqW+AkadfZ52YQAiQoRZpOI4BxDQTCs+UmubaSVRCqyk85Tu\n zMdAFG5fzOINm0jVzlEfJAg4T4bw4kYEAm/sDmXm6iOnbVbVBP9DxHmW1UiFNe/bUTzI\n QmqDZADKfN5ykN3D2JXgWU35co0zXYh1qPOxuRa+SRTvpnRTUoqI3ABIRe/06pegcSbL sQ==",
        "From": "Andrzej Ostruszka <aostruszka@marvell.com>",
        "To": "<dev@dpdk.org>, Thomas Monjalon <thomas@monjalon.net>",
        "Date": "Tue, 10 Mar 2020 12:10:34 +0100",
        "Message-ID": "<20200310111037.31451-2-aostruszka@marvell.com>",
        "X-Mailer": "git-send-email 2.17.1",
        "In-Reply-To": "<20200310111037.31451-1-aostruszka@marvell.com>",
        "References": "<20200306164104.15528-1-aostruszka@marvell.com>\n <20200310111037.31451-1-aostruszka@marvell.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain",
        "X-Proofpoint-Virus-Version": "vendor=fsecure engine=2.50.10434:6.0.138, 18.0.572\n definitions=2020-03-10_06:2020-03-10,\n 2020-03-10 signatures=0",
        "Subject": "[dpdk-dev] [PATCH v2 1/4] lib: introduce IF Proxy library",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "This library allows to designate ports visible to the system (such as\nTun/Tap or KNI) as port representors serving as proxies for other DPDK\nports.  When such a proxy is configured this library initially queries\nnetwork configuration from the system and later monitors its changes.\n\nThe information gathered is passed to the application either via a set\nof user registered callbacks or as an event added to the configured\nnotification queue (or a combination of these two mechanisms).  This way\nuser can use normal network utilities (like those from the iproute2\nsuite) to configure DPDK ports.\n\nSigned-off-by: Andrzej Ostruszka <aostruszka@marvell.com>\n---\n MAINTAINERS                                   |   3 +\n config/common_base                            |   5 +\n config/common_linux                           |   1 +\n lib/Makefile                                  |   2 +\n .../common/include/rte_eal_interrupts.h       |   2 +\n lib/librte_eal/linux/eal/eal_interrupts.c     |  14 +-\n lib/librte_if_proxy/Makefile                  |  29 +\n lib/librte_if_proxy/if_proxy_common.c         | 494 +++++++++++++++\n lib/librte_if_proxy/if_proxy_priv.h           |  97 +++\n lib/librte_if_proxy/linux/Makefile            |   4 +\n lib/librte_if_proxy/linux/if_proxy.c          | 550 +++++++++++++++++\n lib/librte_if_proxy/meson.build               |  19 +\n lib/librte_if_proxy/rte_if_proxy.h            | 561 ++++++++++++++++++\n lib/librte_if_proxy/rte_if_proxy_version.map  |  19 +\n lib/meson.build                               |   2 +-\n 15 files changed, 1797 insertions(+), 5 deletions(-)\n create mode 100644 lib/librte_if_proxy/Makefile\n create mode 100644 lib/librte_if_proxy/if_proxy_common.c\n create mode 100644 lib/librte_if_proxy/if_proxy_priv.h\n create mode 100644 lib/librte_if_proxy/linux/Makefile\n create mode 100644 lib/librte_if_proxy/linux/if_proxy.c\n create mode 100644 lib/librte_if_proxy/meson.build\n create mode 100644 lib/librte_if_proxy/rte_if_proxy.h\n create mode 100644 lib/librte_if_proxy/rte_if_proxy_version.map",
    "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex f4e0ed8e0..aec7326ca 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -1469,6 +1469,9 @@ F: examples/bpf/\n F: app/test/test_bpf.c\n F: doc/guides/prog_guide/bpf_lib.rst\n \n+IF Proxy - EXPERIMENTAL\n+M: Andrzej Ostruszka <aostruszka@marvell.com>\n+F: lib/librte_if_proxy/\n \n Test Applications\n -----------------\ndiff --git a/config/common_base b/config/common_base\nindex 7ca2f28b1..dcc0a0650 100644\n--- a/config/common_base\n+++ b/config/common_base\n@@ -1075,6 +1075,11 @@ CONFIG_RTE_LIBRTE_BPF_ELF=n\n #\n CONFIG_RTE_LIBRTE_IPSEC=y\n \n+#\n+# Compile librte_if_proxy\n+#\n+CONFIG_RTE_LIBRTE_IF_PROXY=n\n+\n #\n # Compile the test application\n #\ndiff --git a/config/common_linux b/config/common_linux\nindex 816810671..1244eb0ae 100644\n--- a/config/common_linux\n+++ b/config/common_linux\n@@ -16,6 +16,7 @@ CONFIG_RTE_LIBRTE_VHOST_NUMA=y\n CONFIG_RTE_LIBRTE_VHOST_POSTCOPY=n\n CONFIG_RTE_LIBRTE_PMD_VHOST=y\n CONFIG_RTE_LIBRTE_IFC_PMD=y\n+CONFIG_RTE_LIBRTE_IF_PROXY=y\n CONFIG_RTE_LIBRTE_PMD_AF_PACKET=y\n CONFIG_RTE_LIBRTE_PMD_MEMIF=y\n CONFIG_RTE_LIBRTE_PMD_SOFTNIC=y\ndiff --git a/lib/Makefile b/lib/Makefile\nindex 46b91ae1a..6a20806f1 100644\n--- a/lib/Makefile\n+++ b/lib/Makefile\n@@ -118,6 +118,8 @@ DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry\n DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev\n DIRS-$(CONFIG_RTE_LIBRTE_RCU) += librte_rcu\n DEPDIRS-librte_rcu := librte_eal\n+DIRS-$(CONFIG_RTE_LIBRTE_IF_PROXY) += librte_if_proxy\n+DEPDIRS-librte_if_proxy := librte_eal librte_ethdev\n \n ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)\n DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni\ndiff --git a/lib/librte_eal/common/include/rte_eal_interrupts.h b/lib/librte_eal/common/include/rte_eal_interrupts.h\nindex 773a34a42..296a3853d 100644\n--- a/lib/librte_eal/common/include/rte_eal_interrupts.h\n+++ b/lib/librte_eal/common/include/rte_eal_interrupts.h\n@@ -36,6 +36,8 @@ enum rte_intr_handle_type {\n \tRTE_INTR_HANDLE_VDEV,         /**< virtual device */\n \tRTE_INTR_HANDLE_DEV_EVENT,    /**< device event handle */\n \tRTE_INTR_HANDLE_VFIO_REQ,     /**< VFIO request handle */\n+\tRTE_INTR_HANDLE_NETLINK,      /**< netlink notification handle */\n+\n \tRTE_INTR_HANDLE_MAX           /**< count of elements */\n };\n \ndiff --git a/lib/librte_eal/linux/eal/eal_interrupts.c b/lib/librte_eal/linux/eal/eal_interrupts.c\nindex cb8e10709..16236a8c4 100644\n--- a/lib/librte_eal/linux/eal/eal_interrupts.c\n+++ b/lib/librte_eal/linux/eal/eal_interrupts.c\n@@ -680,6 +680,9 @@ rte_intr_enable(const struct rte_intr_handle *intr_handle)\n \t\tbreak;\n \t/* not used at this moment */\n \tcase RTE_INTR_HANDLE_ALARM:\n+#if RTE_LIBRTE_IF_PROXY\n+\tcase RTE_INTR_HANDLE_NETLINK:\n+#endif\n \t\treturn -1;\n #ifdef VFIO_PRESENT\n \tcase RTE_INTR_HANDLE_VFIO_MSIX:\n@@ -796,6 +799,9 @@ rte_intr_disable(const struct rte_intr_handle *intr_handle)\n \t\tbreak;\n \t/* not used at this moment */\n \tcase RTE_INTR_HANDLE_ALARM:\n+#if RTE_LIBRTE_IF_PROXY\n+\tcase RTE_INTR_HANDLE_NETLINK:\n+#endif\n \t\treturn -1;\n #ifdef VFIO_PRESENT\n \tcase RTE_INTR_HANDLE_VFIO_MSIX:\n@@ -889,12 +895,12 @@ eal_intr_process_interrupts(struct epoll_event *events, int nfds)\n \t\t\tbreak;\n #endif\n #endif\n-\t\tcase RTE_INTR_HANDLE_VDEV:\n \t\tcase RTE_INTR_HANDLE_EXT:\n-\t\t\tbytes_read = 0;\n-\t\t\tcall = true;\n-\t\t\tbreak;\n+\t\tcase RTE_INTR_HANDLE_VDEV:\n \t\tcase RTE_INTR_HANDLE_DEV_EVENT:\n+#if RTE_LIBRTE_IF_PROXY\n+\t\tcase RTE_INTR_HANDLE_NETLINK:\n+#endif\n \t\t\tbytes_read = 0;\n \t\t\tcall = true;\n \t\t\tbreak;\ndiff --git a/lib/librte_if_proxy/Makefile b/lib/librte_if_proxy/Makefile\nnew file mode 100644\nindex 000000000..43cb702a2\n--- /dev/null\n+++ b/lib/librte_if_proxy/Makefile\n@@ -0,0 +1,29 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(C) 2020 Marvell International Ltd.\n+\n+include $(RTE_SDK)/mk/rte.vars.mk\n+\n+# library name\n+LIB = librte_if_proxy.a\n+\n+CFLAGS += -DALLOW_EXPERIMENTAL_API\n+CFLAGS += -O3\n+CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR)\n+LDLIBS += -lrte_eal -lrte_ethdev\n+\n+EXPORT_MAP := rte_if_proxy_version.map\n+\n+LIBABIVER := 1\n+\n+# all source are stored in SRCS-y\n+SRCS-$(CONFIG_RTE_LIBRTE_IF_PROXY) := if_proxy_common.c\n+\n+SYSDIR := $(patsubst \"%app\",%,$(CONFIG_RTE_EXEC_ENV))\n+include $(SRCDIR)/$(SYSDIR)/Makefile\n+\n+SRCS-$(CONFIG_RTE_LIBRTE_IF_PROXY) += $(addprefix $(SYSDIR)/,$(SRCS))\n+\n+# install this header file\n+SYMLINK-$(CONFIG_RTE_LIBRTE_IF_PROXY)-include := rte_if_proxy.h\n+\n+include $(RTE_SDK)/mk/rte.lib.mk\ndiff --git a/lib/librte_if_proxy/if_proxy_common.c b/lib/librte_if_proxy/if_proxy_common.c\nnew file mode 100644\nindex 000000000..546dc7810\n--- /dev/null\n+++ b/lib/librte_if_proxy/if_proxy_common.c\n@@ -0,0 +1,494 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#include <if_proxy_priv.h>\n+#include <rte_string_fns.h>\n+\n+\n+/* Definitions of data mentioned in if_proxy_priv.h and local ones. */\n+int ifpx_log_type;\n+\n+uint16_t ifpx_ports[RTE_MAX_ETHPORTS];\n+\n+rte_spinlock_t ifpx_lock = RTE_SPINLOCK_INITIALIZER;\n+\n+struct ifpx_proxies_head ifpx_proxies = TAILQ_HEAD_INITIALIZER(ifpx_proxies);\n+\n+struct ifpx_queue_node {\n+\tTAILQ_ENTRY(ifpx_queue_node) elem;\n+\tuint16_t state;\n+\tstruct rte_ring *r;\n+};\n+static\n+TAILQ_HEAD(ifpx_queues_head, ifpx_queue_node) ifpx_queues =\n+\t\tTAILQ_HEAD_INITIALIZER(ifpx_queues);\n+\n+/* All function pointers have the same size - so use this one to typecast\n+ * different callbacks in rte_ifpx_callbacks and test their presence in a\n+ * generic way.\n+ */\n+union cb_ptr_t {\n+\tint (*f_ptr)(void *ev); /* type for normal event notification */\n+\tint (*cfg_done)(void);  /* lib notification for finished config */\n+};\n+union {\n+\tstruct rte_ifpx_callbacks cbs;\n+\tunion cb_ptr_t funcs[RTE_IFPX_NUM_EVENTS];\n+} ifpx_callbacks;\n+\n+uint64_t rte_ifpx_events_available(void)\n+{\n+\t/* All events are supported on Linux. */\n+\treturn (1ULL << RTE_IFPX_NUM_EVENTS) - 1;\n+}\n+\n+uint16_t rte_ifpx_proxy_create(enum rte_ifpx_proxy_type type)\n+{\n+\tchar devargs[16] = { '\\0' };\n+\tint dev_cnt = 0, nlen;\n+\tuint16_t port_id;\n+\n+\tswitch (type) {\n+\tcase RTE_IFPX_DEFAULT:\n+\tcase RTE_IFPX_TAP:\n+\t\tnlen = strlcpy(devargs, \"net_tap\", sizeof(devargs));\n+\t\tbreak;\n+\tcase RTE_IFPX_KNI:\n+\t\tnlen = strlcpy(devargs, \"net_kni\", sizeof(devargs));\n+\t\tbreak;\n+\tdefault:\n+\t\tIFPX_LOG(ERR, \"Unknown proxy type: %d\", type);\n+\t\treturn RTE_MAX_ETHPORTS;\n+\t}\n+\n+\tRTE_ETH_FOREACH_DEV(port_id) {\n+\t\tif (strcmp(rte_eth_devices[port_id].device->driver->name,\n+\t\t\t   devargs) == 0)\n+\t\t\t++dev_cnt;\n+\t}\n+\tsnprintf(devargs+nlen, sizeof(devargs)-nlen, \"%d\", dev_cnt);\n+\n+\treturn rte_ifpx_proxy_create_by_devarg(devargs);\n+}\n+\n+uint16_t rte_ifpx_proxy_create_by_devarg(const char *devarg)\n+{\n+\tuint16_t port_id = RTE_MAX_ETHPORTS;\n+\tstruct rte_dev_iterator iter;\n+\n+\tif (rte_dev_probe(devarg) < 0) {\n+\t\tIFPX_LOG(ERR, \"Failed to create proxy port %s\\n\", devarg);\n+\t\treturn RTE_MAX_ETHPORTS;\n+\t}\n+\n+\tif (rte_eth_iterator_init(&iter, devarg) == 0) {\n+\t\tport_id = rte_eth_iterator_next(&iter);\n+\t\tif (port_id != RTE_MAX_ETHPORTS)\n+\t\t\trte_eth_iterator_cleanup(&iter);\n+\t}\n+\n+\treturn port_id;\n+}\n+\n+int ifpx_proxy_destroy(struct ifpx_proxy_node *px)\n+{\n+\tunsigned int i;\n+\tuint16_t proxy_id = px->proxy_id;\n+\n+\tTAILQ_REMOVE(&ifpx_proxies, px, elem);\n+\tfree(px);\n+\n+\t/* Clear any bindings for this proxy. */\n+\tfor (i = 0; i < RTE_DIM(ifpx_ports); ++i) {\n+\t\tif (ifpx_ports[i] == proxy_id) {\n+\t\t\tif (i == proxy_id) /* this entry is for proxy itself */\n+\t\t\t\tifpx_ports[i] = RTE_MAX_ETHPORTS;\n+\t\t\telse\n+\t\t\t\trte_ifpx_port_unbind(i);\n+\t\t}\n+\t}\n+\n+\treturn rte_dev_remove(rte_eth_devices[proxy_id].device);\n+}\n+\n+int rte_ifpx_proxy_destroy(uint16_t proxy_id)\n+{\n+\tstruct ifpx_proxy_node *px;\n+\tint ec = 0;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tTAILQ_FOREACH(px, &ifpx_proxies, elem) {\n+\t\tif (px->proxy_id != proxy_id)\n+\t\t\tcontinue;\n+\t}\n+\tif (!px) {\n+\t\tec = -EINVAL;\n+\t\tgoto exit;\n+\t}\n+\tif (px->state & IN_USE)\n+\t\tpx->state |= DEL_PENDING;\n+\telse\n+\t\tec = ifpx_proxy_destroy(px);\n+exit:\n+\trte_spinlock_unlock(&ifpx_lock);\n+\treturn ec;\n+}\n+\n+int rte_ifpx_queue_add(struct rte_ring *r)\n+{\n+\tstruct ifpx_queue_node *node;\n+\tint ec = 0;\n+\n+\tif (!r)\n+\t\treturn -EINVAL;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tTAILQ_FOREACH(node, &ifpx_queues, elem) {\n+\t\tif (node->r == r) {\n+\t\t\tec = -EEXIST;\n+\t\t\tgoto exit;\n+\t\t}\n+\t}\n+\n+\tnode = malloc(sizeof(*node));\n+\tif (!node) {\n+\t\tec = -ENOMEM;\n+\t\tgoto exit;\n+\t}\n+\n+\tnode->r = r;\n+\tTAILQ_INSERT_TAIL(&ifpx_queues, node, elem);\n+exit:\n+\trte_spinlock_unlock(&ifpx_lock);\n+\n+\treturn ec;\n+}\n+\n+int rte_ifpx_queue_remove(struct rte_ring *r)\n+{\n+\tstruct ifpx_queue_node *node, *next;\n+\tint ec = -EINVAL;\n+\n+\tif (!r)\n+\t\treturn ec;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tfor (node = TAILQ_FIRST(&ifpx_queues); node; node = next) {\n+\t\tnext = TAILQ_NEXT(node, elem);\n+\t\tif (node->r != r)\n+\t\t\tcontinue;\n+\t\tTAILQ_REMOVE(&ifpx_queues, node, elem);\n+\t\tfree(node);\n+\t\tec = 0;\n+\t\tbreak;\n+\t}\n+\trte_spinlock_unlock(&ifpx_lock);\n+\n+\treturn ec;\n+}\n+\n+int rte_ifpx_port_bind(uint16_t port_id, uint16_t proxy_id)\n+{\n+\tstruct rte_eth_dev_info proxy_eth_info;\n+\tstruct ifpx_proxy_node *px;\n+\tint ec;\n+\n+\tif (port_id >= RTE_MAX_ETHPORTS || proxy_id >= RTE_MAX_ETHPORTS ||\n+\t    /* port is a proxy */\n+\t    ifpx_ports[port_id] == port_id) {\n+\t\tIFPX_LOG(ERR, \"Invalid port_id: %d\", port_id);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Do automatic rebinding but issue a warning since this is not\n+\t * considered to be a valid behaviour.\n+\t */\n+\tif (ifpx_ports[port_id] != RTE_MAX_ETHPORTS) {\n+\t\tIFPX_LOG(WARNING, \"Port already bound: %d -> %d\", port_id,\n+\t\t\t ifpx_ports[port_id]);\n+\t}\n+\n+\t/* Search for existing proxy - if not found add one to the list. */\n+\trte_spinlock_lock(&ifpx_lock);\n+\tTAILQ_FOREACH(px, &ifpx_proxies, elem) {\n+\t\tif (px->proxy_id == proxy_id)\n+\t\t\tbreak;\n+\t}\n+\tif (!px) {\n+\t\tec = rte_eth_dev_info_get(proxy_id, &proxy_eth_info);\n+\t\tif (ec < 0 || proxy_eth_info.if_index == 0) {\n+\t\t\tIFPX_LOG(ERR, \"Invalid proxy: %d\", proxy_id);\n+\t\t\trte_spinlock_unlock(&ifpx_lock);\n+\t\t\treturn ec < 0 ? ec : -EINVAL;\n+\t\t}\n+\t\tpx = malloc(sizeof(*px));\n+\t\tif (!px) {\n+\t\t\trte_spinlock_unlock(&ifpx_lock);\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\t\tpx->proxy_id = proxy_id;\n+\t\tpx->info.if_index = proxy_eth_info.if_index;\n+\t\trte_eth_dev_get_mtu(proxy_id, &px->info.mtu);\n+\t\trte_eth_macaddr_get(proxy_id, &px->info.mac);\n+\t\tmemset(px->info.if_name, 0, sizeof(px->info.if_name));\n+\t\tTAILQ_INSERT_TAIL(&ifpx_proxies, px, elem);\n+\t\tifpx_ports[proxy_id] = proxy_id;\n+\t}\n+\trte_spinlock_unlock(&ifpx_lock);\n+\tifpx_ports[port_id] = proxy_id;\n+\n+\t/* Add proxy MAC to the port - since port will often just forward\n+\t * packets from the proxy/system they will be sent with proxy MAC as\n+\t * src.  In order to pass communication in other direction we should be\n+\t * accepting packets with proxy MAC as dst.\n+\t */\n+\trte_eth_dev_mac_addr_add(port_id, &px->info.mac, 0);\n+\n+\tif (ifpx_platform.get_info)\n+\t\tifpx_platform.get_info(px->info.if_index);\n+\n+\treturn 0;\n+}\n+\n+int rte_ifpx_port_unbind(uint16_t port_id)\n+{\n+\tif (port_id >= RTE_MAX_ETHPORTS ||\n+\t    ifpx_ports[port_id] == RTE_MAX_ETHPORTS ||\n+\t    /* port is a proxy */\n+\t    ifpx_ports[port_id] == port_id)\n+\t\treturn -EINVAL;\n+\n+\tifpx_ports[port_id] = RTE_MAX_ETHPORTS;\n+\t/* Proxy without any port bound is OK - that is the state of the proxy\n+\t * that has just been created, and it can still report routing\n+\t * information.  So we do not even check if this is the case.\n+\t */\n+\n+\treturn 0;\n+}\n+\n+int rte_ifpx_callbacks_register(const struct rte_ifpx_callbacks *cbs)\n+{\n+\tif (!cbs)\n+\t\treturn -EINVAL;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tifpx_callbacks.cbs = *cbs;\n+\trte_spinlock_unlock(&ifpx_lock);\n+\n+\treturn 0;\n+}\n+\n+void rte_ifpx_callbacks_unregister(void)\n+{\n+\trte_spinlock_lock(&ifpx_lock);\n+\tmemset(&ifpx_callbacks.cbs, 0, sizeof(ifpx_callbacks.cbs));\n+\trte_spinlock_unlock(&ifpx_lock);\n+}\n+\n+uint16_t rte_ifpx_proxy_get(uint16_t port_id)\n+{\n+\tif (port_id >= RTE_MAX_ETHPORTS)\n+\t\treturn RTE_MAX_ETHPORTS;\n+\n+\treturn ifpx_ports[port_id];\n+}\n+\n+unsigned int rte_ifpx_port_get(uint16_t proxy_id,\n+\t\t\t       uint16_t *ports, unsigned int num)\n+{\n+\tunsigned int p, cnt = 0;\n+\n+\tfor (p = 0; p < RTE_DIM(ifpx_ports); ++p) {\n+\t\tif (ifpx_ports[p] == proxy_id && ifpx_ports[p] != p) {\n+\t\t\t++cnt;\n+\t\t\tif (ports && num > 0) {\n+\t\t\t\t*ports++ = p;\n+\t\t\t\t--num;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn cnt;\n+}\n+\n+const struct rte_ifpx_info *rte_ifpx_info_get(uint16_t port_id)\n+{\n+\tstruct ifpx_proxy_node *px;\n+\n+\tif (port_id >= RTE_MAX_ETHPORTS ||\n+\t    ifpx_ports[port_id] == RTE_MAX_ETHPORTS)\n+\t\treturn NULL;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tTAILQ_FOREACH(px, &ifpx_proxies, elem) {\n+\t\tif (px->proxy_id == ifpx_ports[port_id])\n+\t\t\tbreak;\n+\t}\n+\trte_spinlock_unlock(&ifpx_lock);\n+\tRTE_ASSERT(px && \"Internal IF Proxy library error\");\n+\n+\treturn &px->info;\n+}\n+\n+static\n+void queue_event(const struct rte_ifpx_event *ev, struct rte_ring *r)\n+{\n+\tstruct rte_ifpx_event *e = malloc(sizeof(*ev));\n+\n+\tif (!e) {\n+\t\tIFPX_LOG(ERR, \"Failed to allocate event!\");\n+\t\treturn;\n+\t}\n+\tRTE_ASSERT(r);\n+\n+\t*e = *ev;\n+\trte_ring_sp_enqueue(r, e);\n+}\n+\n+void ifpx_notify_event(struct rte_ifpx_event *ev, struct ifpx_proxy_node *px)\n+{\n+\tstruct ifpx_queue_node *q;\n+\tint done = 0;\n+\tuint16_t p, proxy_id;\n+\n+\tif (px) {\n+\t\tif (px->state & DEL_PENDING)\n+\t\t\treturn;\n+\t\tproxy_id = px->proxy_id;\n+\t\tRTE_ASSERT(proxy_id != RTE_MAX_ETHPORTS);\n+\t\tpx->state |= IN_USE;\n+\t} else\n+\t\tproxy_id = RTE_MAX_ETHPORTS;\n+\n+\tRTE_ASSERT(ev);\n+\t/* This function is expected to be called with a lock held. */\n+\tRTE_ASSERT(rte_spinlock_trylock(&ifpx_lock) == 0);\n+\n+\tif (ifpx_callbacks.funcs[ev->type].f_ptr) {\n+\t\tunion cb_ptr_t cb = ifpx_callbacks.funcs[ev->type];\n+\n+\t\t/* Drop the lock for the time of callback call. */\n+\t\trte_spinlock_unlock(&ifpx_lock);\n+\t\tif (px) {\n+\t\t\tfor (p = 0; p < RTE_DIM(ifpx_ports); ++p) {\n+\t\t\t\tif (ifpx_ports[p] != proxy_id ||\n+\t\t\t\t    ifpx_ports[p] == p)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tev->data.port_id = p;\n+\t\t\t\tdone = cb.f_ptr(&ev->data) || done;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tRTE_ASSERT(ev->type == RTE_IFPX_CFG_DONE);\n+\t\t\tdone = cb.cfg_done();\n+\t\t}\n+\t\trte_spinlock_lock(&ifpx_lock);\n+\t}\n+\tif (done)\n+\t\tgoto exit;\n+\n+\t/* Event not \"consumed\" yet so try to notify via queues. */\n+\tTAILQ_FOREACH(q, &ifpx_queues, elem) {\n+\t\tif (px) {\n+\t\t\tfor (p = 0; p < RTE_DIM(ifpx_ports); ++p) {\n+\t\t\t\tif (ifpx_ports[p] != proxy_id ||\n+\t\t\t\t    ifpx_ports[p] == p)\n+\t\t\t\t\tcontinue;\n+\t\t\t\t/* Set the port_id - the remaining params should\n+\t\t\t\t * be filled before calling this function.\n+\t\t\t\t */\n+\t\t\t\tev->data.port_id = p;\n+\t\t\t\tqueue_event(ev, q->r);\n+\t\t\t}\n+\t\t} else\n+\t\t\tqueue_event(ev, q->r);\n+\t}\n+exit:\n+\tif (px)\n+\t\tpx->state &= ~IN_USE;\n+}\n+\n+void ifpx_cleanup_proxies(void)\n+{\n+\tstruct ifpx_proxy_node *px, *next;\n+\tfor (px = TAILQ_FIRST(&ifpx_proxies); px; px = next) {\n+\t\tnext = TAILQ_NEXT(px, elem);\n+\t\tif (px->state & DEL_PENDING)\n+\t\t\tifpx_proxy_destroy(px);\n+\t}\n+}\n+\n+int rte_ifpx_listen(void)\n+{\n+\tint ec;\n+\n+\tif (!ifpx_platform.listen)\n+\t\treturn -ENOTSUP;\n+\n+\tec = ifpx_platform.listen();\n+\tif (ec == 0 && ifpx_platform.get_info)\n+\t\tifpx_platform.get_info(0);\n+\n+\treturn ec;\n+}\n+\n+int rte_ifpx_close(void)\n+{\n+\tstruct ifpx_proxy_node *px;\n+\tstruct ifpx_queue_node *q;\n+\tunsigned int p;\n+\tint ec = 0;\n+\n+\tif (ifpx_platform.close) {\n+\t\tec = ifpx_platform.close();\n+\t\tif (ec != 0)\n+\t\t\tIFPX_LOG(ERR, \"Platform 'close' calback failed.\");\n+\t}\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\t/* Remove queues. */\n+\twhile (!TAILQ_EMPTY(&ifpx_queues)) {\n+\t\tq = TAILQ_FIRST(&ifpx_queues);\n+\t\tTAILQ_REMOVE(&ifpx_queues, q, elem);\n+\t\tfree(q);\n+\t}\n+\n+\t/* Clear callbacks. */\n+\tmemset(&ifpx_callbacks.cbs, 0, sizeof(ifpx_callbacks.cbs));\n+\n+\t/* Unbind ports. */\n+\tfor (p = 0; p < RTE_DIM(ifpx_ports); ++p) {\n+\t\tif (ifpx_ports[p] == RTE_MAX_ETHPORTS)\n+\t\t\tcontinue;\n+\t\tif (ifpx_ports[p] == p)\n+\t\t\t/* port is a proxy - just clear entry */\n+\t\t\tifpx_ports[p] = RTE_MAX_ETHPORTS;\n+\t\telse\n+\t\t\trte_ifpx_port_unbind(p);\n+\t}\n+\n+\t/* Clear proxies. */\n+\twhile (!TAILQ_EMPTY(&ifpx_proxies)) {\n+\t\tpx = TAILQ_FIRST(&ifpx_proxies);\n+\t\tTAILQ_REMOVE(&ifpx_proxies, px, elem);\n+\t\tfree(px);\n+\t}\n+\n+\trte_spinlock_unlock(&ifpx_lock);\n+\n+\treturn ec;\n+}\n+\n+RTE_INIT(if_proxy_init)\n+{\n+\tunsigned int i;\n+\tfor (i = 0; i < RTE_DIM(ifpx_ports); ++i)\n+\t\tifpx_ports[i] = RTE_MAX_ETHPORTS;\n+\n+\tifpx_log_type = rte_log_register(\"lib.if_proxy\");\n+\tif (ifpx_log_type >= 0)\n+\t\trte_log_set_level(ifpx_log_type, RTE_LOG_WARNING);\n+\n+\tif (ifpx_platform.init)\n+\t\tifpx_platform.init();\n+}\ndiff --git a/lib/librte_if_proxy/if_proxy_priv.h b/lib/librte_if_proxy/if_proxy_priv.h\nnew file mode 100644\nindex 000000000..dd7468891\n--- /dev/null\n+++ b/lib/librte_if_proxy/if_proxy_priv.h\n@@ -0,0 +1,97 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+#ifndef _IF_PROXY_PRIV_H_\n+#define _IF_PROXY_PRIV_H_\n+\n+#include <rte_if_proxy.h>\n+#include <rte_spinlock.h>\n+\n+extern int ifpx_log_type;\n+#define IFPX_LOG(level, fmt, args...) \\\n+\trte_log(RTE_LOG_ ## level, ifpx_log_type, \"%s(): \" fmt \"\\n\", \\\n+\t\t__func__, ##args)\n+\n+/* Table keeping mapping between port and their proxies. */\n+extern\n+uint16_t ifpx_ports[RTE_MAX_ETHPORTS];\n+\n+/* Callbacks and proxies are kept in linked lists.  Since this library is really\n+ * a slow/config path we guard them with a lock - and only one for all of them\n+ * should be enough.  We don't expect a need to protect other data structures -\n+ * e.g. data for given port is expected be accessed/modified from single thread.\n+ */\n+extern rte_spinlock_t ifpx_lock;\n+\n+enum ifpx_node_status {\n+\tIN_USE\t\t= 1U << 0,\n+\tDEL_PENDING\t= 1U << 1,\n+};\n+\n+/* List of configured proxies */\n+struct ifpx_proxy_node {\n+\tTAILQ_ENTRY(ifpx_proxy_node) elem;\n+\tuint16_t proxy_id;\n+\tuint16_t state;\n+\tstruct rte_ifpx_info info;\n+};\n+extern\n+TAILQ_HEAD(ifpx_proxies_head, ifpx_proxy_node) ifpx_proxies;\n+\n+/* This function should be called by the implementation whenever it notices\n+ * change in the network configuration.  The arguments are:\n+ * - ev : pointer to filled event data structure (all fields are expected to be\n+ *     filled, with the exception of 'port_id' for all proxy/port related\n+ *     events: this function clones the event notification for each bound port\n+ *     and fills 'port_id' appropriately).\n+ * - px : proxy node when given event is proxy/port related, otherwise pass NULL\n+ */\n+void ifpx_notify_event(struct rte_ifpx_event *ev, struct ifpx_proxy_node *px);\n+\n+/* This function should be called by the implementation whenever it is done with\n+ * notification about network configuration change.  It is only really needed\n+ * for the case of callback based API - from the callback user might to attempt\n+ * to remove callbacks/proxies.  Removing of callbacks is handled by the\n+ * ifpx_notify_event() function above, however only implementation really knows\n+ * when notification for given proxy is finished so it is a duty of it to call\n+ * this function to cleanup all proxies that has been marked for deletion.\n+ */\n+void ifpx_cleanup_proxies(void);\n+\n+/* This is the internal function removing the proxy from the list.  It is\n+ * related to the notification function above and intended to be used by the\n+ * platform implementation for the case of callback based API.\n+ * During notification via callback the internal lock is released so that\n+ * operation would not deadlock on an attempt to take a lock.  However\n+ * modification (destruction) is not really performed - instead the\n+ * callbacks/proxies are marked as \"to be deleted\".\n+ * Handling of callbacks that are \"to be deleted\" is done by the\n+ * ifpx_notify_event() function itself however it cannot delete the proxies (in\n+ * particular the proxy passed as an argument) since they might still be\n+ * referred by the calling function.  So it is a responsibility of the platform\n+ * implementation to check after calling notification function if there are any\n+ * proxies to be removed and use ifpx_proxy_destroy() to actually release them.\n+ */\n+int ifpx_proxy_destroy(struct ifpx_proxy_node *px);\n+\n+/* Every implementation should provide definition of this structure:\n+ * - init : called during library initialization (NULL when not needed)\n+ * - listen : this function should start service listening to the network\n+ *     configuration events/changes,\n+ * - close : this function should close the service started by listen()\n+ * - get_info : this function should query system for current configuration of\n+ *     interface with index 'if_index'.  After successful initialization of\n+ *     listening service this function is called with 0 as an argument.  In that\n+ *     case configuration of all ports should be obtained - and when this\n+ *     procedure completes a RTE_IFPX_CFG_DONE event should be signaled via\n+ *     ifpx_notify_event().\n+ */\n+extern\n+struct ifpx_platform_callbacks {\n+\tvoid (*init)(void);\n+\tint (*listen)(void);\n+\tint (*close)(void);\n+\tvoid (*get_info)(int if_index);\n+} ifpx_platform;\n+\n+#endif /* _IF_PROXY_PRIV_H_ */\ndiff --git a/lib/librte_if_proxy/linux/Makefile b/lib/librte_if_proxy/linux/Makefile\nnew file mode 100644\nindex 000000000..275b7e1e3\n--- /dev/null\n+++ b/lib/librte_if_proxy/linux/Makefile\n@@ -0,0 +1,4 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(C) 2020 Marvell International Ltd.\n+\n+SRCS += if_proxy.c\ndiff --git a/lib/librte_if_proxy/linux/if_proxy.c b/lib/librte_if_proxy/linux/if_proxy.c\nnew file mode 100644\nindex 000000000..0204505e3\n--- /dev/null\n+++ b/lib/librte_if_proxy/linux/if_proxy.c\n@@ -0,0 +1,550 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+#include <if_proxy_priv.h>\n+#include <rte_interrupts.h>\n+#include <rte_string_fns.h>\n+\n+#include <stdbool.h>\n+#include <unistd.h>\n+#include <errno.h>\n+#include <sys/socket.h>\n+#include <linux/rtnetlink.h>\n+#include <linux/if.h>\n+\n+static\n+struct rte_intr_handle ifpx_irq = {\n+\t.type = RTE_INTR_HANDLE_NETLINK,\n+\t.fd = -1,\n+};\n+\n+static\n+unsigned int ifpx_pid;\n+\n+static\n+int request_info(int type, int index)\n+{\n+\tstatic rte_spinlock_t send_lock = RTE_SPINLOCK_INITIALIZER;\n+\tstruct info_get {\n+\t\tstruct nlmsghdr h;\n+\t\tunion {\n+\t\t\tstruct ifinfomsg ifm;\n+\t\t\tstruct ifaddrmsg ifa;\n+\t\t\tstruct rtmsg rtm;\n+\t\t\tstruct ndmsg ndm;\n+\t\t} __rte_aligned(NLMSG_ALIGNTO);\n+\t} info_req;\n+\tint ret;\n+\n+\tmemset(&info_req, 0, sizeof(info_req));\n+\t/* First byte of these messages is family, so just make sure that this\n+\t * memset is enough to get all families.\n+\t */\n+\tRTE_ASSERT(AF_UNSPEC == 0);\n+\n+\tinfo_req.h.nlmsg_pid = ifpx_pid;\n+\tinfo_req.h.nlmsg_type = type;\n+\tinfo_req.h.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;\n+\tinfo_req.h.nlmsg_len = offsetof(struct info_get, ifm);\n+\n+\tswitch (type) {\n+\tcase RTM_GETLINK:\n+\t\tinfo_req.h.nlmsg_len += sizeof(info_req.ifm);\n+\t\tinfo_req.ifm.ifi_index = index;\n+\t\tbreak;\n+\tcase RTM_GETADDR:\n+\t\tinfo_req.h.nlmsg_len += sizeof(info_req.ifa);\n+\t\tinfo_req.ifa.ifa_index = index;\n+\t\tbreak;\n+\tcase RTM_GETROUTE:\n+\t\tinfo_req.h.nlmsg_len += sizeof(info_req.rtm);\n+\t\tbreak;\n+\tcase RTM_GETNEIGH:\n+\t\tinfo_req.h.nlmsg_len += sizeof(info_req.ndm);\n+\t\tbreak;\n+\tdefault:\n+\t\tIFPX_LOG(WARNING, \"Unhandled message type: %d\", type);\n+\t\treturn -EINVAL;\n+\t}\n+\t/* Store request type (and if it is global or link specific) in 'seq'.\n+\t * Later it is used during handling of reply to continue requesting of\n+\t * information dump from system - if needed.\n+\t */\n+\tinfo_req.h.nlmsg_seq = index << 8 | type;\n+\n+\tIFPX_LOG(DEBUG, \"\\tRequesting msg %d for: %u\", type, index);\n+\n+\trte_spinlock_lock(&send_lock);\n+\tret = send(ifpx_irq.fd, &info_req, info_req.h.nlmsg_len, 0);\n+\tif (ret < 0) {\n+\t\tIFPX_LOG(ERR, \"Failed to send netlink msg: %d\", errno);\n+\t\trte_errno = errno;\n+\t}\n+\trte_spinlock_unlock(&send_lock);\n+\n+\treturn ret;\n+}\n+\n+static\n+void handle_link(const struct nlmsghdr *h)\n+{\n+\tconst struct ifinfomsg *ifi = NLMSG_DATA(h);\n+\tint alen = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));\n+\tconst struct rtattr *attrs[IFLA_MAX+1] = { NULL };\n+\tconst struct rtattr *attr;\n+\tstruct ifpx_proxy_node *px;\n+\tstruct rte_ifpx_event ev;\n+\n+\tIFPX_LOG(DEBUG, \"\\tLink action (%u): %u, 0x%x/0x%x (flags/changed)\",\n+\t\t ifi->ifi_index, h->nlmsg_type, ifi->ifi_flags,\n+\t\t ifi->ifi_change);\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tTAILQ_FOREACH(px, &ifpx_proxies, elem) {\n+\t\tif (px->info.if_index == (unsigned int)ifi->ifi_index)\n+\t\t\tbreak;\n+\t}\n+\n+\t/* Drop messages that are not associated with any proxy */\n+\tif (!px)\n+\t\tgoto exit;\n+\t/* When message is a reply to request for specific interface then keep\n+\t * it only when it contains info for this interface.\n+\t */\n+\tif (h->nlmsg_pid == ifpx_pid && h->nlmsg_seq >> 8 &&\n+\t    (h->nlmsg_seq >> 8) != (unsigned int)ifi->ifi_index)\n+\t\tgoto exit;\n+\n+\tfor (attr = IFLA_RTA(ifi); RTA_OK(attr, alen);\n+\t\t\t\t   attr = RTA_NEXT(attr, alen)) {\n+\t\tif (attr->rta_type > IFLA_MAX)\n+\t\t\tcontinue;\n+\t\tattrs[attr->rta_type] = attr;\n+\t}\n+\n+\tif (ifi->ifi_change & IFF_UP) {\n+\t\tev.type = RTE_IFPX_LINK_CHANGE;\n+\t\tev.link_change.is_up = ifi->ifi_flags & IFF_UP;\n+\t\tifpx_notify_event(&ev, px);\n+\t}\n+\tif (attrs[IFLA_MTU]) {\n+\t\tuint16_t mtu = *(const int *)RTA_DATA(attrs[IFLA_MTU]);\n+\t\tif (mtu != px->info.mtu) {\n+\t\t\tpx->info.mtu = mtu;\n+\t\t\tev.type = RTE_IFPX_MTU_CHANGE;\n+\t\t\tev.mtu_change.mtu = mtu;\n+\t\t\tifpx_notify_event(&ev, px);\n+\t\t}\n+\t}\n+\tif (attrs[IFLA_ADDRESS]) {\n+\t\tconst struct rte_ether_addr *mac =\n+\t\t\t\tRTA_DATA(attrs[IFLA_ADDRESS]);\n+\n+\t\tRTE_ASSERT(RTA_PAYLOAD(attrs[IFLA_ADDRESS]) ==\n+\t\t\t   RTE_ETHER_ADDR_LEN);\n+\t\tif (memcmp(mac, &px->info.mac, RTE_ETHER_ADDR_LEN) != 0) {\n+\t\t\trte_ether_addr_copy(mac, &px->info.mac);\n+\t\t\tev.type = RTE_IFPX_MAC_CHANGE;\n+\t\t\trte_ether_addr_copy(mac, &ev.mac_change.mac);\n+\t\t\tifpx_notify_event(&ev, px);\n+\t\t}\n+\t}\n+\tif (h->nlmsg_pid == ifpx_pid) {\n+\t\tRTE_ASSERT((h->nlmsg_seq & 0xFF) == RTM_GETLINK);\n+\t\t/* If this is reply for specific link request (not initial\n+\t\t * global dump) then follow up with address request, otherwise\n+\t\t * just store the interface name.\n+\t\t */\n+\t\tif (h->nlmsg_seq >> 8)\n+\t\t\trequest_info(RTM_GETADDR, ifi->ifi_index);\n+\t\telse if (!px->info.if_name[0] && attrs[IFLA_IFNAME])\n+\t\t\tstrlcpy(px->info.if_name, RTA_DATA(attrs[IFLA_IFNAME]),\n+\t\t\t\tsizeof(px->info.if_name));\n+\t}\n+\n+\tifpx_cleanup_proxies();\n+exit:\n+\trte_spinlock_unlock(&ifpx_lock);\n+}\n+\n+static\n+void handle_addr(const struct nlmsghdr *h, bool needs_del)\n+{\n+\tconst struct ifaddrmsg *ifa = NLMSG_DATA(h);\n+\tint alen = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));\n+\tconst struct rtattr *attrs[IFA_MAX+1] = { NULL };\n+\tconst struct rtattr *attr;\n+\tstruct ifpx_proxy_node *px;\n+\tstruct rte_ifpx_event ev;\n+\tconst uint8_t *ip;\n+\n+\tIFPX_LOG(DEBUG, \"\\tAddr action (%u): %u, family: %u\",\n+\t\t ifa->ifa_index, h->nlmsg_type, ifa->ifa_family);\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tTAILQ_FOREACH(px, &ifpx_proxies, elem) {\n+\t\tif (px->info.if_index == ifa->ifa_index)\n+\t\t\tbreak;\n+\t}\n+\n+\t/* Drop messages that are not associated with any proxy */\n+\tif (!px)\n+\t\tgoto exit;\n+\t/* When message is a reply to request for specific interface then keep\n+\t * it only when it contains info for this interface.\n+\t */\n+\tif (h->nlmsg_pid == ifpx_pid && h->nlmsg_seq >> 8 &&\n+\t    (h->nlmsg_seq >> 8) != ifa->ifa_index)\n+\t\tgoto exit;\n+\n+\tfor (attr = IFA_RTA(ifa); RTA_OK(attr, alen);\n+\t\t\t\t  attr = RTA_NEXT(attr, alen)) {\n+\t\tif (attr->rta_type > IFA_MAX)\n+\t\t\tcontinue;\n+\t\tattrs[attr->rta_type] = attr;\n+\t}\n+\n+\tif (attrs[IFA_ADDRESS]) {\n+\t\tip = RTA_DATA(attrs[IFA_ADDRESS]);\n+\t\tif (ifa->ifa_family == AF_INET) {\n+\t\t\tev.type = needs_del ? RTE_IFPX_ADDR_DEL\n+\t\t\t\t\t    : RTE_IFPX_ADDR_ADD;\n+\t\t\tev.addr_change.ip =\n+\t\t\t\t\tRTE_IPV4(ip[0], ip[1], ip[2], ip[3]);\n+\t\t} else {\n+\t\t\tev.type = needs_del ? RTE_IFPX_ADDR6_DEL\n+\t\t\t\t\t    : RTE_IFPX_ADDR6_ADD;\n+\t\t\tmemcpy(ev.addr6_change.ip, ip, 16);\n+\t\t}\n+\t\tifpx_notify_event(&ev, px);\n+\t\tifpx_cleanup_proxies();\n+\t}\n+exit:\n+\trte_spinlock_unlock(&ifpx_lock);\n+}\n+\n+static\n+void handle_route(const struct nlmsghdr *h, bool needs_del)\n+{\n+\tconst struct rtmsg *r = NLMSG_DATA(h);\n+\tint alen = h->nlmsg_len - NLMSG_LENGTH(sizeof(*r));\n+\tconst struct rtattr *attrs[RTA_MAX+1] = { NULL };\n+\tconst struct rtattr *attr;\n+\tstruct rte_ifpx_event ev;\n+\tstruct ifpx_proxy_node *px = NULL;\n+\tconst uint8_t *ip;\n+\n+\tIFPX_LOG(DEBUG, \"\\tRoute action: %u, family: %u\",\n+\t\t h->nlmsg_type, r->rtm_family);\n+\n+\tfor (attr = RTM_RTA(r); RTA_OK(attr, alen);\n+\t\t\t\tattr = RTA_NEXT(attr, alen)) {\n+\t\tif (attr->rta_type > RTA_MAX)\n+\t\t\tcontinue;\n+\t\tattrs[attr->rta_type] = attr;\n+\t}\n+\n+\tmemset(&ev, 0, sizeof(ev));\n+\tev.type = RTE_IFPX_NUM_EVENTS;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tif (attrs[RTA_OIF]) {\n+\t\tint if_index = *((int32_t *)RTA_DATA(attrs[RTA_OIF]));\n+\n+\t\tif (if_index > 0) {\n+\t\t\tTAILQ_FOREACH(px, &ifpx_proxies, elem) {\n+\t\t\t\tif (px->info.if_index == (uint32_t)if_index)\n+\t\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\t/* We are only interested in routes related to the proxy interfaces and\n+\t * we need to have dst - otherwise skip the message.\n+\t */\n+\tif (!px || !attrs[RTA_DST])\n+\t\tgoto exit;\n+\n+\tip = RTA_DATA(attrs[RTA_DST]);\n+\t/* This is common to both IPv4/6. */\n+\tev.route_change.depth = r->rtm_dst_len;\n+\tif (r->rtm_family == AF_INET) {\n+\t\tev.type = needs_del ? RTE_IFPX_ROUTE_DEL\n+\t\t\t\t    : RTE_IFPX_ROUTE_ADD;\n+\t\tev.route_change.ip = RTE_IPV4(ip[0], ip[1], ip[2], ip[3]);\n+\t} else {\n+\t\tev.type = needs_del ? RTE_IFPX_ROUTE6_DEL\n+\t\t\t\t    : RTE_IFPX_ROUTE6_ADD;\n+\t\tmemcpy(ev.route6_change.ip, ip, 16);\n+\t}\n+\tif (attrs[RTA_GATEWAY]) {\n+\t\tip = RTA_DATA(attrs[RTA_GATEWAY]);\n+\t\tif (r->rtm_family == AF_INET)\n+\t\t\tev.route_change.gateway =\n+\t\t\t\t\tRTE_IPV4(ip[0], ip[1], ip[2], ip[3]);\n+\t\telse\n+\t\t\tmemcpy(ev.route6_change.gateway, ip, 16);\n+\t}\n+\n+\tifpx_notify_event(&ev, px);\n+\t/* Let's check for proxies to remove here too - just in case somebody\n+\t * removed the non-proxy related callback.\n+\t */\n+\tifpx_cleanup_proxies();\n+exit:\n+\trte_spinlock_unlock(&ifpx_lock);\n+}\n+\n+/* Link, addr and route related messages seem to have this macro defined but not\n+ * neighbour one.  Define one if it is missing - const qualifiers added just to\n+ * silence compiler - for some reason it is not needed in equivalent macros for\n+ * other messages and here compiler is complaining about (char*) cast on pointer\n+ * to const.\n+ */\n+#ifndef NDA_RTA\n+#define NDA_RTA(r) ((const struct rtattr *)(((const char *)(r)) + \\\n+\t\t\tNLMSG_ALIGN(sizeof(struct ndmsg))))\n+#endif\n+\n+static\n+void handle_neigh(const struct nlmsghdr *h, bool needs_del)\n+{\n+\tconst struct ndmsg *n = NLMSG_DATA(h);\n+\tint alen = h->nlmsg_len - NLMSG_LENGTH(sizeof(*n));\n+\tconst struct rtattr *attrs[NDA_MAX+1] = { NULL };\n+\tconst struct rtattr *attr;\n+\tstruct ifpx_proxy_node *px;\n+\tstruct rte_ifpx_event ev;\n+\tconst uint8_t *ip;\n+\n+\tIFPX_LOG(DEBUG, \"\\tNeighbour action: %u, family: %u, state: %u, if: %d\",\n+\t\t h->nlmsg_type, n->ndm_family, n->ndm_state, n->ndm_ifindex);\n+\n+\tfor (attr = NDA_RTA(n); RTA_OK(attr, alen);\n+\t\t\t\tattr = RTA_NEXT(attr, alen)) {\n+\t\tif (attr->rta_type > NDA_MAX)\n+\t\t\tcontinue;\n+\t\tattrs[attr->rta_type] = attr;\n+\t}\n+\n+\tmemset(&ev, 0, sizeof(ev));\n+\tev.type = RTE_IFPX_NUM_EVENTS;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tTAILQ_FOREACH(px, &ifpx_proxies, elem) {\n+\t\tif (px->info.if_index == (unsigned int)n->ndm_ifindex)\n+\t\t\tbreak;\n+\t}\n+\t/* We need only subset of neighbourhood related to proxy interfaces.\n+\t * lladdr seems to be needed only for adding new entry - modifications\n+\t * (also reported via RTM_NEWLINK) and deletion include only dst.\n+\t */\n+\tif (!px || !attrs[NDA_DST] || (!needs_del && !attrs[NDA_LLADDR]))\n+\t\tgoto exit;\n+\n+\tip = RTA_DATA(attrs[NDA_DST]);\n+\tif (n->ndm_family == AF_INET) {\n+\t\tev.type = needs_del ? RTE_IFPX_NEIGH_DEL\n+\t\t\t\t    : RTE_IFPX_NEIGH_ADD;\n+\t\tev.neigh_change.ip = RTE_IPV4(ip[0], ip[1], ip[2], ip[3]);\n+\t} else {\n+\t\tev.type = needs_del ? RTE_IFPX_NEIGH6_DEL\n+\t\t\t\t    : RTE_IFPX_NEIGH6_ADD;\n+\t\tmemcpy(ev.neigh6_change.ip, ip, 16);\n+\t}\n+\tif (attrs[NDA_LLADDR])\n+\t\trte_ether_addr_copy(RTA_DATA(attrs[NDA_LLADDR]),\n+\t\t\t\t    &ev.neigh_change.mac);\n+\n+\tifpx_notify_event(&ev, px);\n+\t/* Let's check for proxies to remove here too - just in case somebody\n+\t * removed the non-proxy related callback.\n+\t */\n+\tifpx_cleanup_proxies();\n+exit:\n+\trte_spinlock_unlock(&ifpx_lock);\n+}\n+\n+static\n+void if_proxy_intr_callback(void *arg __rte_unused)\n+{\n+\tstruct nlmsghdr *h;\n+\tstruct sockaddr_nl addr;\n+\tsocklen_t addr_len;\n+\tchar buf[8192];\n+\tssize_t len;\n+\n+restart:\n+\tlen = recvfrom(ifpx_irq.fd, buf, sizeof(buf), 0,\n+\t\t       (struct sockaddr *)&addr, &addr_len);\n+\tif (len < 0) {\n+\t\tif (errno == EINTR) {\n+\t\t\tIFPX_LOG(DEBUG, \"recvmsg() interrupted\");\n+\t\t\tgoto restart;\n+\t\t}\n+\t\tIFPX_LOG(ERR, \"Failed to read netlink msg: %ld (errno %d)\",\n+\t\t\t len, errno);\n+\t\treturn;\n+\t}\n+\tif (addr_len != sizeof(addr)) {\n+\t\tIFPX_LOG(ERR, \"Invalid netlink addr size: %d\", addr_len);\n+\t\treturn;\n+\t}\n+\tIFPX_LOG(DEBUG, \"Read %lu bytes (buf %lu) from %u/%u\", len,\n+\t\t sizeof(buf), addr.nl_pid, addr.nl_groups);\n+\n+\tfor (h = (struct nlmsghdr *)buf; NLMSG_OK(h, len);\n+\t\t\t\t\t h = NLMSG_NEXT(h, len)) {\n+\t\tIFPX_LOG(DEBUG, \"Recv msg: %u (%u/%u/%u seq/flags/pid)\",\n+\t\t\t h->nlmsg_type, h->nlmsg_seq, h->nlmsg_flags,\n+\t\t\t h->nlmsg_pid);\n+\n+\t\tswitch (h->nlmsg_type) {\n+\t\tcase RTM_NEWLINK:\n+\t\tcase RTM_DELLINK:\n+\t\t\thandle_link(h);\n+\t\t\tbreak;\n+\t\tcase RTM_NEWADDR:\n+\t\tcase RTM_DELADDR:\n+\t\t\thandle_addr(h, h->nlmsg_type == RTM_DELADDR);\n+\t\t\tbreak;\n+\t\tcase RTM_NEWROUTE:\n+\t\tcase RTM_DELROUTE:\n+\t\t\thandle_route(h, h->nlmsg_type == RTM_DELROUTE);\n+\t\t\tbreak;\n+\t\tcase RTM_NEWNEIGH:\n+\t\tcase RTM_DELNEIGH:\n+\t\t\thandle_neigh(h, h->nlmsg_type == RTM_DELNEIGH);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* If this is a reply for global request then follow up with\n+\t\t * additional requests and notify about finish.\n+\t\t */\n+\t\tif (h->nlmsg_pid == ifpx_pid && (h->nlmsg_seq >> 8) == 0 &&\n+\t\t    h->nlmsg_type == NLMSG_DONE) {\n+\t\t\tif ((h->nlmsg_seq & 0xFF) == RTM_GETLINK)\n+\t\t\t\trequest_info(RTM_GETADDR, 0);\n+\t\t\telse if ((h->nlmsg_seq & 0xFF) == RTM_GETADDR)\n+\t\t\t\trequest_info(RTM_GETROUTE, 0);\n+\t\t\telse if ((h->nlmsg_seq & 0xFF) == RTM_GETROUTE)\n+\t\t\t\trequest_info(RTM_GETNEIGH, 0);\n+\t\t\telse {\n+\t\t\t\tstruct rte_ifpx_event ev = {\n+\t\t\t\t\t.type = RTE_IFPX_CFG_DONE\n+\t\t\t\t};\n+\n+\t\t\t\tRTE_ASSERT((h->nlmsg_seq & 0xFF) ==\n+\t\t\t\t\t\tRTM_GETNEIGH);\n+\t\t\t\trte_spinlock_lock(&ifpx_lock);\n+\t\t\t\tifpx_notify_event(&ev, NULL);\n+\t\t\t\trte_spinlock_unlock(&ifpx_lock);\n+\t\t\t}\n+\t\t}\n+\t}\n+\tIFPX_LOG(DEBUG, \"Finished msg loop: %ld bytes left\", len);\n+}\n+\n+static\n+int nlink_listen(void)\n+{\n+\tstruct sockaddr_nl addr = {\n+\t\t.nl_family = AF_NETLINK,\n+\t\t.nl_pid = 0,\n+\t};\n+\tsocklen_t addr_len = sizeof(addr);\n+\tint ret;\n+\n+\tif (ifpx_irq.fd != -1) {\n+\t\trte_errno = EBUSY;\n+\t\treturn -1;\n+\t}\n+\n+\taddr.nl_groups = 1 << (RTNLGRP_LINK-1)\n+\t\t\t| 1 << (RTNLGRP_NEIGH-1)\n+\t\t\t| 1 << (RTNLGRP_IPV4_IFADDR-1)\n+\t\t\t| 1 << (RTNLGRP_IPV6_IFADDR-1)\n+\t\t\t| 1 << (RTNLGRP_IPV4_ROUTE-1)\n+\t\t\t| 1 << (RTNLGRP_IPV6_ROUTE-1);\n+\n+\tifpx_irq.fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC,\n+\t\t\t\t NETLINK_ROUTE);\n+\tif (ifpx_irq.fd == -1) {\n+\t\tIFPX_LOG(ERR, \"Failed to create netlink socket: %d\", errno);\n+\t\tgoto error;\n+\t}\n+\t/* Starting with kernel 4.19 you can request dump for a specific\n+\t * interface and kernel will filter out and send only relevant info.\n+\t * Otherwise NLM_F_DUMP will generate info for all interfaces and you\n+\t * need to filter them yourself.\n+\t */\n+#ifdef NETLINK_DUMP_STRICT_CHK\n+\tret = 1; /* use this var also as an input param */\n+\tret = setsockopt(ifpx_irq.fd, SOL_SOCKET, NETLINK_DUMP_STRICT_CHK,\n+\t\t\t &ret, sizeof(ret));\n+\tif (ret < 0) {\n+\t\tIFPX_LOG(ERR, \"Failed to set socket option: %d\", errno);\n+\t\tgoto error;\n+\t}\n+#endif\n+\n+\tret = bind(ifpx_irq.fd, (struct sockaddr *)&addr, addr_len);\n+\tif (ret < 0) {\n+\t\tIFPX_LOG(ERR, \"Failed to bind socket: %d\", errno);\n+\t\tgoto error;\n+\t}\n+\tret = getsockname(ifpx_irq.fd, (struct sockaddr *)&addr, &addr_len);\n+\tif (ret < 0) {\n+\t\tIFPX_LOG(ERR, \"Failed to get socket addr: %d\", errno);\n+\t\tgoto error;\n+\t} else {\n+\t\tifpx_pid = addr.nl_pid;\n+\t\tIFPX_LOG(DEBUG, \"Assigned port ID: %u\", addr.nl_pid);\n+\t}\n+\n+\tret = rte_intr_callback_register(&ifpx_irq, if_proxy_intr_callback,\n+\t\t\t\t\t NULL);\n+\tif (ret == 0)\n+\t\treturn 0;\n+\n+error:\n+\trte_errno = errno;\n+\tif (ifpx_irq.fd != -1) {\n+\t\tclose(ifpx_irq.fd);\n+\t\tifpx_irq.fd = -1;\n+\t}\n+\treturn -1;\n+}\n+\n+static\n+int nlink_close(void)\n+{\n+\tint ec;\n+\n+\tif (ifpx_irq.fd < 0)\n+\t\treturn -EBADFD;\n+\n+\tdo\n+\t\tec = rte_intr_callback_unregister(&ifpx_irq,\n+\t\t\t\t\t\t  if_proxy_intr_callback, NULL);\n+\twhile (ec == -EAGAIN); /* unlikely but possible - at least I think so */\n+\n+\tclose(ifpx_irq.fd);\n+\tifpx_irq.fd = -1;\n+\tifpx_pid = 0;\n+\n+\treturn 0;\n+}\n+\n+static\n+void nlink_get_info(int if_index)\n+{\n+\tif (ifpx_irq.fd != -1)\n+\t\trequest_info(RTM_GETLINK, if_index);\n+}\n+\n+struct ifpx_platform_callbacks ifpx_platform = {\n+\t.init = NULL,\n+\t.listen = nlink_listen,\n+\t.close = nlink_close,\n+\t.get_info = nlink_get_info,\n+};\ndiff --git a/lib/librte_if_proxy/meson.build b/lib/librte_if_proxy/meson.build\nnew file mode 100644\nindex 000000000..f0c1a6e15\n--- /dev/null\n+++ b/lib/librte_if_proxy/meson.build\n@@ -0,0 +1,19 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(C) 2020 Marvell International Ltd.\n+\n+# Currently only implemented on Linux\n+if not is_linux\n+\tbuild = false\n+\treason = 'only supported on linux'\n+endif\n+\n+version = 1\n+allow_experimental_apis = true\n+\n+deps += ['ethdev']\n+sources = files('if_proxy_common.c')\n+headers = files('rte_if_proxy.h')\n+\n+if is_linux\n+\tsources += files('linux/if_proxy.c')\n+endif\ndiff --git a/lib/librte_if_proxy/rte_if_proxy.h b/lib/librte_if_proxy/rte_if_proxy.h\nnew file mode 100644\nindex 000000000..70f701719\n--- /dev/null\n+++ b/lib/librte_if_proxy/rte_if_proxy.h\n@@ -0,0 +1,561 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#ifndef _RTE_IF_PROXY_H_\n+#define _RTE_IF_PROXY_H_\n+\n+/**\n+ * @file\n+ * RTE IF Proxy library\n+ *\n+ * The IF Proxy library allows for monitoring of system network configuration\n+ * and configuration of DPDK ports by using usual system utilities (like the\n+ * ones from iproute2 package).\n+ *\n+ * It is based on the notion of \"proxy interface\" which actually can be any DPDK\n+ * port which is also visible to the system - that is it has non-zero 'if_index'\n+ * field in 'rte_eth_dev_info' structure.\n+ *\n+ * If application doesn't have any such port (or doesn't want to use it for\n+ * proxy) it can create one by calling:\n+ *\n+ *   proxy_id = rte_ifpx_create(RTE_IFPX_DEFAULT);\n+ *\n+ * This function is just a wrapper that constructs valid 'devargs' string based\n+ * on the proxy type chosen (currently Tap or KNI) and creates the interface by\n+ * calling rte_ifpx_dev_create().\n+ *\n+ * Once one has DPDK port capable of being proxy one can bind target DPDK port\n+ * to it by calling.\n+ *\n+ *   rte_ifpx_port_bind(port_id, proxy_id);\n+ *\n+ * This binding is a logical one - there is no automatic packet forwarding\n+ * between port and it's proxy since the library doesn't know the structure of\n+ * application's packet processing.  It remains application responsibility to\n+ * forward the packets from/to proxy port (by calling the usual DPDK RX/TX burst\n+ * API).  However when the library notes some change to the proxy interface it\n+ * will simply call appropriate callback with 'port_id' of the DPDK port that is\n+ * bound to this proxy interface.  The binding can be 1 to many - that is many\n+ * ports can point to one proxy - in that case registered callbacks will be\n+ * called for every bound port.\n+ *\n+ * The callbacks that are used for notifications are described by the\n+ * 'rte_ifpx_callbacks' structure and they are registered by calling:\n+ *\n+ *   rte_ifpx_callbacks_register(&cbs);\n+ *\n+ * Finally the application should call:\n+ *\n+ *   rte_ifpx_listen();\n+ *\n+ * which will query system for present network configuration and start listening\n+ * to its changes.\n+ */\n+\n+#include <rte_eal.h>\n+#include <rte_ethdev.h>\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+/**\n+ * Enum naming the type of proxy to create.\n+ *\n+ * @see rte_ifpx_create()\n+ */\n+enum rte_ifpx_proxy_type {\n+\tRTE_IFPX_DEFAULT,\t/**< Use default proxy type for given arch. */\n+\tRTE_IFPX_TAP,\t\t/**< Use Tap based port for proxy. */\n+\tRTE_IFPX_KNI\t\t/**< Use KNI based port for proxy. */\n+};\n+\n+/**\n+ * Create DPDK port that can serve as an interface proxy.\n+ *\n+ * This function is just a wrapper around rte_ifpx_create_by_devarg() that\n+ * constructs its 'devarg' argument based on type of proxy requested.\n+ *\n+ * @param type\n+ *   A type of proxy to create.\n+ *\n+ * @return\n+ *   DPDK port id on success, RTE_MAX_ETHPORTS otherwise.\n+ *\n+ * @see enum rte_ifpx_type\n+ * @see rte_ifpx_create_by_devarg()\n+ */\n+__rte_experimental\n+uint16_t rte_ifpx_proxy_create(enum rte_ifpx_proxy_type type);\n+\n+/**\n+ * Create DPDK port that can serve as an interface proxy.\n+ *\n+ * @param devarg\n+ *   A string passed to rte_dev_probe() to create proxy port.\n+ *\n+ * @return\n+ *   DPDK port id on success, RTE_MAX_ETHPORTS otherwise.\n+ */\n+__rte_experimental\n+uint16_t rte_ifpx_proxy_create_by_devarg(const char *devarg);\n+\n+/**\n+ * Remove DPDK proxy port.\n+ *\n+ * In addition to removing the proxy port the bindings (if any) are cleared.\n+ *\n+ * @param proxy_id\n+ *   Port id of the proxy that should be removed.\n+ *\n+ * @return\n+ *   0 on success, negative on error.\n+ */\n+__rte_experimental\n+int rte_ifpx_proxy_destroy(uint16_t proxy_id);\n+\n+/**\n+ * The rte_ifpx_event_type enum lists all possible event types that can be\n+ * signaled by this library.  To learn what events are supported on your\n+ * platform call rte_ifpx_events_available().\n+ *\n+ * NOTE - do not reorder these enums freely, their values need to correspond to\n+ * the order of the callbacks in struct rte_ifpx_callbacks.\n+ */\n+enum rte_ifpx_event_type {\n+\tRTE_IFPX_MAC_CHANGE,  /**< @see struct rte_ifpx_mac_change */\n+\tRTE_IFPX_MTU_CHANGE,  /**< @see struct rte_ifpx_mtu_change */\n+\tRTE_IFPX_LINK_CHANGE, /**< @see struct rte_ifpx_link_change */\n+\tRTE_IFPX_ADDR_ADD,    /**< @see struct rte_ifpx_addr_change */\n+\tRTE_IFPX_ADDR_DEL,    /**< @see struct rte_ifpx_addr_change */\n+\tRTE_IFPX_ADDR6_ADD,   /**< @see struct rte_ifpx_addr6_change */\n+\tRTE_IFPX_ADDR6_DEL,   /**< @see struct rte_ifpx_addr6_change */\n+\tRTE_IFPX_ROUTE_ADD,   /**< @see struct rte_ifpx_route_change */\n+\tRTE_IFPX_ROUTE_DEL,   /**< @see struct rte_ifpx_route_change */\n+\tRTE_IFPX_ROUTE6_ADD,  /**< @see struct rte_ifpx_route6_change */\n+\tRTE_IFPX_ROUTE6_DEL,  /**< @see struct rte_ifpx_route6_change */\n+\tRTE_IFPX_NEIGH_ADD,   /**< @see struct rte_ifpx_neigh_change */\n+\tRTE_IFPX_NEIGH_DEL,   /**< @see struct rte_ifpx_neigh_change */\n+\tRTE_IFPX_NEIGH6_ADD,  /**< @see struct rte_ifpx_neigh6_change */\n+\tRTE_IFPX_NEIGH6_DEL,  /**< @see struct rte_ifpx_neigh6_change */\n+\tRTE_IFPX_CFG_DONE,    /**< This event is a lib specific event - it is\n+\t\t\t       * signaled when initial network configuration\n+\t\t\t       * query is finished and has no event data.\n+\t\t\t       */\n+\tRTE_IFPX_NUM_EVENTS,\n+};\n+\n+/**\n+ * Get the bit mask of implemented events/callbacks for this platform.\n+ *\n+ * @return\n+ *   Bit mask of events/callbacks implemented: each event type can be tested by\n+ *   checking bit (1 << ev) where 'ev' is one of the rte_ifpx_event_type enum\n+ *   values.\n+ * @see enum rte_ifpx_event_type\n+ */\n+__rte_experimental\n+uint64_t rte_ifpx_events_available(void);\n+\n+/**\n+ * The rte_ifpx_event defines structure used to pass notification event to\n+ * application.  Each event type has its own dedicated inner structure - these\n+ * structures are also used when using callbacks notifications.\n+ */\n+struct rte_ifpx_event {\n+\tenum rte_ifpx_event_type type;\n+\tunion {\n+\t\t/** Structure used to pass notification about MAC change of the\n+\t\t * proxy interface.\n+\t\t * @see RTE_IFPX_MAC_CHANGE\n+\t\t */\n+\t\tstruct rte_ifpx_mac_change {\n+\t\t\tuint16_t port_id;\n+\t\t\tstruct rte_ether_addr mac;\n+\t\t} mac_change;\n+\t\t/** Structure used to pass notification about MTU change.\n+\t\t * @see RTE_IFPX_MTU_CHANGE\n+\t\t */\n+\t\tstruct rte_ifpx_mtu_change {\n+\t\t\tuint16_t port_id;\n+\t\t\tuint16_t mtu;\n+\t\t} mtu_change;\n+\t\t/** Structure used to pass notification about link going\n+\t\t * up/down.\n+\t\t * @see RTE_IFPX_LINK_CHANGE\n+\t\t */\n+\t\tstruct rte_ifpx_link_change {\n+\t\t\tuint16_t port_id;\n+\t\t\tint is_up;\n+\t\t} link_change;\n+\t\t/** Structure used to pass notification about IPv4 address being\n+\t\t * added/removed.  All IPv4 addresses reported by this library\n+\t\t * are in host order.\n+\t\t * @see RTE_IFPX_ADDR_ADD\n+\t\t * @see RTE_IFPX_ADDR_DEL\n+\t\t */\n+\t\tstruct rte_ifpx_addr_change {\n+\t\t\tuint16_t port_id;\n+\t\t\tuint32_t ip;\n+\t\t} addr_change;\n+\t\t/** Structure used to pass notification about IPv6 address being\n+\t\t * added/removed.\n+\t\t * @see RTE_IFPX_ADDR6_ADD\n+\t\t * @see RTE_IFPX_ADDR6_DEL\n+\t\t */\n+\t\tstruct rte_ifpx_addr6_change {\n+\t\t\tuint16_t port_id;\n+\t\t\tuint8_t ip[16];\n+\t\t} addr6_change;\n+\t\t/** Structure used to pass notification about IPv4 route being\n+\t\t * added/removed.\n+\t\t * @see RTE_IFPX_ROUTE_ADD\n+\t\t * @see RTE_IFPX_ROUTE_DEL\n+\t\t */\n+\t\tstruct rte_ifpx_route_change {\n+\t\t\tuint16_t port_id;\n+\t\t\tuint8_t depth;\n+\t\t\tuint32_t ip;\n+\t\t\tuint32_t gateway;\n+\t\t} route_change;\n+\t\t/** Structure used to pass notification about IPv6 route being\n+\t\t * added/removed.\n+\t\t * @see RTE_IFPX_ROUTE6_ADD\n+\t\t * @see RTE_IFPX_ROUTE6_DEL\n+\t\t */\n+\t\tstruct rte_ifpx_route6_change {\n+\t\t\tuint16_t port_id;\n+\t\t\tuint8_t depth;\n+\t\t\tuint8_t ip[16];\n+\t\t\tuint8_t gateway[16];\n+\t\t} route6_change;\n+\t\t/** Structure used to pass notification about IPv4 neighbour\n+\t\t * info changes.\n+\t\t * @see RTE_IFPX_NEIGH_ADD\n+\t\t * @see RTE_IFPX_NEIGH_DEL\n+\t\t */\n+\t\tstruct rte_ifpx_neigh_change {\n+\t\t\tuint16_t port_id;\n+\t\t\tstruct rte_ether_addr mac;\n+\t\t\tuint32_t ip;\n+\t\t} neigh_change;\n+\t\t/** Structure used to pass notification about IPv6 neighbour\n+\t\t * info changes.\n+\t\t * @see RTE_IFPX_NEIGH6_ADD\n+\t\t * @see RTE_IFPX_NEIGH6_DEL\n+\t\t */\n+\t\tstruct rte_ifpx_neigh6_change {\n+\t\t\tuint16_t port_id;\n+\t\t\tstruct rte_ether_addr mac;\n+\t\t\tuint8_t ip[16];\n+\t\t} neigh6_change;\n+\t\t/* This structure is used internally - to abstract common parts\n+\t\t * of proxy/port related events and to be able to refer to this\n+\t\t * union without giving it a name.\n+\t\t */\n+\t\tstruct {\n+\t\t\tuint16_t port_id;\n+\t\t} data;\n+\t};\n+};\n+\n+/**\n+ * This library can deliver notification about network configuration changes\n+ * either by the use of registered callbacks and/or by queueing change events to\n+ * configured notification queues.  The logic used is:\n+ * 1. If there is callback registered for given event type it is called.  In\n+ *   case of many ports to one proxy binding, this callback is called for every\n+ *   port bound.\n+ * 2. If this callback returns non-zero value (for any of ports in case of\n+ *   many-1 bindings) the handling of an event is considered as complete.\n+ * 3. Otherwise the event is added to each configured event queue.  The event is\n+ *   allocated with malloc() so after dequeueing and handling the application\n+ *   should deallocate it with free().\n+ *\n+ * This dual notification mechanism is meant to provide some flexibility to\n+ * application writer.  For example, if you store your data in a single writer/\n+ * many readers coherent data structure you could just update this structure\n+ * from the callback.  If you keep separate copy per lcore/port you could make\n+ * some common preparations (if applicable) in the callback, return 0 and use\n+ * notification queues to pick up the change and update data structures.  Or you\n+ * could skip the callbacks altogether and just use notification queues - and\n+ * configure them at the level appropriate for your application design (one\n+ * global / one per lcore / one per port ...).\n+ */\n+\n+/**\n+ * Add notification queue to the list of queues.\n+ *\n+ * @param r\n+ *   Ring used for queueing of notification events - application can assume that\n+ *   there is only one producer.\n+ * @return\n+ *   0 on success, negative otherwise.\n+ */\n+int rte_ifpx_queue_add(struct rte_ring *r);\n+\n+/**\n+ * Remove notification queue from the list of queues.\n+ *\n+ * @param r\n+ *   Notification ring used for queueing of notification events (previously\n+ *   added via rte_ifpx_queue_add()).\n+ * @return\n+ *   0 on success, negative otherwise.\n+ */\n+int rte_ifpx_queue_remove(struct rte_ring *r);\n+\n+/**\n+ * This structure groups the callbacks that might be called as a notification\n+ * events for changing network configuration.  Not every platform might\n+ * implement all of them and you can query the availability with\n+ * rte_ifpx_callbacks_available() function.\n+ * @see rte_ifpx_events_available()\n+ * @see rte_ifpx_callbacks_register()\n+ */\n+struct rte_ifpx_callbacks {\n+\tint (*mac_change)(const struct rte_ifpx_mac_change *event);\n+\t/**< Callback for notification about MAC change of the proxy interface.\n+\t * This callback (as all other port related callbacks) is called for\n+\t * each port (with its port_id as a first argument) bound to the proxy\n+\t * interface for which change has been observed.\n+\t * @see struct rte_ifpx_mac_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*mtu_change)(const struct rte_ifpx_mtu_change *event);\n+\t/**< Callback for notification about MTU change.\n+\t * @see struct rte_ifpx_mtu_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*link_change)(const struct rte_ifpx_link_change *event);\n+\t/**< Callback for notification about link going up/down.\n+\t * @see struct rte_ifpx_link_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*addr_add)(const struct rte_ifpx_addr_change *event);\n+\t/**< Callback for notification about IPv4 address being added.\n+\t * @see struct rte_ifpx_addr_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*addr_del)(const struct rte_ifpx_addr_change *event);\n+\t/**< Callback for notification about IPv4 address removal.\n+\t * @see struct rte_ifpx_addr_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*addr6_add)(const struct rte_ifpx_addr6_change *event);\n+\t/**< Callback for notification about IPv6 address being added.\n+\t * @see struct rte_ifpx_addr6_change\n+\t */\n+\tint (*addr6_del)(const struct rte_ifpx_addr6_change *event);\n+\t/**< Callback for notification about IPv4 address removal.\n+\t * @see struct rte_ifpx_addr6_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\t/* Please note that \"route\" callbacks might be also called when user\n+\t * adds address to the interface (that is in addition to address related\n+\t * callbacks).\n+\t */\n+\tint (*route_add)(const struct rte_ifpx_route_change *event);\n+\t/**< Callback for notification about IPv4 route being added.\n+\t * @see struct rte_ifpx_route_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*route_del)(const struct rte_ifpx_route_change *event);\n+\t/**< Callback for notification about IPv4 route removal.\n+\t * @see struct rte_ifpx_route_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*route6_add)(const struct rte_ifpx_route6_change *event);\n+\t/**< Callback for notification about IPv6 route being added.\n+\t * @see struct rte_ifpx_route6_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*route6_del)(const struct rte_ifpx_route6_change *event);\n+\t/**< Callback for notification about IPv6 route removal.\n+\t * @see struct rte_ifpx_route6_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*neigh_add)(const struct rte_ifpx_neigh_change *event);\n+\t/**< Callback for notification about IPv4 neighbour being added.\n+\t * @see struct rte_ifpx_neigh_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*neigh_del)(const struct rte_ifpx_neigh_change *event);\n+\t/**< Callback for notification about IPv4 neighbour removal.\n+\t * @see struct rte_ifpx_neigh_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*neigh6_add)(const struct rte_ifpx_neigh6_change *event);\n+\t/**< Callback for notification about IPv6 neighbour being added.\n+\t * @see struct rte_ifpx_neigh_change\n+\t */\n+\tint (*neigh6_del)(const struct rte_ifpx_neigh6_change *event);\n+\t/**< Callback for notification about IPv6 neighbour removal.\n+\t * @see struct rte_ifpx_neigh_change\n+\t * @return non-zero if event handling is finished\n+\t */\n+\tint (*cfg_done)(void);\n+\t/**< Lib specific callback - called when initial network configuration\n+\t * query is finished.\n+\t * @return non-zero if event handling is finished\n+\t */\n+};\n+\n+/**\n+ * Register proxy callbacks.\n+ *\n+ * This function registers callbacks to be called upon appropriate network\n+ * event notification.\n+ *\n+ * @param cbs\n+ *   Set of callbacks that will be called.  The library does not take any\n+ *   ownership of the pointer passed - the callbacks are stored internally.\n+ *\n+ * @return\n+ *   0 on success, negative otherwise.\n+ */\n+__rte_experimental\n+int rte_ifpx_callbacks_register(const struct rte_ifpx_callbacks *cbs);\n+\n+/**\n+ * Unregister proxy callbacks.\n+ *\n+ * This function unregisters callbacks previously registered with\n+ * rte_ifpx_callbacks_register().\n+ *\n+ * @param cbs\n+ *   Handle/pointer returned on previous callback registration.\n+ *\n+ * @return\n+ *   0 on success, negative otherwise.\n+ */\n+__rte_experimental\n+void rte_ifpx_callbacks_unregister(void);\n+\n+/**\n+ * Bind the port to its proxy.\n+ *\n+ * After calling this function all network configuration of the proxy (and it's\n+ * changes) will be passed to given port by calling registered callbacks with\n+ * 'port_id' as an argument.\n+ *\n+ * Note: since both arguments are of the same type in order to not mix them and\n+ * ease remembering the order the first one is kept the same for bind/unbind.\n+ *\n+ * @param port_id\n+ *   Id of the port to be bound.\n+ * @param proxy_id\n+ *   Id of the proxy the port needs to be bound to.\n+ * @return\n+ *   0 on success, negative on error.\n+ */\n+__rte_experimental\n+int rte_ifpx_port_bind(uint16_t port_id, uint16_t proxy_id);\n+\n+/**\n+ * Unbind the port from its proxy.\n+ *\n+ * After calling this function registered callbacks will no longer be called for\n+ * this port (but they might be called for other ports in one to many binding\n+ * scenario).\n+ *\n+ * @param port_id\n+ *   Id of the port to unbind.\n+ * @return\n+ *   0 on success, negative on error.\n+ */\n+__rte_experimental\n+int rte_ifpx_port_unbind(uint16_t port_id);\n+\n+/**\n+ * Get the system network configuration and start listening to its changes.\n+ *\n+ * @return\n+ *   0 on success, negative otherwise.\n+ */\n+__rte_experimental\n+int rte_ifpx_listen(void);\n+\n+/**\n+ * Remove all bindings/callbacks and stop listening to network configuration.\n+ *\n+ * @return\n+ *   0 on success, negative otherwise.\n+ */\n+__rte_experimental\n+int rte_ifpx_close(void);\n+\n+/**\n+ * Get the id of the proxy the port is bound to.\n+ *\n+ * @param port_id\n+ *   Id of the port for which to get proxy.\n+ * @return\n+ *   Port id of the proxy on success, RTE_MAX_ETHPORTS on error.\n+ */\n+__rte_experimental\n+uint16_t rte_ifpx_proxy_get(uint16_t port_id);\n+\n+/**\n+ * Test for port acting as a proxy.\n+ *\n+ * @param port_id\n+ *   Id of the port.\n+ * @return\n+ *   1 if port acts as a proxy, 0 otherwise.\n+ */\n+static inline\n+int rte_ifpx_is_proxy(uint16_t port_id)\n+{\n+\treturn rte_ifpx_proxy_get(port_id) == port_id;\n+}\n+\n+/**\n+ * Get the ids of the ports bound to the proxy.\n+ *\n+ * @param proxy_id\n+ *   Id of the proxy for which to get ports.\n+ * @param ports\n+ *   Array where to store the port ids.\n+ * @param num\n+ *   Size of the 'ports' array.\n+ * @return\n+ *   The number of ports bound to given proxy.  Note that bound ports are filled\n+ *   in 'ports' array up to its size but the return value is always the total\n+ *   number of ports bound - so you can make call first with NULL/0 to query for\n+ *   the size of the buffer to create or call it with the buffer you have and\n+ *   later check if it was large enough.\n+ */\n+__rte_experimental\n+unsigned int rte_ifpx_port_get(uint16_t proxy_id,\n+\t\t\t       uint16_t *ports, unsigned int num);\n+\n+/**\n+ * The structure containing some properties of the proxy interface.\n+ */\n+struct rte_ifpx_info {\n+\tunsigned int if_index; /* entry valid iff if_index != 0 */\n+\tuint16_t mtu;\n+\tstruct rte_ether_addr mac;\n+\tchar if_name[RTE_ETH_NAME_MAX_LEN];\n+};\n+\n+/**\n+ * Get the properties of the proxy interface.  Argument can be either id of the\n+ * proxy or an id of a port that is bound to it.\n+ *\n+ * @param port_id\n+ *   Id of the port (or proxy) for which to get proxy properties.\n+ * @return\n+ *   Pointer to the proxy information structure.\n+ */\n+__rte_experimental\n+const struct rte_ifpx_info *rte_ifpx_info_get(uint16_t port_id);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif /* _RTE_IF_PROXY_H_ */\ndiff --git a/lib/librte_if_proxy/rte_if_proxy_version.map b/lib/librte_if_proxy/rte_if_proxy_version.map\nnew file mode 100644\nindex 000000000..e2093137d\n--- /dev/null\n+++ b/lib/librte_if_proxy/rte_if_proxy_version.map\n@@ -0,0 +1,19 @@\n+EXPERIMENTAL {\n+\tglobal:\n+\n+\t rte_ifpx_proxy_create;\n+\t rte_ifpx_proxy_create_by_devarg;\n+\t rte_ifpx_proxy_destroy;\n+\t rte_ifpx_events_available;\n+\t rte_ifpx_callbacks_register;\n+\t rte_ifpx_callbacks_unregister;\n+\t rte_ifpx_port_bind;\n+\t rte_ifpx_port_unbind;\n+\t rte_ifpx_listen;\n+\t rte_ifpx_close;\n+\t rte_ifpx_proxy_get;\n+\t rte_ifpx_port_get;\n+\t rte_ifpx_info_get;\n+\n+\tlocal: *;\n+};\ndiff --git a/lib/meson.build b/lib/meson.build\nindex 0af3efab2..c913b33dd 100644\n--- a/lib/meson.build\n+++ b/lib/meson.build\n@@ -19,7 +19,7 @@ libraries = [\n \t'acl', 'bbdev', 'bitratestats', 'cfgfile',\n \t'compressdev', 'cryptodev',\n \t'distributor', 'efd', 'eventdev',\n-\t'gro', 'gso', 'ip_frag', 'jobstats',\n+\t'gro', 'gso', 'if_proxy', 'ip_frag', 'jobstats',\n \t'kni', 'latencystats', 'lpm', 'member',\n \t'power', 'pdump', 'rawdev',\n \t'rcu', 'rib', 'reorder', 'sched', 'security', 'stack', 'vhost',\n",
    "prefixes": [
        "v2",
        "1/4"
    ]
}