get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 64669,
    "url": "http://patches.dpdk.org/api/patches/64669/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20200114142517.29522-3-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": "<20200114142517.29522-3-aostruszka@marvell.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200114142517.29522-3-aostruszka@marvell.com",
    "date": "2020-01-14T14:25:16",
    "name": "[RFC,2/3] if_proxy: add preliminary Linux implementation",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "85e3b02ad0347a5b5170007f5e2505ee3f1d9bb0",
    "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/20200114142517.29522-3-aostruszka@marvell.com/mbox/",
    "series": [
        {
            "id": 8119,
            "url": "http://patches.dpdk.org/api/series/8119/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=8119",
            "date": "2020-01-14T14:25:14",
            "name": "introduce IF proxy library",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/8119/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/64669/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/64669/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 03B88A04FF;\n\tTue, 14 Jan 2020 15:25:48 +0100 (CET)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id DD2A61C1BE;\n\tTue, 14 Jan 2020 15:25:30 +0100 (CET)",
            "from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com\n [67.231.156.173]) by dpdk.org (Postfix) with ESMTP id F260F1C1B7\n for <dev@dpdk.org>; Tue, 14 Jan 2020 15:25:28 +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 00EEPRg6007770; Tue, 14 Jan 2020 06:25:28 -0800",
            "from sc-exch02.marvell.com ([199.233.58.182])\n by mx0b-0016f401.pphosted.com with ESMTP id 2xgng4vxm0-1\n (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT);\n Tue, 14 Jan 2020 06:25:28 -0800",
            "from SC-EXCH03.marvell.com (10.93.176.83) by SC-EXCH02.marvell.com\n (10.93.176.82) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Tue, 14 Jan\n 2020 06:25:25 -0800",
            "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, 14 Jan 2020 06:25:25 -0800",
            "from amok.marvell.com (unknown [10.95.130.253])\n by maili.marvell.com (Postfix) with ESMTP id 868423F703F;\n Tue, 14 Jan 2020 06:25:23 -0800 (PST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com;\n h=from : to : cc :\n subject : date : message-id : in-reply-to : references : mime-version :\n content-transfer-encoding : content-type; s=pfpt0818;\n bh=6dXfNNpIjK+jZl3afcTqg62n8ua3BmA4HDbdP8foEjA=;\n b=MrLdWURqqHlenhkHmnjjbU0WlQtr3FId52h1n0rTI2R2MLWaXBVsOqI+K25paIMfB+Mt\n zAkKhfPIDJBfOwEotEg5S4jwNk4mfNAgjLnGc/8fnXfm/PGJc1FF+kCIsLkFtKR2+2Ht\n aQ0y/TLxmkvjUk8KxC2rdewVz7xRMEj3a2ebHvo8DQ1kQqLbLiFww7IhqMHMxwhMkNKC\n 5vvFbyPyBjPnIGzuiefNrkbffgyebC8F82bGfCSUKKfMtCY3FBZRPfyyyylJhvku05xv\n wK1Q+NqPHea3ATmbX3Evspq/NnR1R7jvDMKkfdWoK9Gcmn27MgZKlQQrESSv5qX+XRGz CA==",
        "From": "Andrzej Ostruszka <aostruszka@marvell.com>",
        "To": "<dev@dpdk.org>, Thomas Monjalon <thomas@monjalon.net>",
        "CC": "Jerin Jacob Kollanukkaran <jerinj@marvell.com>, Nithin Kumar Dabilpuram\n <ndabilpuram@marvell.com>, Pavan Nikhilesh Bhagavatula\n <pbhagavatula@marvell.com>, Kiran Kumar Kokkilagadda\n <kirankumark@marvell.com>, Krzysztof Kanas <kkanas@marvell.com>",
        "Date": "Tue, 14 Jan 2020 15:25:16 +0100",
        "Message-ID": "<20200114142517.29522-3-aostruszka@marvell.com>",
        "X-Mailer": "git-send-email 2.17.1",
        "In-Reply-To": "<20200114142517.29522-1-aostruszka@marvell.com>",
        "References": "<20200114142517.29522-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-01-14_04:2020-01-13,\n 2020-01-14 signatures=0",
        "Subject": "[dpdk-dev] [RFC PATCH 2/3] if_proxy: add preliminary Linux\n\timplementation",
        "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 commit adds a preliminary Linux implementation of the IF Proxy\nlibrary.  It should allow one to play around with the idea and check its\nusefulness.\n\nSigned-off-by: Andrzej Ostruszka <aostruszka@marvell.com>\n---\n config/common_base                            |   5 +\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                  |  25 +\n lib/librte_if_proxy/meson.build               |   7 +\n lib/librte_if_proxy/rte_if_proxy.c            | 803 ++++++++++++++++++\n lib/meson.build                               |   2 +-\n 8 files changed, 855 insertions(+), 5 deletions(-)\n create mode 100644 lib/librte_if_proxy/Makefile\n create mode 100644 lib/librte_if_proxy/meson.build\n create mode 100644 lib/librte_if_proxy/rte_if_proxy.c",
    "diff": "diff --git a/config/common_base b/config/common_base\nindex 7dec7ed45..f20296750 100644\n--- a/config/common_base\n+++ b/config/common_base\n@@ -1056,6 +1056,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=y\n+\n #\n # Compile the test application\n #\ndiff --git a/lib/Makefile b/lib/Makefile\nindex 46b91ae1a..0a60f3656 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\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 b370c0d26..f3d39a5ce 100644\n--- a/lib/librte_eal/common/include/rte_eal_interrupts.h\n+++ b/lib/librte_eal/common/include/rte_eal_interrupts.h\n@@ -35,7 +35,9 @@ enum rte_intr_handle_type {\n \tRTE_INTR_HANDLE_EXT,          /**< external handler */\n \tRTE_INTR_HANDLE_VDEV,         /**< virtual device */\n \tRTE_INTR_HANDLE_DEV_EVENT,    /**< device event handle */\n+\tRTE_INTR_HANDLE_NETLINK,      /**< netlink notification handle */\n \tRTE_INTR_HANDLE_VFIO_REQ,     /**< VFIO request 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 14ebb108c..ccdd94002 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..9dd5f4791\n--- /dev/null\n+++ b/lib/librte_if_proxy/Makefile\n@@ -0,0 +1,25 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(C) 2019 Marvell International Ltd.\n+\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\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) := rte_if_proxy.c\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/meson.build b/lib/librte_if_proxy/meson.build\nnew file mode 100644\nindex 000000000..f9ed410b6\n--- /dev/null\n+++ b/lib/librte_if_proxy/meson.build\n@@ -0,0 +1,7 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(C) 2019 Marvell International Ltd.\n+\n+version = 1\n+allow_experimental_apis = true\n+sources = files('rte_if_proxy.c')\n+headers = files('rte_if_proxy.h')\ndiff --git a/lib/librte_if_proxy/rte_if_proxy.c b/lib/librte_if_proxy/rte_if_proxy.c\nnew file mode 100644\nindex 000000000..770462702\n--- /dev/null\n+++ b/lib/librte_if_proxy/rte_if_proxy.c\n@@ -0,0 +1,803 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#include <rte_if_proxy.h>\n+#include <rte_interrupts.h>\n+#include <rte_spinlock.h>\n+#include <rte_string_fns.h>\n+\n+#include <stdbool.h>\n+#include <unistd.h>\n+#include <errno.h>\n+#include <linux/rtnetlink.h>\n+#include <linux/if.h>\n+#include <sys/socket.h>\n+#include <sys/queue.h>\n+\n+static\n+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+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+/* Port to proxy mapping table */\n+static uint16_t ifpx_p2p[RTE_MAX_ETHPORTS];\n+\n+/* Since this library is really slow/config path we guard data structures with\n+ * a lock - and only one for all of them should be enough.  But only callback\n+ * and proxies lists are protected, I don't expect the need to protect port to\n+ * proxy map table above.\n+ */\n+static\n+rte_spinlock_t ifpx_lock = RTE_SPINLOCK_INITIALIZER;\n+\n+/* List of configured proxies */\n+struct ifpx_proxies_node {\n+\tTAILQ_ENTRY(ifpx_proxies_node) elem;\n+\tuint16_t proxy_id;\n+\tstruct rte_ifpx_info info;\n+};\n+static\n+TAILQ_HEAD(ifpx_proxies_head, ifpx_proxies_node) ifpx_proxies =\n+\t\tTAILQ_HEAD_INITIALIZER(ifpx_proxies);\n+\n+/* List of registered callbacks */\n+struct ifpx_cbs_node {\n+\tTAILQ_ENTRY(ifpx_cbs_node) elem;\n+\tstruct rte_ifpx_callbacks cbs;\n+};\n+static\n+TAILQ_HEAD(ifpx_cbs_head, ifpx_cbs_node) ifpx_callbacks =\n+\t\tTAILQ_HEAD_INITIALIZER(ifpx_callbacks);\n+\n+static\n+int request_info(int type, int index);\n+\n+uint64_t rte_ifpx_callbacks_available(void)\n+{\n+\treturn RTE_IFPX_MAC_CHANGE | RTE_IFPX_MTU_CHANGE |\n+\t\tRTE_IFPX_LINK_CHANGE | RTE_IFPX_ADDR_ADD |\n+\t\tRTE_IFPX_ADDR_DEL | RTE_IFPX_ADDR6_ADD |\n+\t\tRTE_IFPX_ADDR6_DEL | RTE_IFPX_ROUTE_ADD |\n+\t\tRTE_IFPX_ROUTE_DEL | RTE_IFPX_ROUTE6_ADD |\n+\t\tRTE_IFPX_ROUTE6_DEL;\n+}\n+\n+uint16_t rte_ifpx_create(enum rte_ifpx_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_create_by_devarg(devargs);\n+}\n+\n+uint16_t rte_ifpx_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+\tRTE_ETH_FOREACH_MATCHING_DEV(port_id, devarg, &iter) {\n+\t\tbreak;\n+\t}\n+\tif (port_id != RTE_MAX_ETHPORTS)\n+\t\trte_eth_iterator_cleanup(&iter);\n+\n+\treturn port_id;\n+}\n+\n+int rte_ifpx_destroy(uint16_t proxy_id)\n+{\n+\tstruct ifpx_proxies_node *px;\n+\tunsigned int i;\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+\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_p2p); ++i) {\n+\t\tif (ifpx_p2p[i] == proxy_id)\n+\t\t\tifpx_p2p[i] = RTE_MAX_ETHPORTS;\n+\t}\n+\n+\tec = rte_dev_remove(rte_eth_devices[proxy_id].device);\n+exit:\n+\trte_spinlock_unlock(&ifpx_lock);\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_proxies_node *px;\n+\tint ec;\n+\n+\tif (port_id >= RTE_MAX_ETHPORTS || proxy_id >= RTE_MAX_ETHPORTS) {\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_p2p[port_id] != RTE_MAX_ETHPORTS) {\n+\t\tIFPX_LOG(WARNING, \"Port already bound: %d -> %d\", port_id,\n+\t\t\t ifpx_p2p[port_id]);\n+\t}\n+\n+\tec = rte_eth_dev_info_get(proxy_id, &proxy_eth_info);\n+\tif (ec < 0) {\n+\t\tIFPX_LOG(ERR, \"Failed to read proxy dev info: %d\", ec);\n+\t\treturn ec;\n+\t}\n+\tif (proxy_eth_info.if_index == 0) {\n+\t\tIFPX_LOG(ERR, \"Proxy with no IF index\");\n+\t\treturn -EINVAL;\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\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}\n+\trte_spinlock_unlock(&ifpx_lock);\n+\tifpx_p2p[port_id] = proxy_id;\n+\n+\tif (ifpx_irq.fd != -1)\n+\t\trequest_info(RTM_GETLINK, 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_p2p[port_id] == RTE_MAX_ETHPORTS)\n+\t\treturn -EINVAL;\n+\n+\tifpx_p2p[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+rte_ifpx_cbs_hndl rte_ifpx_callbacks_register(const\n+\t\t\t\t\t      struct rte_ifpx_callbacks *cbs)\n+{\n+\trte_ifpx_cbs_hndl cb_hndl = NULL;\n+\tstruct ifpx_cbs_node *node;\n+\n+\tif (!cbs)\n+\t\treturn NULL;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tTAILQ_FOREACH(node, &ifpx_callbacks, elem) {\n+\t\tif (&node->cbs == cbs) {\n+\t\t\tcb_hndl = cbs;\n+\t\t\tgoto exit;\n+\t\t}\n+\t}\n+\n+\tnode = malloc(sizeof(*node));\n+\tif (!node)\n+\t\tgoto exit;\n+\n+\tnode->cbs = *cbs;\n+\tTAILQ_INSERT_TAIL(&ifpx_callbacks, node, elem);\n+\tcb_hndl = &node->cbs;\n+exit:\n+\trte_spinlock_unlock(&ifpx_lock);\n+\n+\treturn cb_hndl;\n+}\n+\n+int rte_ifpx_callbacks_unregister(rte_ifpx_cbs_hndl cbs)\n+{\n+\tstruct ifpx_cbs_node *node;\n+\tint ec = -EINVAL;\n+\n+\tif (!cbs)\n+\t\treturn ec;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tTAILQ_FOREACH(node, &ifpx_callbacks, elem) {\n+\t\tif (&node->cbs == cbs) {\n+\t\t\tTAILQ_REMOVE(&ifpx_callbacks, node, elem);\n+\t\t\tfree(node);\n+\t\t\tec = 0;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\trte_spinlock_unlock(&ifpx_lock);\n+\n+\treturn ec;\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_p2p[port_id];\n+}\n+\n+unsigned int rte_ifpx_port_get(uint16_t proxy_id,\n+                               uint16_t *ports, unsigned int num)\n+{\n+\tunsigned int p, cnt = 0;\n+\n+\tfor (p = 0; p < RTE_DIM(ifpx_p2p); ++p) {\n+\t\tif (ifpx_p2p[p] == proxy_id) {\n+\t\t\t++cnt;\n+\t\t\tif (ports && num > 0) {\n+\t\t\t\t*ports++ = ifpx_p2p[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_proxies_node *px;\n+\n+\tif (port_id >= RTE_MAX_ETHPORTS ||\n+\t    ifpx_p2p[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_p2p[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 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_proxies_node *px;\n+\tstruct ifpx_cbs_node *cb;\n+\tuint16_t p;\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+\trte_spinlock_unlock(&ifpx_lock);\n+\n+\t/* Drop messages that are not associated with any proxy */\n+\tif (!px)\n+\t\treturn;\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\treturn;\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+\trte_spinlock_lock(&ifpx_lock);\n+\tif (ifi->ifi_change & IFF_UP) {\n+\t\tTAILQ_FOREACH(cb, &ifpx_callbacks, elem) {\n+\t\t\tif (!cb->cbs.link_change)\n+\t\t\t\tcontinue;\n+\t\t\tfor (p = 0; p < RTE_DIM(ifpx_p2p); ++p) {\n+\t\t\t\tif (ifpx_p2p[p] != px->proxy_id)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tcb->cbs.link_change(p,\n+\t\t\t\t\t\t    ifi->ifi_flags & IFF_UP);\n+\t\t\t}\n+\t\t}\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\tTAILQ_FOREACH(cb, &ifpx_callbacks, elem) {\n+\t\t\t\tif (!cb->cbs.mtu_change)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tfor (p = 0; p < RTE_DIM(ifpx_p2p); ++p) {\n+\t\t\t\t\tif (ifpx_p2p[p] != px->proxy_id)\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\tcb->cbs.mtu_change(p, mtu);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\tif (attrs[IFLA_ADDRESS]) {\n+\t\tconst struct rte_ether_addr *mac =\n+\t\t                RTA_DATA(attrs[IFLA_ADDRESS]);\n+\n+\t\tRTE_ASSERT(RTA_PAYLOAD(attrs[IFLA_ADDRESS]) ==\n+\t\t           RTE_ETHER_ADDR_LEN);\n+\t\tif (memcmp(mac, &px->info.mac, RTE_ETHER_ADDR_LEN) != 0) {\n+\t\t\tmemcpy(px->info.mac.addr_bytes, mac, RTE_ETHER_ADDR_LEN);\n+\t\t\tTAILQ_FOREACH(cb, &ifpx_callbacks, elem) {\n+\t\t\t\tif (!cb->cbs.mac_change)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tfor (p = 0; p < RTE_DIM(ifpx_p2p); ++p) {\n+\t\t\t\t\tif (ifpx_p2p[p] != px->proxy_id)\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\tcb->cbs.mac_change(p, mac);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\trte_spinlock_unlock(&ifpx_lock);\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+\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_proxies_node *px;\n+\tstruct ifpx_cbs_node *cb;\n+\tconst uint8_t *ip;\n+\tuint16_t p;\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+\trte_spinlock_unlock(&ifpx_lock);\n+\n+\t/* Drop messages that are not associated with any proxy */\n+\tif (!px)\n+\t\treturn;\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\treturn;\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+\trte_spinlock_lock(&ifpx_lock);\n+\tif (attrs[IFA_ADDRESS]) {\n+\t\tTAILQ_FOREACH(cb, &ifpx_callbacks, elem) {\n+\t\t\tstruct rte_ifpx_callbacks *cbs = &cb->cbs;\n+\n+\t\t\tip = RTA_DATA(attrs[IFA_ADDRESS]);\n+\t\t\tif (ifa->ifa_family == AF_INET) {\n+\t\t\t\t/* address is in network order */\n+\t\t\t\tuint32_t ipv4 =\n+\t\t\t\t\tRTE_IPV4(ip[0], ip[1], ip[2], ip[3]);\n+\n+\t\t\t\tfor (p = 0; p < RTE_DIM(ifpx_p2p); ++p) {\n+\t\t\t\t\tif (ifpx_p2p[p] != px->proxy_id)\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\tif (needs_del && cbs->addr_del)\n+\t\t\t\t\t\tcb->cbs.addr_del(p, ipv4);\n+\t\t\t\t\telse if (!needs_del && cbs->addr_add)\n+\t\t\t\t\t\tcb->cbs.addr_add(p, ipv4);\n+\t\t\t\t}\n+\t\t\t} else if (ifa->ifa_family == AF_INET6) {\n+\t\t\t\tfor (p = 0; p < RTE_DIM(ifpx_p2p); ++p) {\n+\t\t\t\t\tif (ifpx_p2p[p] != px->proxy_id)\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\tif (needs_del && cbs->addr6_del)\n+\t\t\t\t\t\tcb->cbs.addr6_del(p, ip);\n+\t\t\t\t\telse if (!needs_del && cbs->addr6_add)\n+\t\t\t\t\t\tcb->cbs.addr6_add(p, ip);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\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 ifpx_cbs_node *node;\n+\tconst uint8_t *ip;\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+\trte_spinlock_lock(&ifpx_lock);\n+\tif (attrs[RTA_DST]) {\n+\t\tTAILQ_FOREACH(node, &ifpx_callbacks, elem) {\n+\t\t\tstruct rte_ifpx_callbacks *cbs = &node->cbs;\n+\n+\t\t\tip = RTA_DATA(attrs[RTA_DST]);\n+\t\t\tif (r->rtm_family == AF_INET) {\n+\t\t\t\t/* address is in network order */\n+\t\t\t\tuint32_t ipv4 =\n+\t\t\t\t\tRTE_IPV4(ip[0], ip[1], ip[2], ip[3]);\n+\n+\t\t\t\tif (needs_del && cbs->route_del)\n+\t\t\t\t\tcbs->route_del(ipv4, r->rtm_dst_len);\n+\t\t\t\telse if (!needs_del && cbs->route_add)\n+\t\t\t\t\tcbs->route_add(ipv4, r->rtm_dst_len);\n+\t\t\t} else if (r->rtm_family == AF_INET6) {\n+\t\t\t\tif (needs_del && cbs->route6_del)\n+\t\t\t\t\tcbs->route6_del(ip, r->rtm_dst_len);\n+\t\t\t\telse if (!needs_del && cbs->route6_add)\n+\t\t\t\t\tcbs->route6_add(ip, r->rtm_dst_len);\n+\t\t\t}\n+\t\t}\n+\t}\n+\trte_spinlock_unlock(&ifpx_lock);\n+}\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} __rte_aligned(NLMSG_ALIGNTO);\n+\t} info_req;\n+\tint ret;\n+\n+\tIFPX_LOG(DEBUG, \"\\tRequesting msg %d for: %u\", type, index);\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+\tdefault:\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+\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 notify_cfg_finished(void)\n+{\n+\tstruct ifpx_cbs_node *node;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\tTAILQ_FOREACH(node, &ifpx_callbacks, elem) {\n+\t\tif ( !node->cbs.cfg_finished)\n+\t\t\tcontinue;\n+\t\tnode->cbs.cfg_finished();\n+\t}\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\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 {\n+\t\t\t\tRTE_ASSERT((h->nlmsg_seq & 0xFF) ==\n+\t\t\t\t\t\t\t\tRTE_GETROUTE);\n+\t\t\t\tnotify_cfg_finished();\n+\t\t\t}\n+\t\t}\n+\t}\n+\tIFPX_LOG(DEBUG, \"Finished msg loop: %ld bytes left\", len);\n+}\n+\n+int rte_ifpx_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_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\tgoto error;\n+\n+\trequest_info(RTM_GETLINK, 0);\n+\n+\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+int rte_ifpx_close(void)\n+{\n+\tint ec;\n+\tunsigned int p;\n+\tstruct ifpx_cbs_node *cbs;\n+\tstruct ifpx_proxies_node *px;\n+\n+\tif (ifpx_irq.fd < 0)\n+\t\treturn -EBADFD;\n+\n+restart:\n+\tec = rte_intr_callback_unregister(&ifpx_irq,\n+\t\t\t\t\t  if_proxy_intr_callback, NULL);\n+\tif (ec == -EAGAIN) /* unlikely but possible - at least I think so */\n+\t\tgoto restart;\n+\n+\trte_spinlock_lock(&ifpx_lock);\n+\n+\tclose(ifpx_irq.fd);\n+\tifpx_irq.fd = -1;\n+\tifpx_pid = 0;\n+\n+\t/* Clear callbacks. */\n+\twhile (!TAILQ_EMPTY(&ifpx_callbacks)) {\n+\t\tcbs = TAILQ_FIRST(&ifpx_callbacks);\n+\t\tTAILQ_REMOVE(&ifpx_callbacks, cbs, elem);\n+\t\tfree(cbs);\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+\tfor (p = 0; p < RTE_DIM(ifpx_p2p); ++p)\n+\t\tifpx_p2p[p] = RTE_MAX_ETHPORTS;\n+\n+\trte_spinlock_unlock(&ifpx_lock);\n+\n+\treturn 0;\n+}\n+\n+RTE_INIT(if_proxy_init)\n+{\n+\tunsigned int i;\n+\tfor (i = 0; i < RTE_DIM(ifpx_p2p); ++i)\n+\t\tifpx_p2p[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+}\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": [
        "RFC",
        "2/3"
    ]
}