@@ -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)
@@ -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',
new file mode 100644
@@ -0,0 +1,431 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "test.h"
+
+#include <rte_ethdev.h>
+#include <rte_if_proxy.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <time.h>
+
+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)
new file mode 100644
@@ -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.
@@ -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
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_if_proxy.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <arpa/inet.h>
+
+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;
+}
new file mode 100644
@@ -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'
+)
@@ -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',