get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 64670,
    "url": "http://patches.dpdk.org/api/patches/64670/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20200114142517.29522-4-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-4-aostruszka@marvell.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20200114142517.29522-4-aostruszka@marvell.com",
    "date": "2020-01-14T14:25:17",
    "name": "[RFC,3/3] if_proxy: add example, test and documentation",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "a75f157b7d83d5408d0d1b421da9ef87cae11906",
    "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-4-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/64670/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/64670/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 C5DC7A04FF;\n\tTue, 14 Jan 2020 15:25:57 +0100 (CET)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 6E49F1C1C1;\n\tTue, 14 Jan 2020 15:25:33 +0100 (CET)",
            "from mx0b-0016f401.pphosted.com (mx0a-0016f401.pphosted.com\n [67.231.148.174]) by dpdk.org (Postfix) with ESMTP id 561EA1C1C1\n for <dev@dpdk.org>; Tue, 14 Jan 2020 15:25:31 +0100 (CET)",
            "from pps.filterd (m0045849.ppops.net [127.0.0.1])\n by mx0a-0016f401.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id\n 00EEKULf013366; Tue, 14 Jan 2020 06:25:30 -0800",
            "from sc-exch01.marvell.com ([199.233.58.181])\n by mx0a-0016f401.pphosted.com with ESMTP id 2xhc6sgngg-1\n (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT);\n Tue, 14 Jan 2020 06:25:30 -0800",
            "from SC-EXCH03.marvell.com (10.93.176.83) by SC-EXCH01.marvell.com\n (10.93.176.81) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Tue, 14 Jan\n 2020 06:25:28 -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:28 -0800",
            "from amok.marvell.com (unknown [10.95.130.253])\n by maili.marvell.com (Postfix) with ESMTP id 6BD153F7040;\n Tue, 14 Jan 2020 06:25:26 -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=31ESREwEYa4sdPp199Ejs1RynvMaRAj7vKifLQW5QkE=;\n b=GqHJWwuwoq1L6RxAjKVjRoM9yJMLNMopyDSH+N8OGRJzpr6Lt/x02OXclmljmHd34wH/\n jUzBMIycrtAYyv0UMq7jx8RQC06Uly6gRMGBJJ1TBu36sYtnMpxw/20jaV2l8MVy2Jyp\n Lcxcno34mobxWlLhDR3IQFSzj5VV4JSh5wMwMZzlEgsHEhmgYxxx8NK/sHliMCMm4iFp\n IzxYfKv3UfSJ7Fk1oIhvn+MF4z/Lz/i5ybfG57IgB9B+8wIai6wBlUad8F5Igqcu8NRi\n t+jXfhpb6VaIN8ojDBzXHWL77RD7rOhz9sQBJiz7LfUiyriQZ7r6zpGDpk4i/gHmItkV 1Q==",
        "From": "Andrzej Ostruszka <aostruszka@marvell.com>",
        "To": "<dev@dpdk.org>, John McNamara <john.mcnamara@intel.com>, Marko Kovacevic\n <marko.kovacevic@intel.com>",
        "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:17 +0100",
        "Message-ID": "<20200114142517.29522-4-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 3/3] if_proxy: add example,\n\ttest and documentation",
        "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 test, documentation and a small example.\n\nThe example just creates one proxy port and binds all ports available to\nit.  Then you can play around with changing of network configuration of\nthis proxy port and you should observe notifications from the\nappropriate callbacks.  Below is an exemplary output (with some parts\nelided and some comments added) - 'dtap0' is the name of the proxy\ninterface.\n\nsudo ./if_proxy -w 00:03.0 -w 00:04.0\n...\nPress ^C to quit\n        route add -> 10.0.0.0/16\n        route add -> 192.168.123.0/24\n...\n        route6 add -> ::1/128\n        route6 add -> fe80::/64\n        route6 add -> fe80::ee05:deaf:6827:b435/128\n...\n[[ output on: ip link set dtap0 mtu 1600 ]]\n        mtu change for port 0 -> 1600\n        mtu change for port 1 -> 1600\n[[ output on: ip link set dtap0 up ]]\n        port 0 going up\n        port 1 going up\n        route6 add -> ff00::/8\n        route6 add -> fe80::/64\n        address6 add for port 0 -> fe80::2436:17ff:fefd:94ed\n        address6 add for port 1 -> fe80::2436:17ff:fefd:94ed\n        route6 add -> fe80::2436:17ff:fefd:94ed/128\n\nSigned-off-by: Andrzej Ostruszka <aostruszka@marvell.com>\n---\n app/test/Makefile                      |   5 +\n app/test/meson.build                   |   1 +\n app/test/test_if_proxy.c               | 431 +++++++++++++++++++++++++\n doc/guides/prog_guide/if_proxy_lib.rst | 103 ++++++\n doc/guides/prog_guide/index.rst        |   1 +\n examples/Makefile                      |   1 +\n examples/if_proxy/Makefile             |  58 ++++\n examples/if_proxy/main.c               | 203 ++++++++++++\n examples/if_proxy/meson.build          |  12 +\n examples/meson.build                   |   2 +-\n 10 files changed, 816 insertions(+), 1 deletion(-)\n create mode 100644 app/test/test_if_proxy.c\n create mode 100644 doc/guides/prog_guide/if_proxy_lib.rst\n create mode 100644 examples/if_proxy/Makefile\n create mode 100644 examples/if_proxy/main.c\n create mode 100644 examples/if_proxy/meson.build",
    "diff": "diff --git a/app/test/Makefile b/app/test/Makefile\nindex 57930c00b..f621978d7 100644\n--- a/app/test/Makefile\n+++ b/app/test/Makefile\n@@ -230,6 +230,11 @@ SRCS-$(CONFIG_RTE_LIBRTE_BPF) += test_bpf.c\n \n SRCS-$(CONFIG_RTE_LIBRTE_RCU) += test_rcu_qsbr.c test_rcu_qsbr_perf.c\n \n+ifeq ($(CONFIG_RTE_LIBRTE_IF_PROXY),y)\n+SRCS-y += test_if_proxy.c\n+LDLIBS += -lrte_if_proxy\n+endif\n+\n SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += test_ipsec.c\n SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += test_ipsec_sad.c\n ifeq ($(CONFIG_RTE_LIBRTE_IPSEC),y)\ndiff --git a/app/test/meson.build b/app/test/meson.build\nindex fb49d804b..2a3b5fef2 100644\n--- a/app/test/meson.build\n+++ b/app/test/meson.build\n@@ -61,6 +61,7 @@ test_sources = files('commands.c',\n \t'test_hash_perf.c',\n \t'test_hash_readwrite_lf.c',\n \t'test_interrupts.c',\n+\t'test_if_proxy.c',\n \t'test_ipsec.c',\n \t'test_ipsec_sad.c',\n \t'test_kni.c',\ndiff --git a/app/test/test_if_proxy.c b/app/test/test_if_proxy.c\nnew file mode 100644\nindex 000000000..0ecfb79b4\n--- /dev/null\n+++ b/app/test/test_if_proxy.c\n@@ -0,0 +1,431 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#include \"test.h\"\n+\n+#include <rte_ethdev.h>\n+#include <rte_if_proxy.h>\n+\n+#include <string.h>\n+#include <unistd.h>\n+#include <signal.h>\n+#include <net/if.h>\n+#include <arpa/inet.h>\n+#include <pthread.h>\n+#include <time.h>\n+\n+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;\n+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;\n+\n+enum net_op {\n+\tINITIALIZED\t= 1U << 0,\n+\tLOOP_ROUTE\t= 1U << 1,\n+\tLOOP6_ROUTE\t= 1U << 2,\n+\tLINK_CHANGED\t= 1U << 3,\n+\tMAC_CHANGED\t= 1U << 4,\n+\tMTU_CHANGED\t= 1U << 5,\n+\tADDR_ADD\t= 1U << 6,\n+\tADDR_DEL\t= 1U << 7,\n+\tROUTE_ADD\t= 1U << 8,\n+\tROUTE_DEL\t= 1U << 9,\n+\tADDR6_ADD\t= 1U << 10,\n+\tADDR6_DEL\t= 1U << 11,\n+\tROUTE6_ADD\t= 1U << 12,\n+\tROUTE6_DEL\t= 1U << 13,\n+};\n+\n+static unsigned int state;\n+\n+static struct {\n+\tstruct rte_ether_addr mac_addr;\n+\tuint16_t port_id, mtu;\n+\tstruct in_addr ipv4, route4;\n+\tstruct in6_addr ipv6, route6;\n+\tuint16_t depth4, depth6;\n+\tint is_up;\n+} net_cfg;\n+\n+static\n+int unlock_notify(unsigned int op)\n+{\n+\t/* the mutex is expected to be locked on entry */\n+\tRTE_VERIFY(pthread_mutex_trylock(&mutex) == EBUSY);\n+\tstate |= op;\n+\n+\tpthread_mutex_unlock(&mutex);\n+\treturn pthread_cond_signal(&cond);\n+}\n+\n+static\n+int wait_for(unsigned int op_mask, unsigned int sec)\n+{\n+\tstruct timespec time;\n+\tint ec = pthread_mutex_trylock(&mutex);\n+\n+\t/* the mutex is expected to be locked on entry */\n+\tRTE_VERIFY(ec == EBUSY);\n+\n+\tec = 0;\n+\tclock_gettime(CLOCK_REALTIME, &time);\n+\ttime.tv_sec += sec;\n+\n+\twhile ((state & op_mask) != op_mask && ec == 0)\n+\t\tec = pthread_cond_timedwait(&cond, &mutex, &time);\n+\n+\treturn ec;\n+}\n+\n+static\n+int expect(unsigned int op_mask, const char *fmt, ...)\n+#if __GNUC__\n+\t__attribute__((format(printf, 2, 3)));\n+#endif\n+\n+static\n+int expect(unsigned int op_mask, const char *fmt, ...)\n+{\n+\tchar cmd[128];\n+\tva_list args;\n+\tint ret;\n+\n+\tstate &= ~op_mask;\n+\tva_start(args, fmt);\n+\tvsnprintf(cmd, sizeof(cmd), fmt, args);\n+\tva_end(args);\n+\tret = system(cmd);\n+\tif (ret == 0)\n+\t\t/* IPv6 address notifications seem to need that long delay. */\n+\t\treturn wait_for(op_mask, 2);\n+\treturn ret;\n+}\n+\n+static\n+void mac_change(uint16_t port_id, const struct rte_ether_addr *mac)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\tRTE_VERIFY(port_id == net_cfg.port_id);\n+\tif (memcmp(mac->addr_bytes, net_cfg.mac_addr.addr_bytes,\n+\t\t   RTE_ETHER_ADDR_LEN) == 0) {\n+\t\tunlock_notify(MAC_CHANGED);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void mtu_change(uint16_t port_id, uint16_t mtu)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\tRTE_VERIFY(port_id == net_cfg.port_id);\n+\tif (net_cfg.mtu == mtu) {\n+\t\tunlock_notify(MTU_CHANGED);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void link_change(uint16_t port_id, int is_up)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\tRTE_VERIFY(port_id == net_cfg.port_id);\n+\tif (net_cfg.is_up == is_up) {\n+\t\tunlock_notify(LINK_CHANGED);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void addr_add(uint16_t port_id, uint32_t ip)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\tRTE_VERIFY(port_id == net_cfg.port_id);\n+\tif (net_cfg.ipv4.s_addr == ip) {\n+\t\tunlock_notify(ADDR_ADD);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void addr_del(uint16_t port_id, uint32_t ip)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\tRTE_VERIFY(port_id == net_cfg.port_id);\n+\tif (net_cfg.ipv4.s_addr == ip) {\n+\t\tunlock_notify(ADDR_DEL);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void addr6_add(uint16_t port_id, const uint8_t *ip)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\tRTE_VERIFY(port_id == net_cfg.port_id);\n+\tif (memcmp(ip, net_cfg.ipv6.s6_addr, 16) == 0) {\n+\t\tunlock_notify(ADDR6_ADD);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void addr6_del(uint16_t port_id __rte_unused, const uint8_t *ip)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\tRTE_VERIFY(port_id == net_cfg.port_id);\n+\tif (memcmp(ip, net_cfg.ipv6.s6_addr, 16) == 0) {\n+\t\tunlock_notify(ADDR6_DEL);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void route_add(uint32_t ip, uint8_t depth)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\t/* Since we are checking if during initialization we get some routing\n+\t * info we need to notify either when we are not initialized or when\n+\t * the exact route matches.\n+\t */\n+\tif (!(state & INITIALIZED) ||\n+\t    (net_cfg.depth4 == depth && net_cfg.route4.s_addr == ip)) {\n+\t\tunlock_notify(ROUTE_ADD);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void route_del(uint32_t ip, uint8_t depth)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\tif (net_cfg.depth4 == depth && net_cfg.route4.s_addr == ip) {\n+\t\tunlock_notify(ROUTE_DEL);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void route6_add(const uint8_t *ip, uint8_t depth)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\t/* Since we are checking if during initialization we get some routing\n+\t * info we need to notify either when we are not initialized or when\n+\t * the exact route matches.\n+\t */\n+\tif (!(state & INITIALIZED) ||\n+\t    (net_cfg.depth6 == depth &&\n+\t     /* don't check for trailing zeros */\n+\t     memcmp(ip, net_cfg.route6.s6_addr, depth/8) == 0)) {\n+\t\tunlock_notify(ROUTE6_ADD);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void route6_del(const uint8_t *ip, uint8_t depth)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\tif (net_cfg.depth6 == depth &&\n+\t    /* don't check for trailing zeros */\n+\t    memcmp(ip, net_cfg.route6.s6_addr, depth/8) == 0) {\n+\t\tunlock_notify(ROUTE6_DEL);\n+\t\treturn;\n+\t}\n+\tpthread_mutex_unlock(&mutex);\n+}\n+\n+static\n+void cfg_finished(void)\n+{\n+\tpthread_mutex_lock(&mutex);\n+\tunlock_notify(INITIALIZED);\n+}\n+\n+static\n+struct rte_ifpx_callbacks cbs = {\n+\t.mac_change = mac_change,\n+\t.mtu_change = mtu_change,\n+\t.link_change = link_change,\n+\t.addr_add = addr_add,\n+\t.addr_del = addr_del,\n+\t.addr6_add = addr6_add,\n+\t.addr6_del = addr6_del,\n+\t.route_add = route_add,\n+\t.route_del = route_del,\n+\t.route6_add = route6_add,\n+\t.route6_del = route6_del,\n+\t/* lib specific callback */\n+\t.cfg_finished = cfg_finished,\n+};\n+\n+static int\n+test_if_proxy(void)\n+{\n+\tint ec;\n+\tchar buf[INET6_ADDRSTRLEN];\n+\tconst struct rte_ifpx_info *pinfo;\n+\n+\tstate = 0;\n+\tmemset(&net_cfg, 0, sizeof(net_cfg));\n+\t/* Since we are not going to test RX/TX we can just create proxy and\n+\t * bind it to itself to test just notification functionality.\n+\t */\n+\tnet_cfg.port_id = rte_ifpx_create(RTE_IFPX_DEFAULT);\n+\tRTE_VERIFY(net_cfg.port_id != RTE_MAX_ETHPORTS);\n+\trte_ifpx_port_bind(net_cfg.port_id, net_cfg.port_id);\n+\trte_ifpx_callbacks_register(&cbs);\n+\trte_ifpx_listen();\n+\n+\tpthread_mutex_lock(&mutex);\n+\t/* During initialization we should observe IPv4/6 loopback routes. */\n+\tnet_cfg.route4.s_addr = RTE_IPV4(127, 0, 0, 1);\n+\tnet_cfg.depth4 = 32;\n+\tmemcpy(net_cfg.route6.s6_addr, in6addr_loopback.s6_addr, 16);\n+\tnet_cfg.depth6 = 128;\n+\tec = wait_for(INITIALIZED | ROUTE_ADD | ROUTE6_ADD, 2);\n+\tif (ec != 0) {\n+\t\tprintf(\"Failed to obtain network configuration\\n\");\n+\t\tgoto exit;\n+\t}\n+\tpinfo = rte_ifpx_info_get(net_cfg.port_id);\n+\tRTE_VERIFY(pinfo);\n+\n+\t/* Make sure the link is down. */\n+\tnet_cfg.is_up = 0;\n+\tec = expect(LINK_CHANGED, \"ip link set dev %s down\", pinfo->if_name);\n+\tRTE_VERIFY(ec == ETIMEDOUT || ec == 0);\n+\n+\t/* Test link up notification. */\n+\tnet_cfg.is_up = 1;\n+\tec = expect(LINK_CHANGED, \"ip link set dev %s up\", pinfo->if_name);\n+\tif (ec != 0) {\n+\t\tprintf(\"Failed to notify about link going up\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\t/* Test for MAC changes notification. */\n+\trte_eth_random_addr(net_cfg.mac_addr.addr_bytes);\n+\trte_ether_format_addr(buf, sizeof(buf), &net_cfg.mac_addr);\n+\tec = expect(MAC_CHANGED, \"ip link set dev %s address %s\",\n+\t\t    pinfo->if_name, buf);\n+\tif (ec != 0) {\n+\t\tprintf(\"Missing/wrong notification about mac change\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\t/* Test for MTU changes notification. */\n+\tnet_cfg.mtu = pinfo->mtu + 100;\n+\tec = expect(MTU_CHANGED, \"ip link set dev %s mtu %d\",\n+\t\t    pinfo->if_name, net_cfg.mtu);\n+\tif (ec != 0) {\n+\t\tprintf(\"Missing/wrong notification about mtu change\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\t/* Test for adding of IPv4 address - using address from TEST-2 pool.\n+\t * This test is specific to linux netlink behaviour - after adding\n+\t * address we get both notification about address being added and new\n+\t * route.  So I check both.\n+\t */\n+\tnet_cfg.ipv4.s_addr = RTE_IPV4(198, 51, 100, 14);\n+\tnet_cfg.route4.s_addr = net_cfg.ipv4.s_addr;\n+\tnet_cfg.depth4 = 32;\n+\tec = expect(ADDR_ADD | ROUTE_ADD, \"ip addr add 198.51.100.14 dev %s\",\n+\t\t    pinfo->if_name);\n+\tif (ec != 0) {\n+\t\tprintf(\"Missing/wrong notifications about IPv4 address add\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\t/* Test for IPv4 address removal.  See comment above for 'addr add'. */\n+\tec = expect(ADDR_DEL | ROUTE_DEL, \"ip addr del 198.51.100.14/32 dev %s\",\n+\t\t    pinfo->if_name);\n+\tif (ec != 0) {\n+\t\tprintf(\"Missing/wrong notifications about IPv4 address del\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\t/* Test for adding IPv4 route. */\n+\tnet_cfg.route4.s_addr = RTE_IPV4(198, 51, 100, 0);\n+\tnet_cfg.depth4 = 24;\n+\tec = expect(ROUTE_ADD, \"ip route add 198.51.100.0/24 dev %s\",\n+\t\t    pinfo->if_name);\n+\tif (ec != 0) {\n+\t\tprintf(\"Missing/wrong notifications about IPv4 route add\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\t/* Test for IPv4 route removal. */\n+\tec = expect(ROUTE_DEL, \"ip route del 198.51.100.0/24 dev %s\",\n+\t\t    pinfo->if_name);\n+\tif (ec != 0) {\n+\t\tprintf(\"Missing/wrong notifications about IPv4 route del\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\t/* Now the same for IPv6 - with address from \"documentation pool\". */\n+\tinet_pton(AF_INET6, \"2001:db8::dead:beef\", net_cfg.ipv6.s6_addr);\n+\t/* This is specific to linux netlink behaviour - after adding address\n+\t * we get both notification about address being added and new route.\n+\t * So I wait for both.\n+\t */\n+\tmemcpy(net_cfg.route6.s6_addr, net_cfg.ipv6.s6_addr, 16);\n+\tnet_cfg.depth6 = 128;\n+\tec = expect(ADDR6_ADD | ROUTE6_ADD,\n+\t\t    \"ip addr add 2001:db8::dead:beef dev %s\",\n+\t\t    pinfo->if_name);\n+\tif (ec != 0) {\n+\t\tprintf(\"Missing/wrong notifications about IPv6 address add\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\t/* See comment above for 'addr6 add'. */\n+\tec = expect(ADDR6_DEL | ROUTE6_DEL,\n+\t\t    \"ip addr del 2001:db8::dead:beef/128 dev %s\",\n+\t\t    pinfo->if_name);\n+\tif (ec != 0) {\n+\t\tprintf(\"Missing/wrong notifications about IPv6 address del\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\tnet_cfg.depth6 = 96;\n+\tec = expect(ROUTE6_ADD, \"ip route add 2001:db8::dead:0/96 dev %s\",\n+\t\t    pinfo->if_name);\n+\tif (ec != 0) {\n+\t\tprintf(\"Missing/wrong notifications about IPv6 route add\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\tec = expect(ROUTE6_DEL, \"ip route del 2001:db8::dead:0/96 dev %s\",\n+\t\t    pinfo->if_name);\n+\tif (ec != 0) {\n+\t\tprintf(\"Missing/wrong notifications about IPv6 route del\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\t/* Finally put link down and test for notification. */\n+\tnet_cfg.is_up = 0;\n+\tec = expect(LINK_CHANGED, \"ip link set dev %s down\", pinfo->if_name);\n+\tif (ec != 0) {\n+\t\tprintf(\"Failed to notify about link going down\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+exit:\n+\tpthread_mutex_unlock(&mutex);\n+\trte_ifpx_destroy(net_cfg.port_id);\n+\trte_ifpx_close();\n+\n+\treturn ec;\n+}\n+\n+REGISTER_TEST_COMMAND(if_proxy_autotest, test_if_proxy)\ndiff --git a/doc/guides/prog_guide/if_proxy_lib.rst b/doc/guides/prog_guide/if_proxy_lib.rst\nnew file mode 100644\nindex 000000000..dc1202cdf\n--- /dev/null\n+++ b/doc/guides/prog_guide/if_proxy_lib.rst\n@@ -0,0 +1,103 @@\n+..  SPDX-License-Identifier: BSD-3-Clause\n+    Copyright(C) 2019 Marvell International Ltd.\n+\n+.. _IF_Proxy_Library:\n+\n+IF Proxy Library\n+================\n+\n+When a network interface is assigned to DPDK it usually disappears from\n+the system.\n+This way user looses ability to configure it via typical configuration\n+tools and is left basically with two options:\n+\n+  - configure it via command line arguments,\n+\n+  - add support for live configuration via some IPC mechanism.\n+\n+The first option is static and the second one requires some work to add\n+communication loop (e.g. separate thread listening/communicating on\n+a socket).\n+\n+This library adds a possibility to configure DPDK ports by using normal\n+configuration utilities (e.g. from iproute2 suite).\n+It requires user to configure additional DPDK ports that are visible to\n+the system (such as Tap or KNI - actually any port that has valid\n+'if_index' in 'struct rte_eth_dev_info' will do) and designate them as\n+a port representor (a proxy) in the system.\n+\n+Let's see typical intended usage by an example.\n+Suppose that you have application that handles traffic on two ports (in\n+the white list below).\n+\n+    ./app -w 00:14.0 -w 00:16.0 --vdev=net_tap0 --vdev=net_tap1\n+\n+So in addition you configure two proxy ports and in the application code\n+you bind them to the \"main\" ports:\n+\n+    rte_if_proxy_port_bind(port0, proxy0);\n+    rte_if_proxy_port_bind(port1, proxy1);\n+\n+This binding is a logical one - there is no automatic packet forwarding\n+configured.\n+This is because library cannot tell upfront what portion of the traffic\n+received on ports 0/1 should be redirected to the system via proxies and\n+also it does not know how the application is structured (what packet\n+processing engines it uses).\n+Therefore it is application writer responsibility to include proxy ports\n+into its packet processing and forward appropriate packets between\n+proxies and ports.\n+What the library actually does is that it gets network configuration\n+from the system and listens to its changes.\n+This information is then matched against 'if_index' of the configured\n+proxies (when applicable - routing information is global) and passed to\n+the application via set of callbacks that user has to register:\n+\n+    rte_if_proxy_callbacks_register(&cbs);\n+\n+Here 'cbs' is a 'struct rte_if_proxy_callbacks' which has following\n+members:\n+\n+    void  (*mac_change)(uint16_t port_id, const struct rte_ether_addr *mac);\n+    void  (*mtu_change)(uint16_t port_id, uint16_t mtu);\n+    void (*link_change)(uint16_t port_id, int is_up);\n+    /* IPv4 addresses are in host order */\n+    void    (*addr_add)(uint16_t port_id, uint32_t ip);\n+    void    (*addr_del)(uint16_t port_id, uint32_t ip);\n+    void   (*addr6_add)(uint16_t port_id, const uint8_t *ip);\n+    void   (*addr6_del)(uint16_t port_id, const uint8_t *ip);\n+    void   (*route_add)(uint32_t ip, uint8_t depth);\n+    void   (*route_del)(uint32_t ip, uint8_t depth);\n+    void  (*route6_add)(const uint8_t *ip, uint8_t depth);\n+    void  (*route6_del)(const uint8_t *ip, uint8_t depth);\n+    /* lib specific callback - called when initial network configuration\n+     * query is finished */\n+    void (*cfg_finished)(void);\n+\n+So for example when the user issues command:\n+\n+    ip link set dev dtap0 mtu 1600\n+\n+then library will call `mtu_change()` callback with port_id equal to\n+'port0' (id of the port bound to this proxy) and 'mtu' equal to 1600\n+('dtap0' is the default interface name for 'net_tap0').\n+Application can simply use `rte_eth_dev_set_mtu()` as this callback.\n+The same way `rte_eth_dev_default_mac_addr_set()` can be used for\n+`mac_change()` and `rte_eth_dev_set_link_up/down()` can be used inside\n+the callback that does dispatch based on 'is_up' argument.\n+\n+Please note however that the context in which these callbacks are called\n+is most probably different from the one in which packets are handled and\n+it is application writer responsibility to use proper synchronization\n+mechanisms - if they are needed.\n+\n+If the application supports IP protocol stack then it can utilize\n+callbacks for adding/removing of addresses to the proxies and also\n+routing information (note that routing info is not associated with any\n+port).\n+E.g. application can feed some LPM tables with these addresses and upon\n+reception of a packet on some port match this packet against those\n+tables to figure out what to do with this packet.\n+If the decision is to pass it to the system then it can simply forward\n+them to the proxy corresponding to the port on which packet has been\n+received by using standard PMD TX interface.\ndiff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst\nindex dc4851c57..0a1541f34 100644\n--- a/doc/guides/prog_guide/index.rst\n+++ b/doc/guides/prog_guide/index.rst\n@@ -57,6 +57,7 @@ Programmer's Guide\n     metrics_lib\n     bpf_lib\n     ipsec_lib\n+    if_proxy_lib\n     source_org\n     dev_kit_build_system\n     dev_kit_root_make_help\ndiff --git a/examples/Makefile b/examples/Makefile\nindex feff79784..5aa9ab431 100644\n--- a/examples/Makefile\n+++ b/examples/Makefile\n@@ -81,6 +81,7 @@ else\n $(info vm_power_manager requires libvirt >= 0.9.3)\n endif\n endif\n+DIRS-$(CONFIG_RTE_LIBRTE_IF_PROXY) += if_proxy\n \n DIRS-y += eventdev_pipeline\n \ndiff --git a/examples/if_proxy/Makefile b/examples/if_proxy/Makefile\nnew file mode 100644\nindex 000000000..dd0515fa4\n--- /dev/null\n+++ b/examples/if_proxy/Makefile\n@@ -0,0 +1,58 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2019 Marvell International Ltd.\n+\n+# binary name\n+APP = if_proxy\n+\n+# all source are stored in SRCS-y\n+SRCS-y := main.c\n+\n+# Build using pkg-config variables if possible\n+ifeq ($(shell pkg-config --exists libdpdk && echo 0),0)\n+\n+all: shared\n+.PHONY: shared static\n+shared: build/$(APP)-shared\n+\tln -sf $(APP)-shared build/$(APP)\n+static: build/$(APP)-static\n+\tln -sf $(APP)-static build/$(APP)\n+\n+PKGCONF=pkg-config --define-prefix\n+\n+PC_FILE := $(shell $(PKGCONF) --path libdpdk)\n+CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk)\n+LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)\n+LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk)\n+\n+build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build\n+\t$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)\n+\n+build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build\n+\t$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)\n+\n+build:\n+\t@mkdir -p $@\n+\n+.PHONY: clean\n+clean:\n+\trm -f build/$(APP) build/$(APP)-static build/$(APP)-shared\n+\ttest -d build && rmdir -p build || true\n+\n+else # Build using legacy build system\n+\n+ifeq ($(RTE_SDK),)\n+$(error \"Please define RTE_SDK environment variable\")\n+endif\n+\n+# Default target, detect a build directory, by looking for a path with a .config\n+RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))\n+\n+include $(RTE_SDK)/mk/rte.vars.mk\n+\n+CFLAGS += -O3\n+CFLAGS += -DALLOW_EXPERIMENTAL_API\n+CFLAGS += $(WERROR_FLAGS)\n+LDLIBS += -lrte_if_proxy -lrte_ethdev -lrte_eal\n+\n+include $(RTE_SDK)/mk/rte.extapp.mk\n+endif\ndiff --git a/examples/if_proxy/main.c b/examples/if_proxy/main.c\nnew file mode 100644\nindex 000000000..2195fb490\n--- /dev/null\n+++ b/examples/if_proxy/main.c\n@@ -0,0 +1,203 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(C) 2020 Marvell International Ltd.\n+ */\n+\n+#include <rte_if_proxy.h>\n+\n+#include <string.h>\n+#include <unistd.h>\n+#include <signal.h>\n+#include <arpa/inet.h>\n+\n+static\n+char buf[INET6_ADDRSTRLEN];\n+\n+static\n+uint16_t proxy_id = RTE_MAX_ETHPORTS;\n+\n+static\n+void mac_change(uint16_t port_id, const struct rte_ether_addr *mac)\n+{\n+\tchar buf[3*RTE_ETHER_ADDR_LEN];\n+\n+\trte_ether_format_addr(buf, sizeof(buf), mac);\n+\tprintf(\"\\tmac change for port %u -> %s\\n\", port_id, buf);\n+}\n+\n+static\n+void mtu_change(uint16_t port_id, uint16_t mtu)\n+{\n+\tprintf(\"\\tmtu change for port %u -> %u\\n\", port_id, mtu);\n+}\n+\n+static\n+void link_change(uint16_t port_id, int is_up)\n+{\n+\tprintf(\"\\tport %u going %s\\n\", port_id, is_up ? \"up\" : \"down\");\n+}\n+\n+static\n+void addr_add(uint16_t port_id, uint32_t ip)\n+{\n+\tstruct in_addr a = { .s_addr = htonl(ip) };\n+\n+\tprintf(\"\\taddress add for port %u -> %s\\n\", port_id,\n+\t       inet_ntop(AF_INET, &a, buf, sizeof(buf)));\n+}\n+\n+static\n+void addr_del(uint16_t port_id, uint32_t ip)\n+{\n+\tstruct in_addr a = { .s_addr = htonl(ip) };\n+\n+\tprintf(\"\\taddress del for port %u -> %s\\n\", port_id,\n+\t       inet_ntop(AF_INET, &a, buf, sizeof(buf)));\n+}\n+\n+static\n+void addr6_add(uint16_t port_id, const uint8_t *ip)\n+{\n+\tstruct in6_addr a;\n+\n+\tmemcpy(a.s6_addr, ip, 16);\n+\tprintf(\"\\taddress6 add for port %u -> %s\\n\", port_id,\n+\t       inet_ntop(AF_INET6, &a, buf, sizeof(buf)));\n+}\n+\n+static\n+void addr6_del(uint16_t port_id, const uint8_t *ip)\n+{\n+\tstruct in6_addr a;\n+\n+\tmemcpy(a.s6_addr, ip, 16);\n+\tprintf(\"\\taddress6 del for port %u -> %s\\n\", port_id,\n+\t       inet_ntop(AF_INET6, &a, buf, sizeof(buf)));\n+}\n+\n+static\n+void route_add(uint32_t ip, uint8_t depth)\n+{\n+\tstruct in_addr a = { .s_addr = htonl(ip) };\n+\n+\tprintf(\"\\troute add -> %s/%u\\n\",\n+\t       inet_ntop(AF_INET, &a, buf, sizeof(buf)), depth);\n+}\n+\n+static\n+void route_del(uint32_t ip, uint8_t depth)\n+{\n+\tstruct in_addr a = { .s_addr = htonl(ip) };\n+\n+\tprintf(\"\\troute del -> %s/%u\\n\",\n+\t       inet_ntop(AF_INET, &a, buf, sizeof(buf)), depth);\n+}\n+\n+static\n+void route6_add(const uint8_t *ip, uint8_t depth)\n+{\n+\tstruct in6_addr a;\n+\n+\tmemcpy(a.s6_addr, ip, 16);\n+\tprintf(\"\\troute6 add -> %s/%u\\n\",\n+\t       inet_ntop(AF_INET6, &a, buf, sizeof(buf)), depth);\n+}\n+\n+static\n+void route6_del(const uint8_t *ip, uint8_t depth)\n+{\n+\tstruct in6_addr a;\n+\n+\tmemcpy(a.s6_addr, ip, 16);\n+\tprintf(\"\\troute6 del -> %s/%u\\n\",\n+\t       inet_ntop(AF_INET6, &a, buf, sizeof(buf)), depth);\n+}\n+\n+struct rte_ifpx_callbacks cbs = {\n+\t.mac_change = mac_change,\n+\t.mtu_change = mtu_change,\n+\t.link_change = link_change,\n+\t.addr_add = addr_add,\n+\t.addr_del = addr_del,\n+\t.addr6_add = addr6_add,\n+\t.addr6_del = addr6_del,\n+\t.route_add = route_add,\n+\t.route_del = route_del,\n+\t.route6_add = route6_add,\n+\t.route6_del = route6_del,\n+};\n+\n+static\n+void proxy_bind_change(int sig)\n+{\n+\tuint16_t port;\n+\tif (sig == SIGUSR1)\n+\t\tport = 0;\n+\telse if (sig == SIGUSR2)\n+\t\tport = 1;\n+\telse\n+\t\treturn;\n+\n+\tif (port >= rte_eth_dev_count_avail()) {\n+\t\tprintf(\"\\tNot enough ports allocated!\\n\");\n+\t\treturn;\n+\t}\n+\n+\tif (rte_ifpx_proxy_get(port) == RTE_MAX_ETHPORTS) {\n+\t\tprintf(\"\\tbinding port %d to proxy\\n\", port);\n+\t\trte_ifpx_port_bind(port, proxy_id);\n+\t} else {\n+\t\tprintf(\"\\tunbinding port %d\\n\", port);\n+\t\trte_ifpx_port_unbind(port);\n+\t}\n+}\n+\n+int\n+main(int argc, char **argv)\n+{\n+\tint i, sig, nb_ports;\n+\tsigset_t set;\n+\n+\t/* init EAL */\n+\ti = rte_eal_init(argc, argv);\n+\tif (i < 0)\n+\t\trte_exit(EXIT_FAILURE, \"Invalid EAL arguments\\n\");\n+\targc -= i;\n+\targv += i;\n+\n+\tnb_ports = rte_eth_dev_count_avail();\n+\tif (nb_ports == 0)\n+\t\trte_exit(EXIT_FAILURE, \"No Ethernet ports - bye\\n\");\n+\n+\tproxy_id = rte_ifpx_create(RTE_IFPX_DEFAULT);\n+\tif (proxy_id >= RTE_MAX_ETHPORTS) {\n+\t\tprintf(\"Failed to create default proxy\\n\");\n+\t\treturn -1;\n+\t}\n+\t/* Bind all ports to the same proxy. */\n+\tfor (i = 0; i < nb_ports; ++i)\n+\t\trte_ifpx_port_bind(i, proxy_id);\n+\trte_ifpx_callbacks_register(&cbs);\n+\trte_ifpx_listen();\n+\n+\t/* Since we do not process packets - only listen to net events - we only\n+\t * wait for signal either to quit or to change proxy binding.\n+\t */\n+\tsignal(SIGUSR1, proxy_bind_change);\n+\tsignal(SIGUSR2, proxy_bind_change);\n+\n+\tsigemptyset(&set);\n+\tsigaddset(&set, SIGINT);\n+\tsigprocmask(SIG_BLOCK, &set, NULL);\n+\tprintf(\"Press ^C to quit\\n\");\n+\tdo {\n+\t\ti = sigwait(&set, &sig);\n+\t} while (i != 0 && sig != SIGINT);\n+\n+\tRTE_ETH_FOREACH_DEV(i) {\n+\t\tprintf(\"\\nClosing port %d...\\n\", i);\n+\t\trte_eth_dev_close(i);\n+\t}\n+\tprintf(\"Bye\\n\");\n+\n+\treturn 0;\n+}\ndiff --git a/examples/if_proxy/meson.build b/examples/if_proxy/meson.build\nnew file mode 100644\nindex 000000000..5f5826a90\n--- /dev/null\n+++ b/examples/if_proxy/meson.build\n@@ -0,0 +1,12 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2019 Marvell International Ltd.\n+\n+# meson file, for building this example as part of a main DPDK build.\n+#\n+# To build this example as a standalone application with an already-installed\n+# DPDK instance, use 'make'\n+\n+allow_experimental_apis = true\n+sources = files(\n+\t'main.c'\n+)\ndiff --git a/examples/meson.build b/examples/meson.build\nindex 1f2b6f516..468ef8a90 100644\n--- a/examples/meson.build\n+++ b/examples/meson.build\n@@ -16,7 +16,7 @@ all_examples = [\n \t'eventdev_pipeline',\n \t'fips_validation', 'flow_classify',\n \t'flow_filtering', 'helloworld',\n-\t'ioat',\n+\t'if_proxy', 'ioat',\n \t'ip_fragmentation', 'ip_pipeline',\n \t'ip_reassembly', 'ipsec-secgw',\n \t'ipv4_multicast', 'kni',\n",
    "prefixes": [
        "RFC",
        "3/3"
    ]
}