From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id C5DC7A04FF; Tue, 14 Jan 2020 15:25:57 +0100 (CET) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 6E49F1C1C1; Tue, 14 Jan 2020 15:25:33 +0100 (CET) Received: from mx0b-0016f401.pphosted.com (mx0a-0016f401.pphosted.com [67.231.148.174]) by dpdk.org (Postfix) with ESMTP id 561EA1C1C1 for ; Tue, 14 Jan 2020 15:25:31 +0100 (CET) Received: from pps.filterd (m0045849.ppops.net [127.0.0.1]) by mx0a-0016f401.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 00EEKULf013366; Tue, 14 Jan 2020 06:25:30 -0800 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=pfpt0818; bh=31ESREwEYa4sdPp199Ejs1RynvMaRAj7vKifLQW5QkE=; b=GqHJWwuwoq1L6RxAjKVjRoM9yJMLNMopyDSH+N8OGRJzpr6Lt/x02OXclmljmHd34wH/ jUzBMIycrtAYyv0UMq7jx8RQC06Uly6gRMGBJJ1TBu36sYtnMpxw/20jaV2l8MVy2Jyp Lcxcno34mobxWlLhDR3IQFSzj5VV4JSh5wMwMZzlEgsHEhmgYxxx8NK/sHliMCMm4iFp IzxYfKv3UfSJ7Fk1oIhvn+MF4z/Lz/i5ybfG57IgB9B+8wIai6wBlUad8F5Igqcu8NRi t+jXfhpb6VaIN8ojDBzXHWL77RD7rOhz9sQBJiz7LfUiyriQZ7r6zpGDpk4i/gHmItkV 1Q== Received: from sc-exch01.marvell.com ([199.233.58.181]) by mx0a-0016f401.pphosted.com with ESMTP id 2xhc6sgngg-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Tue, 14 Jan 2020 06:25:30 -0800 Received: from SC-EXCH03.marvell.com (10.93.176.83) by SC-EXCH01.marvell.com (10.93.176.81) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Tue, 14 Jan 2020 06:25:28 -0800 Received: from maili.marvell.com (10.93.176.43) by SC-EXCH03.marvell.com (10.93.176.83) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Tue, 14 Jan 2020 06:25:28 -0800 Received: from amok.marvell.com (unknown [10.95.130.253]) by maili.marvell.com (Postfix) with ESMTP id 6BD153F7040; Tue, 14 Jan 2020 06:25:26 -0800 (PST) From: Andrzej Ostruszka To: , John McNamara , Marko Kovacevic CC: Jerin Jacob Kollanukkaran , Nithin Kumar Dabilpuram , Pavan Nikhilesh Bhagavatula , Kiran Kumar Kokkilagadda , Krzysztof Kanas 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 definitions=2020-01-14_04:2020-01-13, 2020-01-14 signatures=0 Subject: [dpdk-dev] [RFC PATCH 3/3] if_proxy: add example, test and documentation X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" This commit adds a test, documentation and a small example. The example just creates one proxy port and binds all ports available to it. Then you can play around with changing of network configuration of this proxy port and you should observe notifications from the appropriate callbacks. Below is an exemplary output (with some parts elided and some comments added) - 'dtap0' is the name of the proxy interface. sudo ./if_proxy -w 00:03.0 -w 00:04.0 ... Press ^C to quit route add -> 10.0.0.0/16 route add -> 192.168.123.0/24 ... route6 add -> ::1/128 route6 add -> fe80::/64 route6 add -> fe80::ee05:deaf:6827:b435/128 ... [[ output on: ip link set dtap0 mtu 1600 ]] mtu change for port 0 -> 1600 mtu change for port 1 -> 1600 [[ output on: ip link set dtap0 up ]] port 0 going up port 1 going up route6 add -> ff00::/8 route6 add -> fe80::/64 address6 add for port 0 -> fe80::2436:17ff:fefd:94ed address6 add for port 1 -> fe80::2436:17ff:fefd:94ed route6 add -> fe80::2436:17ff:fefd:94ed/128 Signed-off-by: Andrzej Ostruszka --- app/test/Makefile | 5 + app/test/meson.build | 1 + app/test/test_if_proxy.c | 431 +++++++++++++++++++++++++ doc/guides/prog_guide/if_proxy_lib.rst | 103 ++++++ doc/guides/prog_guide/index.rst | 1 + examples/Makefile | 1 + examples/if_proxy/Makefile | 58 ++++ examples/if_proxy/main.c | 203 ++++++++++++ examples/if_proxy/meson.build | 12 + examples/meson.build | 2 +- 10 files changed, 816 insertions(+), 1 deletion(-) create mode 100644 app/test/test_if_proxy.c create mode 100644 doc/guides/prog_guide/if_proxy_lib.rst create mode 100644 examples/if_proxy/Makefile create mode 100644 examples/if_proxy/main.c create mode 100644 examples/if_proxy/meson.build diff --git a/app/test/Makefile b/app/test/Makefile index 57930c00b..f621978d7 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -230,6 +230,11 @@ SRCS-$(CONFIG_RTE_LIBRTE_BPF) += test_bpf.c SRCS-$(CONFIG_RTE_LIBRTE_RCU) += test_rcu_qsbr.c test_rcu_qsbr_perf.c +ifeq ($(CONFIG_RTE_LIBRTE_IF_PROXY),y) +SRCS-y += test_if_proxy.c +LDLIBS += -lrte_if_proxy +endif + SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += test_ipsec.c SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += test_ipsec_sad.c ifeq ($(CONFIG_RTE_LIBRTE_IPSEC),y) diff --git a/app/test/meson.build b/app/test/meson.build index fb49d804b..2a3b5fef2 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -61,6 +61,7 @@ test_sources = files('commands.c', 'test_hash_perf.c', 'test_hash_readwrite_lf.c', 'test_interrupts.c', + 'test_if_proxy.c', 'test_ipsec.c', 'test_ipsec_sad.c', 'test_kni.c', diff --git a/app/test/test_if_proxy.c b/app/test/test_if_proxy.c new file mode 100644 index 000000000..0ecfb79b4 --- /dev/null +++ b/app/test/test_if_proxy.c @@ -0,0 +1,431 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2020 Marvell International Ltd. + */ + +#include "test.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + +enum net_op { + INITIALIZED = 1U << 0, + LOOP_ROUTE = 1U << 1, + LOOP6_ROUTE = 1U << 2, + LINK_CHANGED = 1U << 3, + MAC_CHANGED = 1U << 4, + MTU_CHANGED = 1U << 5, + ADDR_ADD = 1U << 6, + ADDR_DEL = 1U << 7, + ROUTE_ADD = 1U << 8, + ROUTE_DEL = 1U << 9, + ADDR6_ADD = 1U << 10, + ADDR6_DEL = 1U << 11, + ROUTE6_ADD = 1U << 12, + ROUTE6_DEL = 1U << 13, +}; + +static unsigned int state; + +static struct { + struct rte_ether_addr mac_addr; + uint16_t port_id, mtu; + struct in_addr ipv4, route4; + struct in6_addr ipv6, route6; + uint16_t depth4, depth6; + int is_up; +} net_cfg; + +static +int unlock_notify(unsigned int op) +{ + /* the mutex is expected to be locked on entry */ + RTE_VERIFY(pthread_mutex_trylock(&mutex) == EBUSY); + state |= op; + + pthread_mutex_unlock(&mutex); + return pthread_cond_signal(&cond); +} + +static +int wait_for(unsigned int op_mask, unsigned int sec) +{ + struct timespec time; + int ec = pthread_mutex_trylock(&mutex); + + /* the mutex is expected to be locked on entry */ + RTE_VERIFY(ec == EBUSY); + + ec = 0; + clock_gettime(CLOCK_REALTIME, &time); + time.tv_sec += sec; + + while ((state & op_mask) != op_mask && ec == 0) + ec = pthread_cond_timedwait(&cond, &mutex, &time); + + return ec; +} + +static +int expect(unsigned int op_mask, const char *fmt, ...) +#if __GNUC__ + __attribute__((format(printf, 2, 3))); +#endif + +static +int expect(unsigned int op_mask, const char *fmt, ...) +{ + char cmd[128]; + va_list args; + int ret; + + state &= ~op_mask; + va_start(args, fmt); + vsnprintf(cmd, sizeof(cmd), fmt, args); + va_end(args); + ret = system(cmd); + if (ret == 0) + /* IPv6 address notifications seem to need that long delay. */ + return wait_for(op_mask, 2); + return ret; +} + +static +void mac_change(uint16_t port_id, const struct rte_ether_addr *mac) +{ + pthread_mutex_lock(&mutex); + RTE_VERIFY(port_id == net_cfg.port_id); + if (memcmp(mac->addr_bytes, net_cfg.mac_addr.addr_bytes, + RTE_ETHER_ADDR_LEN) == 0) { + unlock_notify(MAC_CHANGED); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void mtu_change(uint16_t port_id, uint16_t mtu) +{ + pthread_mutex_lock(&mutex); + RTE_VERIFY(port_id == net_cfg.port_id); + if (net_cfg.mtu == mtu) { + unlock_notify(MTU_CHANGED); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void link_change(uint16_t port_id, int is_up) +{ + pthread_mutex_lock(&mutex); + RTE_VERIFY(port_id == net_cfg.port_id); + if (net_cfg.is_up == is_up) { + unlock_notify(LINK_CHANGED); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void addr_add(uint16_t port_id, uint32_t ip) +{ + pthread_mutex_lock(&mutex); + RTE_VERIFY(port_id == net_cfg.port_id); + if (net_cfg.ipv4.s_addr == ip) { + unlock_notify(ADDR_ADD); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void addr_del(uint16_t port_id, uint32_t ip) +{ + pthread_mutex_lock(&mutex); + RTE_VERIFY(port_id == net_cfg.port_id); + if (net_cfg.ipv4.s_addr == ip) { + unlock_notify(ADDR_DEL); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void addr6_add(uint16_t port_id, const uint8_t *ip) +{ + pthread_mutex_lock(&mutex); + RTE_VERIFY(port_id == net_cfg.port_id); + if (memcmp(ip, net_cfg.ipv6.s6_addr, 16) == 0) { + unlock_notify(ADDR6_ADD); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void addr6_del(uint16_t port_id __rte_unused, const uint8_t *ip) +{ + pthread_mutex_lock(&mutex); + RTE_VERIFY(port_id == net_cfg.port_id); + if (memcmp(ip, net_cfg.ipv6.s6_addr, 16) == 0) { + unlock_notify(ADDR6_DEL); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void route_add(uint32_t ip, uint8_t depth) +{ + pthread_mutex_lock(&mutex); + /* Since we are checking if during initialization we get some routing + * info we need to notify either when we are not initialized or when + * the exact route matches. + */ + if (!(state & INITIALIZED) || + (net_cfg.depth4 == depth && net_cfg.route4.s_addr == ip)) { + unlock_notify(ROUTE_ADD); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void route_del(uint32_t ip, uint8_t depth) +{ + pthread_mutex_lock(&mutex); + if (net_cfg.depth4 == depth && net_cfg.route4.s_addr == ip) { + unlock_notify(ROUTE_DEL); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void route6_add(const uint8_t *ip, uint8_t depth) +{ + pthread_mutex_lock(&mutex); + /* Since we are checking if during initialization we get some routing + * info we need to notify either when we are not initialized or when + * the exact route matches. + */ + if (!(state & INITIALIZED) || + (net_cfg.depth6 == depth && + /* don't check for trailing zeros */ + memcmp(ip, net_cfg.route6.s6_addr, depth/8) == 0)) { + unlock_notify(ROUTE6_ADD); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void route6_del(const uint8_t *ip, uint8_t depth) +{ + pthread_mutex_lock(&mutex); + if (net_cfg.depth6 == depth && + /* don't check for trailing zeros */ + memcmp(ip, net_cfg.route6.s6_addr, depth/8) == 0) { + unlock_notify(ROUTE6_DEL); + return; + } + pthread_mutex_unlock(&mutex); +} + +static +void cfg_finished(void) +{ + pthread_mutex_lock(&mutex); + unlock_notify(INITIALIZED); +} + +static +struct rte_ifpx_callbacks cbs = { + .mac_change = mac_change, + .mtu_change = mtu_change, + .link_change = link_change, + .addr_add = addr_add, + .addr_del = addr_del, + .addr6_add = addr6_add, + .addr6_del = addr6_del, + .route_add = route_add, + .route_del = route_del, + .route6_add = route6_add, + .route6_del = route6_del, + /* lib specific callback */ + .cfg_finished = cfg_finished, +}; + +static int +test_if_proxy(void) +{ + int ec; + char buf[INET6_ADDRSTRLEN]; + const struct rte_ifpx_info *pinfo; + + state = 0; + memset(&net_cfg, 0, sizeof(net_cfg)); + /* Since we are not going to test RX/TX we can just create proxy and + * bind it to itself to test just notification functionality. + */ + net_cfg.port_id = rte_ifpx_create(RTE_IFPX_DEFAULT); + RTE_VERIFY(net_cfg.port_id != RTE_MAX_ETHPORTS); + rte_ifpx_port_bind(net_cfg.port_id, net_cfg.port_id); + rte_ifpx_callbacks_register(&cbs); + rte_ifpx_listen(); + + pthread_mutex_lock(&mutex); + /* During initialization we should observe IPv4/6 loopback routes. */ + net_cfg.route4.s_addr = RTE_IPV4(127, 0, 0, 1); + net_cfg.depth4 = 32; + memcpy(net_cfg.route6.s6_addr, in6addr_loopback.s6_addr, 16); + net_cfg.depth6 = 128; + ec = wait_for(INITIALIZED | ROUTE_ADD | ROUTE6_ADD, 2); + if (ec != 0) { + printf("Failed to obtain network configuration\n"); + goto exit; + } + pinfo = rte_ifpx_info_get(net_cfg.port_id); + RTE_VERIFY(pinfo); + + /* Make sure the link is down. */ + net_cfg.is_up = 0; + ec = expect(LINK_CHANGED, "ip link set dev %s down", pinfo->if_name); + RTE_VERIFY(ec == ETIMEDOUT || ec == 0); + + /* Test link up notification. */ + net_cfg.is_up = 1; + ec = expect(LINK_CHANGED, "ip link set dev %s up", pinfo->if_name); + if (ec != 0) { + printf("Failed to notify about link going up\n"); + goto exit; + } + + /* Test for MAC changes notification. */ + rte_eth_random_addr(net_cfg.mac_addr.addr_bytes); + rte_ether_format_addr(buf, sizeof(buf), &net_cfg.mac_addr); + ec = expect(MAC_CHANGED, "ip link set dev %s address %s", + pinfo->if_name, buf); + if (ec != 0) { + printf("Missing/wrong notification about mac change\n"); + goto exit; + } + + /* Test for MTU changes notification. */ + net_cfg.mtu = pinfo->mtu + 100; + ec = expect(MTU_CHANGED, "ip link set dev %s mtu %d", + pinfo->if_name, net_cfg.mtu); + if (ec != 0) { + printf("Missing/wrong notification about mtu change\n"); + goto exit; + } + + /* Test for adding of IPv4 address - using address from TEST-2 pool. + * This test is specific to linux netlink behaviour - after adding + * address we get both notification about address being added and new + * route. So I check both. + */ + net_cfg.ipv4.s_addr = RTE_IPV4(198, 51, 100, 14); + net_cfg.route4.s_addr = net_cfg.ipv4.s_addr; + net_cfg.depth4 = 32; + ec = expect(ADDR_ADD | ROUTE_ADD, "ip addr add 198.51.100.14 dev %s", + pinfo->if_name); + if (ec != 0) { + printf("Missing/wrong notifications about IPv4 address add\n"); + goto exit; + } + + /* Test for IPv4 address removal. See comment above for 'addr add'. */ + ec = expect(ADDR_DEL | ROUTE_DEL, "ip addr del 198.51.100.14/32 dev %s", + pinfo->if_name); + if (ec != 0) { + printf("Missing/wrong notifications about IPv4 address del\n"); + goto exit; + } + + /* Test for adding IPv4 route. */ + net_cfg.route4.s_addr = RTE_IPV4(198, 51, 100, 0); + net_cfg.depth4 = 24; + ec = expect(ROUTE_ADD, "ip route add 198.51.100.0/24 dev %s", + pinfo->if_name); + if (ec != 0) { + printf("Missing/wrong notifications about IPv4 route add\n"); + goto exit; + } + + /* Test for IPv4 route removal. */ + ec = expect(ROUTE_DEL, "ip route del 198.51.100.0/24 dev %s", + pinfo->if_name); + if (ec != 0) { + printf("Missing/wrong notifications about IPv4 route del\n"); + goto exit; + } + + /* Now the same for IPv6 - with address from "documentation pool". */ + inet_pton(AF_INET6, "2001:db8::dead:beef", net_cfg.ipv6.s6_addr); + /* This is specific to linux netlink behaviour - after adding address + * we get both notification about address being added and new route. + * So I wait for both. + */ + memcpy(net_cfg.route6.s6_addr, net_cfg.ipv6.s6_addr, 16); + net_cfg.depth6 = 128; + ec = expect(ADDR6_ADD | ROUTE6_ADD, + "ip addr add 2001:db8::dead:beef dev %s", + pinfo->if_name); + if (ec != 0) { + printf("Missing/wrong notifications about IPv6 address add\n"); + goto exit; + } + + /* See comment above for 'addr6 add'. */ + ec = expect(ADDR6_DEL | ROUTE6_DEL, + "ip addr del 2001:db8::dead:beef/128 dev %s", + pinfo->if_name); + if (ec != 0) { + printf("Missing/wrong notifications about IPv6 address del\n"); + goto exit; + } + + net_cfg.depth6 = 96; + ec = expect(ROUTE6_ADD, "ip route add 2001:db8::dead:0/96 dev %s", + pinfo->if_name); + if (ec != 0) { + printf("Missing/wrong notifications about IPv6 route add\n"); + goto exit; + } + + ec = expect(ROUTE6_DEL, "ip route del 2001:db8::dead:0/96 dev %s", + pinfo->if_name); + if (ec != 0) { + printf("Missing/wrong notifications about IPv6 route del\n"); + goto exit; + } + + /* Finally put link down and test for notification. */ + net_cfg.is_up = 0; + ec = expect(LINK_CHANGED, "ip link set dev %s down", pinfo->if_name); + if (ec != 0) { + printf("Failed to notify about link going down\n"); + goto exit; + } + +exit: + pthread_mutex_unlock(&mutex); + rte_ifpx_destroy(net_cfg.port_id); + rte_ifpx_close(); + + return ec; +} + +REGISTER_TEST_COMMAND(if_proxy_autotest, test_if_proxy) diff --git a/doc/guides/prog_guide/if_proxy_lib.rst b/doc/guides/prog_guide/if_proxy_lib.rst new file mode 100644 index 000000000..dc1202cdf --- /dev/null +++ b/doc/guides/prog_guide/if_proxy_lib.rst @@ -0,0 +1,103 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(C) 2019 Marvell International Ltd. + +.. _IF_Proxy_Library: + +IF Proxy Library +================ + +When a network interface is assigned to DPDK it usually disappears from +the system. +This way user looses ability to configure it via typical configuration +tools and is left basically with two options: + + - configure it via command line arguments, + + - add support for live configuration via some IPC mechanism. + +The first option is static and the second one requires some work to add +communication loop (e.g. separate thread listening/communicating on +a socket). + +This library adds a possibility to configure DPDK ports by using normal +configuration utilities (e.g. from iproute2 suite). +It requires user to configure additional DPDK ports that are visible to +the system (such as Tap or KNI - actually any port that has valid +'if_index' in 'struct rte_eth_dev_info' will do) and designate them as +a port representor (a proxy) in the system. + +Let's see typical intended usage by an example. +Suppose that you have application that handles traffic on two ports (in +the white list below). + + ./app -w 00:14.0 -w 00:16.0 --vdev=net_tap0 --vdev=net_tap1 + +So in addition you configure two proxy ports and in the application code +you bind them to the "main" ports: + + rte_if_proxy_port_bind(port0, proxy0); + rte_if_proxy_port_bind(port1, proxy1); + +This binding is a logical one - there is no automatic packet forwarding +configured. +This is because library cannot tell upfront what portion of the traffic +received on ports 0/1 should be redirected to the system via proxies and +also it does not know how the application is structured (what packet +processing engines it uses). +Therefore it is application writer responsibility to include proxy ports +into its packet processing and forward appropriate packets between +proxies and ports. +What the library actually does is that it gets network configuration +from the system and listens to its changes. +This information is then matched against 'if_index' of the configured +proxies (when applicable - routing information is global) and passed to +the application via set of callbacks that user has to register: + + rte_if_proxy_callbacks_register(&cbs); + +Here 'cbs' is a 'struct rte_if_proxy_callbacks' which has following +members: + + void (*mac_change)(uint16_t port_id, const struct rte_ether_addr *mac); + void (*mtu_change)(uint16_t port_id, uint16_t mtu); + void (*link_change)(uint16_t port_id, int is_up); + /* IPv4 addresses are in host order */ + void (*addr_add)(uint16_t port_id, uint32_t ip); + void (*addr_del)(uint16_t port_id, uint32_t ip); + void (*addr6_add)(uint16_t port_id, const uint8_t *ip); + void (*addr6_del)(uint16_t port_id, const uint8_t *ip); + void (*route_add)(uint32_t ip, uint8_t depth); + void (*route_del)(uint32_t ip, uint8_t depth); + void (*route6_add)(const uint8_t *ip, uint8_t depth); + void (*route6_del)(const uint8_t *ip, uint8_t depth); + /* lib specific callback - called when initial network configuration + * query is finished */ + void (*cfg_finished)(void); + +So for example when the user issues command: + + ip link set dev dtap0 mtu 1600 + +then library will call `mtu_change()` callback with port_id equal to +'port0' (id of the port bound to this proxy) and 'mtu' equal to 1600 +('dtap0' is the default interface name for 'net_tap0'). +Application can simply use `rte_eth_dev_set_mtu()` as this callback. +The same way `rte_eth_dev_default_mac_addr_set()` can be used for +`mac_change()` and `rte_eth_dev_set_link_up/down()` can be used inside +the callback that does dispatch based on 'is_up' argument. + +Please note however that the context in which these callbacks are called +is most probably different from the one in which packets are handled and +it is application writer responsibility to use proper synchronization +mechanisms - if they are needed. + +If the application supports IP protocol stack then it can utilize +callbacks for adding/removing of addresses to the proxies and also +routing information (note that routing info is not associated with any +port). +E.g. application can feed some LPM tables with these addresses and upon +reception of a packet on some port match this packet against those +tables to figure out what to do with this packet. +If the decision is to pass it to the system then it can simply forward +them to the proxy corresponding to the port on which packet has been +received by using standard PMD TX interface. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index dc4851c57..0a1541f34 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -57,6 +57,7 @@ Programmer's Guide metrics_lib bpf_lib ipsec_lib + if_proxy_lib source_org dev_kit_build_system dev_kit_root_make_help diff --git a/examples/Makefile b/examples/Makefile index feff79784..5aa9ab431 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -81,6 +81,7 @@ else $(info vm_power_manager requires libvirt >= 0.9.3) endif endif +DIRS-$(CONFIG_RTE_LIBRTE_IF_PROXY) += if_proxy DIRS-y += eventdev_pipeline diff --git a/examples/if_proxy/Makefile b/examples/if_proxy/Makefile new file mode 100644 index 000000000..dd0515fa4 --- /dev/null +++ b/examples/if_proxy/Makefile @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2019 Marvell International Ltd. + +# binary name +APP = if_proxy + +# all source are stored in SRCS-y +SRCS-y := main.c + +# Build using pkg-config variables if possible +ifeq ($(shell pkg-config --exists libdpdk && echo 0),0) + +all: shared +.PHONY: shared static +shared: build/$(APP)-shared + ln -sf $(APP)-shared build/$(APP) +static: build/$(APP)-static + ln -sf $(APP)-static build/$(APP) + +PKGCONF=pkg-config --define-prefix + +PC_FILE := $(shell $(PKGCONF) --path libdpdk) +CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk) +LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk) +LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk) + +build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED) + +build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC) + +build: + @mkdir -p $@ + +.PHONY: clean +clean: + rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared + test -d build && rmdir -p build || true + +else # Build using legacy build system + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, detect a build directory, by looking for a path with a .config +RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config))))) + +include $(RTE_SDK)/mk/rte.vars.mk + +CFLAGS += -O3 +CFLAGS += -DALLOW_EXPERIMENTAL_API +CFLAGS += $(WERROR_FLAGS) +LDLIBS += -lrte_if_proxy -lrte_ethdev -lrte_eal + +include $(RTE_SDK)/mk/rte.extapp.mk +endif diff --git a/examples/if_proxy/main.c b/examples/if_proxy/main.c new file mode 100644 index 000000000..2195fb490 --- /dev/null +++ b/examples/if_proxy/main.c @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2020 Marvell International Ltd. + */ + +#include + +#include +#include +#include +#include + +static +char buf[INET6_ADDRSTRLEN]; + +static +uint16_t proxy_id = RTE_MAX_ETHPORTS; + +static +void mac_change(uint16_t port_id, const struct rte_ether_addr *mac) +{ + char buf[3*RTE_ETHER_ADDR_LEN]; + + rte_ether_format_addr(buf, sizeof(buf), mac); + printf("\tmac change for port %u -> %s\n", port_id, buf); +} + +static +void mtu_change(uint16_t port_id, uint16_t mtu) +{ + printf("\tmtu change for port %u -> %u\n", port_id, mtu); +} + +static +void link_change(uint16_t port_id, int is_up) +{ + printf("\tport %u going %s\n", port_id, is_up ? "up" : "down"); +} + +static +void addr_add(uint16_t port_id, uint32_t ip) +{ + struct in_addr a = { .s_addr = htonl(ip) }; + + printf("\taddress add for port %u -> %s\n", port_id, + inet_ntop(AF_INET, &a, buf, sizeof(buf))); +} + +static +void addr_del(uint16_t port_id, uint32_t ip) +{ + struct in_addr a = { .s_addr = htonl(ip) }; + + printf("\taddress del for port %u -> %s\n", port_id, + inet_ntop(AF_INET, &a, buf, sizeof(buf))); +} + +static +void addr6_add(uint16_t port_id, const uint8_t *ip) +{ + struct in6_addr a; + + memcpy(a.s6_addr, ip, 16); + printf("\taddress6 add for port %u -> %s\n", port_id, + inet_ntop(AF_INET6, &a, buf, sizeof(buf))); +} + +static +void addr6_del(uint16_t port_id, const uint8_t *ip) +{ + struct in6_addr a; + + memcpy(a.s6_addr, ip, 16); + printf("\taddress6 del for port %u -> %s\n", port_id, + inet_ntop(AF_INET6, &a, buf, sizeof(buf))); +} + +static +void route_add(uint32_t ip, uint8_t depth) +{ + struct in_addr a = { .s_addr = htonl(ip) }; + + printf("\troute add -> %s/%u\n", + inet_ntop(AF_INET, &a, buf, sizeof(buf)), depth); +} + +static +void route_del(uint32_t ip, uint8_t depth) +{ + struct in_addr a = { .s_addr = htonl(ip) }; + + printf("\troute del -> %s/%u\n", + inet_ntop(AF_INET, &a, buf, sizeof(buf)), depth); +} + +static +void route6_add(const uint8_t *ip, uint8_t depth) +{ + struct in6_addr a; + + memcpy(a.s6_addr, ip, 16); + printf("\troute6 add -> %s/%u\n", + inet_ntop(AF_INET6, &a, buf, sizeof(buf)), depth); +} + +static +void route6_del(const uint8_t *ip, uint8_t depth) +{ + struct in6_addr a; + + memcpy(a.s6_addr, ip, 16); + printf("\troute6 del -> %s/%u\n", + inet_ntop(AF_INET6, &a, buf, sizeof(buf)), depth); +} + +struct rte_ifpx_callbacks cbs = { + .mac_change = mac_change, + .mtu_change = mtu_change, + .link_change = link_change, + .addr_add = addr_add, + .addr_del = addr_del, + .addr6_add = addr6_add, + .addr6_del = addr6_del, + .route_add = route_add, + .route_del = route_del, + .route6_add = route6_add, + .route6_del = route6_del, +}; + +static +void proxy_bind_change(int sig) +{ + uint16_t port; + if (sig == SIGUSR1) + port = 0; + else if (sig == SIGUSR2) + port = 1; + else + return; + + if (port >= rte_eth_dev_count_avail()) { + printf("\tNot enough ports allocated!\n"); + return; + } + + if (rte_ifpx_proxy_get(port) == RTE_MAX_ETHPORTS) { + printf("\tbinding port %d to proxy\n", port); + rte_ifpx_port_bind(port, proxy_id); + } else { + printf("\tunbinding port %d\n", port); + rte_ifpx_port_unbind(port); + } +} + +int +main(int argc, char **argv) +{ + int i, sig, nb_ports; + sigset_t set; + + /* init EAL */ + i = rte_eal_init(argc, argv); + if (i < 0) + rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); + argc -= i; + argv += i; + + nb_ports = rte_eth_dev_count_avail(); + if (nb_ports == 0) + rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); + + proxy_id = rte_ifpx_create(RTE_IFPX_DEFAULT); + if (proxy_id >= RTE_MAX_ETHPORTS) { + printf("Failed to create default proxy\n"); + return -1; + } + /* Bind all ports to the same proxy. */ + for (i = 0; i < nb_ports; ++i) + rte_ifpx_port_bind(i, proxy_id); + rte_ifpx_callbacks_register(&cbs); + rte_ifpx_listen(); + + /* Since we do not process packets - only listen to net events - we only + * wait for signal either to quit or to change proxy binding. + */ + signal(SIGUSR1, proxy_bind_change); + signal(SIGUSR2, proxy_bind_change); + + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigprocmask(SIG_BLOCK, &set, NULL); + printf("Press ^C to quit\n"); + do { + i = sigwait(&set, &sig); + } while (i != 0 && sig != SIGINT); + + RTE_ETH_FOREACH_DEV(i) { + printf("\nClosing port %d...\n", i); + rte_eth_dev_close(i); + } + printf("Bye\n"); + + return 0; +} diff --git a/examples/if_proxy/meson.build b/examples/if_proxy/meson.build new file mode 100644 index 000000000..5f5826a90 --- /dev/null +++ b/examples/if_proxy/meson.build @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2019 Marvell International Ltd. + +# meson file, for building this example as part of a main DPDK build. +# +# To build this example as a standalone application with an already-installed +# DPDK instance, use 'make' + +allow_experimental_apis = true +sources = files( + 'main.c' +) diff --git a/examples/meson.build b/examples/meson.build index 1f2b6f516..468ef8a90 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -16,7 +16,7 @@ all_examples = [ 'eventdev_pipeline', 'fips_validation', 'flow_classify', 'flow_filtering', 'helloworld', - 'ioat', + 'if_proxy', 'ioat', 'ip_fragmentation', 'ip_pipeline', 'ip_reassembly', 'ipsec-secgw', 'ipv4_multicast', 'kni', -- 2.17.1