summaryrefslogtreecommitdiffstats
path: root/src/spdk/dpdk/drivers/net/tap
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/dpdk/drivers/net/tap')
-rw-r--r--src/spdk/dpdk/drivers/net/tap/Makefile99
-rw-r--r--src/spdk/dpdk/drivers/net/tap/rte_eth_tap.c2140
-rw-r--r--src/spdk/dpdk/drivers/net/tap/rte_eth_tap.h99
-rw-r--r--src/spdk/dpdk/drivers/net/tap/rte_pmd_tap_version.map4
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_bpf.h117
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_bpf_api.c190
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_bpf_insns.h1696
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_bpf_program.c224
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_flow.c2191
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_flow.h68
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_intr.c110
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_log.h10
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_netlink.c340
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_netlink.h42
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_rss.h40
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_tcmsgs.c296
-rw-r--r--src/spdk/dpdk/drivers/net/tap/tap_tcmsgs.h37
17 files changed, 7703 insertions, 0 deletions
diff --git a/src/spdk/dpdk/drivers/net/tap/Makefile b/src/spdk/dpdk/drivers/net/tap/Makefile
new file mode 100644
index 00000000..32433653
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/Makefile
@@ -0,0 +1,99 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2016 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+#
+# library name
+#
+LIB = librte_pmd_tap.a
+
+EXPORT_MAP := rte_pmd_tap_version.map
+
+LIBABIVER := 1
+
+#
+# TAP_MAX_QUEUES must be a power of 2
+#
+ifeq ($(TAP_MAX_QUEUES),)
+ TAP_MAX_QUEUES = 16
+endif
+CFLAGS += -O3
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -I.
+CFLAGS += $(WERROR_FLAGS)
+LDLIBS += -lrte_eal -lrte_mbuf -lrte_mempool -lrte_ring
+LDLIBS += -lrte_ethdev -lrte_net -lrte_kvargs -lrte_hash
+LDLIBS += -lrte_bus_vdev -lrte_gso
+
+CFLAGS += -DTAP_MAX_QUEUES=$(TAP_MAX_QUEUES)
+
+#
+# all source are stored in SRCS-y
+#
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += rte_eth_tap.c
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += tap_flow.c
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += tap_netlink.c
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += tap_tcmsgs.c
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += tap_bpf_api.c
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_TAP) += tap_intr.c
+
+include $(RTE_SDK)/mk/rte.lib.mk
+
+# Generate and clean-up tap_autoconf.h.
+
+export CC CFLAGS CPPFLAGS EXTRA_CFLAGS EXTRA_CPPFLAGS
+export AUTO_CONFIG_CFLAGS = -Wno-error
+
+ifndef V
+AUTOCONF_OUTPUT := >/dev/null
+endif
+
+tap_autoconf.h.new: FORCE
+
+tap_autoconf.h.new: $(RTE_SDK)/buildtools/auto-config-h.sh
+ $Q $(RM) -f -- '$@'
+ $Q sh -- '$<' '$@' \
+ HAVE_TC_FLOWER \
+ linux/pkt_cls.h \
+ enum TCA_FLOWER_UNSPEC \
+ $(AUTOCONF_OUTPUT)
+ $Q sh -- '$<' '$@' \
+ HAVE_TC_VLAN_ID \
+ linux/pkt_cls.h \
+ enum TCA_FLOWER_KEY_VLAN_PRIO \
+ $(AUTOCONF_OUTPUT)
+ $Q sh -- '$<' '$@' \
+ HAVE_TC_BPF \
+ linux/pkt_cls.h \
+ enum TCA_BPF_UNSPEC \
+ $(AUTOCONF_OUTPUT)
+ $Q sh -- '$<' '$@' \
+ HAVE_TC_BPF_FD \
+ linux/pkt_cls.h \
+ enum TCA_BPF_FD \
+ $(AUTOCONF_OUTPUT)
+ $Q sh -- '$<' '$@' \
+ HAVE_TC_ACT_BPF \
+ linux/tc_act/tc_bpf.h \
+ enum TCA_ACT_BPF_UNSPEC \
+ $(AUTOCONF_OUTPUT)
+ $Q sh -- '$<' '$@' \
+ HAVE_TC_ACT_BPF_FD \
+ linux/tc_act/tc_bpf.h \
+ enum TCA_ACT_BPF_FD \
+ $(AUTOCONF_OUTPUT)
+
+# Create tap_autoconf.h or update it in case it differs from the new one.
+
+tap_autoconf.h: tap_autoconf.h.new
+ $Q [ -f '$@' ] && \
+ cmp '$<' '$@' $(AUTOCONF_OUTPUT) || \
+ mv '$<' '$@'
+
+$(SRCS-$(CONFIG_RTE_LIBRTE_PMD_TAP):.c=.o): tap_autoconf.h
+
+clean_tap: FORCE
+ $Q rm -f -- tap_autoconf.h tap_autoconf.h.new
+
+clean: clean_tap
diff --git a/src/spdk/dpdk/drivers/net/tap/rte_eth_tap.c b/src/spdk/dpdk/drivers/net/tap/rte_eth_tap.c
new file mode 100644
index 00000000..feb92b48
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/rte_eth_tap.c
@@ -0,0 +1,2140 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2016-2017 Intel Corporation
+ */
+
+#include <rte_atomic.h>
+#include <rte_branch_prediction.h>
+#include <rte_byteorder.h>
+#include <rte_common.h>
+#include <rte_mbuf.h>
+#include <rte_ethdev_driver.h>
+#include <rte_ethdev_vdev.h>
+#include <rte_malloc.h>
+#include <rte_bus_vdev.h>
+#include <rte_kvargs.h>
+#include <rte_net.h>
+#include <rte_debug.h>
+#include <rte_ip.h>
+#include <rte_string_fns.h>
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <linux/if_ether.h>
+#include <fcntl.h>
+
+#include <tap_rss.h>
+#include <rte_eth_tap.h>
+#include <tap_flow.h>
+#include <tap_netlink.h>
+#include <tap_tcmsgs.h>
+
+/* Linux based path to the TUN device */
+#define TUN_TAP_DEV_PATH "/dev/net/tun"
+#define DEFAULT_TAP_NAME "dtap"
+#define DEFAULT_TUN_NAME "dtun"
+
+#define ETH_TAP_IFACE_ARG "iface"
+#define ETH_TAP_REMOTE_ARG "remote"
+#define ETH_TAP_MAC_ARG "mac"
+#define ETH_TAP_MAC_FIXED "fixed"
+
+#define ETH_TAP_USR_MAC_FMT "xx:xx:xx:xx:xx:xx"
+#define ETH_TAP_CMP_MAC_FMT "0123456789ABCDEFabcdef"
+#define ETH_TAP_MAC_ARG_FMT ETH_TAP_MAC_FIXED "|" ETH_TAP_USR_MAC_FMT
+
+#define TAP_GSO_MBUFS_PER_CORE 128
+#define TAP_GSO_MBUF_SEG_SIZE 128
+#define TAP_GSO_MBUF_CACHE_SIZE 4
+#define TAP_GSO_MBUFS_NUM \
+ (TAP_GSO_MBUFS_PER_CORE * TAP_GSO_MBUF_CACHE_SIZE)
+
+static struct rte_vdev_driver pmd_tap_drv;
+static struct rte_vdev_driver pmd_tun_drv;
+
+static const char *valid_arguments[] = {
+ ETH_TAP_IFACE_ARG,
+ ETH_TAP_REMOTE_ARG,
+ ETH_TAP_MAC_ARG,
+ NULL
+};
+
+static unsigned int tap_unit;
+static unsigned int tun_unit;
+
+static char tuntap_name[8];
+
+static volatile uint32_t tap_trigger; /* Rx trigger */
+
+static struct rte_eth_link pmd_link = {
+ .link_speed = ETH_SPEED_NUM_10G,
+ .link_duplex = ETH_LINK_FULL_DUPLEX,
+ .link_status = ETH_LINK_DOWN,
+ .link_autoneg = ETH_LINK_FIXED,
+};
+
+static void
+tap_trigger_cb(int sig __rte_unused)
+{
+ /* Valid trigger values are nonzero */
+ tap_trigger = (tap_trigger + 1) | 0x80000000;
+}
+
+/* Specifies on what netdevices the ioctl should be applied */
+enum ioctl_mode {
+ LOCAL_AND_REMOTE,
+ LOCAL_ONLY,
+ REMOTE_ONLY,
+};
+
+static int tap_intr_handle_set(struct rte_eth_dev *dev, int set);
+
+/**
+ * Tun/Tap allocation routine
+ *
+ * @param[in] pmd
+ * Pointer to private structure.
+ *
+ * @param[in] is_keepalive
+ * Keepalive flag
+ *
+ * @return
+ * -1 on failure, fd on success
+ */
+static int
+tun_alloc(struct pmd_internals *pmd, int is_keepalive)
+{
+ struct ifreq ifr;
+#ifdef IFF_MULTI_QUEUE
+ unsigned int features;
+#endif
+ int fd;
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+
+ /*
+ * Do not set IFF_NO_PI as packet information header will be needed
+ * to check if a received packet has been truncated.
+ */
+ ifr.ifr_flags = (pmd->type == ETH_TUNTAP_TYPE_TAP) ?
+ IFF_TAP : IFF_TUN | IFF_POINTOPOINT;
+ snprintf(ifr.ifr_name, IFNAMSIZ, "%s", pmd->name);
+
+ TAP_LOG(DEBUG, "ifr_name '%s'", ifr.ifr_name);
+
+ fd = open(TUN_TAP_DEV_PATH, O_RDWR);
+ if (fd < 0) {
+ TAP_LOG(ERR, "Unable to create %s interface", tuntap_name);
+ goto error;
+ }
+
+#ifdef IFF_MULTI_QUEUE
+ /* Grab the TUN features to verify we can work multi-queue */
+ if (ioctl(fd, TUNGETFEATURES, &features) < 0) {
+ TAP_LOG(ERR, "%s unable to get TUN/TAP features",
+ tuntap_name);
+ goto error;
+ }
+ TAP_LOG(DEBUG, "%s Features %08x", tuntap_name, features);
+
+ if (features & IFF_MULTI_QUEUE) {
+ TAP_LOG(DEBUG, " Multi-queue support for %d queues",
+ RTE_PMD_TAP_MAX_QUEUES);
+ ifr.ifr_flags |= IFF_MULTI_QUEUE;
+ } else
+#endif
+ {
+ ifr.ifr_flags |= IFF_ONE_QUEUE;
+ TAP_LOG(DEBUG, " Single queue only support");
+ }
+
+ /* Set the TUN/TAP configuration and set the name if needed */
+ if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
+ TAP_LOG(WARNING, "Unable to set TUNSETIFF for %s: %s",
+ ifr.ifr_name, strerror(errno));
+ goto error;
+ }
+
+ if (is_keepalive) {
+ /*
+ * Detach the TUN/TAP keep-alive queue
+ * to avoid traffic through it
+ */
+ ifr.ifr_flags = IFF_DETACH_QUEUE;
+ if (ioctl(fd, TUNSETQUEUE, (void *)&ifr) < 0) {
+ TAP_LOG(WARNING,
+ "Unable to detach keep-alive queue for %s: %s",
+ ifr.ifr_name, strerror(errno));
+ goto error;
+ }
+ }
+
+ /* Always set the file descriptor to non-blocking */
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ TAP_LOG(WARNING,
+ "Unable to set %s to nonblocking: %s",
+ ifr.ifr_name, strerror(errno));
+ goto error;
+ }
+
+ /* Set up trigger to optimize empty Rx bursts */
+ errno = 0;
+ do {
+ struct sigaction sa;
+ int flags = fcntl(fd, F_GETFL);
+
+ if (flags == -1 || sigaction(SIGIO, NULL, &sa) == -1)
+ break;
+ if (sa.sa_handler != tap_trigger_cb) {
+ /*
+ * Make sure SIGIO is not already taken. This is done
+ * as late as possible to leave the application a
+ * chance to set up its own signal handler first.
+ */
+ if (sa.sa_handler != SIG_IGN &&
+ sa.sa_handler != SIG_DFL) {
+ errno = EBUSY;
+ break;
+ }
+ sa = (struct sigaction){
+ .sa_flags = SA_RESTART,
+ .sa_handler = tap_trigger_cb,
+ };
+ if (sigaction(SIGIO, &sa, NULL) == -1)
+ break;
+ }
+ /* Enable SIGIO on file descriptor */
+ fcntl(fd, F_SETFL, flags | O_ASYNC);
+ fcntl(fd, F_SETOWN, getpid());
+ } while (0);
+
+ if (errno) {
+ /* Disable trigger globally in case of error */
+ tap_trigger = 0;
+ TAP_LOG(WARNING, "Rx trigger disabled: %s",
+ strerror(errno));
+ }
+
+ return fd;
+
+error:
+ if (fd > 0)
+ close(fd);
+ return -1;
+}
+
+static void
+tap_verify_csum(struct rte_mbuf *mbuf)
+{
+ uint32_t l2 = mbuf->packet_type & RTE_PTYPE_L2_MASK;
+ uint32_t l3 = mbuf->packet_type & RTE_PTYPE_L3_MASK;
+ uint32_t l4 = mbuf->packet_type & RTE_PTYPE_L4_MASK;
+ unsigned int l2_len = sizeof(struct ether_hdr);
+ unsigned int l3_len;
+ uint16_t cksum = 0;
+ void *l3_hdr;
+ void *l4_hdr;
+
+ if (l2 == RTE_PTYPE_L2_ETHER_VLAN)
+ l2_len += 4;
+ else if (l2 == RTE_PTYPE_L2_ETHER_QINQ)
+ l2_len += 8;
+ /* Don't verify checksum for packets with discontinuous L2 header */
+ if (unlikely(l2_len + sizeof(struct ipv4_hdr) >
+ rte_pktmbuf_data_len(mbuf)))
+ return;
+ l3_hdr = rte_pktmbuf_mtod_offset(mbuf, void *, l2_len);
+ if (l3 == RTE_PTYPE_L3_IPV4 || l3 == RTE_PTYPE_L3_IPV4_EXT) {
+ struct ipv4_hdr *iph = l3_hdr;
+
+ /* ihl contains the number of 4-byte words in the header */
+ l3_len = 4 * (iph->version_ihl & 0xf);
+ if (unlikely(l2_len + l3_len > rte_pktmbuf_data_len(mbuf)))
+ return;
+
+ cksum = ~rte_raw_cksum(iph, l3_len);
+ mbuf->ol_flags |= cksum ?
+ PKT_RX_IP_CKSUM_BAD :
+ PKT_RX_IP_CKSUM_GOOD;
+ } else if (l3 == RTE_PTYPE_L3_IPV6) {
+ l3_len = sizeof(struct ipv6_hdr);
+ } else {
+ /* IPv6 extensions are not supported */
+ return;
+ }
+ if (l4 == RTE_PTYPE_L4_UDP || l4 == RTE_PTYPE_L4_TCP) {
+ l4_hdr = rte_pktmbuf_mtod_offset(mbuf, void *, l2_len + l3_len);
+ /* Don't verify checksum for multi-segment packets. */
+ if (mbuf->nb_segs > 1)
+ return;
+ if (l3 == RTE_PTYPE_L3_IPV4)
+ cksum = ~rte_ipv4_udptcp_cksum(l3_hdr, l4_hdr);
+ else if (l3 == RTE_PTYPE_L3_IPV6)
+ cksum = ~rte_ipv6_udptcp_cksum(l3_hdr, l4_hdr);
+ mbuf->ol_flags |= cksum ?
+ PKT_RX_L4_CKSUM_BAD :
+ PKT_RX_L4_CKSUM_GOOD;
+ }
+}
+
+static uint64_t
+tap_rx_offload_get_port_capa(void)
+{
+ /*
+ * No specific port Rx offload capabilities.
+ */
+ return 0;
+}
+
+static uint64_t
+tap_rx_offload_get_queue_capa(void)
+{
+ return DEV_RX_OFFLOAD_SCATTER |
+ DEV_RX_OFFLOAD_IPV4_CKSUM |
+ DEV_RX_OFFLOAD_UDP_CKSUM |
+ DEV_RX_OFFLOAD_TCP_CKSUM |
+ DEV_RX_OFFLOAD_CRC_STRIP;
+}
+
+/* Callback to handle the rx burst of packets to the correct interface and
+ * file descriptor(s) in a multi-queue setup.
+ */
+static uint16_t
+pmd_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
+{
+ struct rx_queue *rxq = queue;
+ uint16_t num_rx;
+ unsigned long num_rx_bytes = 0;
+ uint32_t trigger = tap_trigger;
+
+ if (trigger == rxq->trigger_seen)
+ return 0;
+ if (trigger)
+ rxq->trigger_seen = trigger;
+ rte_compiler_barrier();
+ for (num_rx = 0; num_rx < nb_pkts; ) {
+ struct rte_mbuf *mbuf = rxq->pool;
+ struct rte_mbuf *seg = NULL;
+ struct rte_mbuf *new_tail = NULL;
+ uint16_t data_off = rte_pktmbuf_headroom(mbuf);
+ int len;
+
+ len = readv(rxq->fd, *rxq->iovecs,
+ 1 +
+ (rxq->rxmode->offloads & DEV_RX_OFFLOAD_SCATTER ?
+ rxq->nb_rx_desc : 1));
+ if (len < (int)sizeof(struct tun_pi))
+ break;
+
+ /* Packet couldn't fit in the provided mbuf */
+ if (unlikely(rxq->pi.flags & TUN_PKT_STRIP)) {
+ rxq->stats.ierrors++;
+ continue;
+ }
+
+ len -= sizeof(struct tun_pi);
+
+ mbuf->pkt_len = len;
+ mbuf->port = rxq->in_port;
+ while (1) {
+ struct rte_mbuf *buf = rte_pktmbuf_alloc(rxq->mp);
+
+ if (unlikely(!buf)) {
+ rxq->stats.rx_nombuf++;
+ /* No new buf has been allocated: do nothing */
+ if (!new_tail || !seg)
+ goto end;
+
+ seg->next = NULL;
+ rte_pktmbuf_free(mbuf);
+
+ goto end;
+ }
+ seg = seg ? seg->next : mbuf;
+ if (rxq->pool == mbuf)
+ rxq->pool = buf;
+ if (new_tail)
+ new_tail->next = buf;
+ new_tail = buf;
+ new_tail->next = seg->next;
+
+ /* iovecs[0] is reserved for packet info (pi) */
+ (*rxq->iovecs)[mbuf->nb_segs].iov_len =
+ buf->buf_len - data_off;
+ (*rxq->iovecs)[mbuf->nb_segs].iov_base =
+ (char *)buf->buf_addr + data_off;
+
+ seg->data_len = RTE_MIN(seg->buf_len - data_off, len);
+ seg->data_off = data_off;
+
+ len -= seg->data_len;
+ if (len <= 0)
+ break;
+ mbuf->nb_segs++;
+ /* First segment has headroom, not the others */
+ data_off = 0;
+ }
+ seg->next = NULL;
+ mbuf->packet_type = rte_net_get_ptype(mbuf, NULL,
+ RTE_PTYPE_ALL_MASK);
+ if (rxq->rxmode->offloads & DEV_RX_OFFLOAD_CHECKSUM)
+ tap_verify_csum(mbuf);
+
+ /* account for the receive frame */
+ bufs[num_rx++] = mbuf;
+ num_rx_bytes += mbuf->pkt_len;
+ }
+end:
+ rxq->stats.ipackets += num_rx;
+ rxq->stats.ibytes += num_rx_bytes;
+
+ return num_rx;
+}
+
+static uint64_t
+tap_tx_offload_get_port_capa(void)
+{
+ /*
+ * No specific port Tx offload capabilities.
+ */
+ return 0;
+}
+
+static uint64_t
+tap_tx_offload_get_queue_capa(void)
+{
+ return DEV_TX_OFFLOAD_MULTI_SEGS |
+ DEV_TX_OFFLOAD_IPV4_CKSUM |
+ DEV_TX_OFFLOAD_UDP_CKSUM |
+ DEV_TX_OFFLOAD_TCP_CKSUM |
+ DEV_TX_OFFLOAD_TCP_TSO;
+}
+
+/* Finalize l4 checksum calculation */
+static void
+tap_tx_l4_cksum(uint16_t *l4_cksum, uint16_t l4_phdr_cksum,
+ uint32_t l4_raw_cksum)
+{
+ if (l4_cksum) {
+ uint32_t cksum;
+
+ cksum = __rte_raw_cksum_reduce(l4_raw_cksum);
+ cksum += l4_phdr_cksum;
+
+ cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
+ cksum = (~cksum) & 0xffff;
+ if (cksum == 0)
+ cksum = 0xffff;
+ *l4_cksum = cksum;
+ }
+}
+
+/* Accumaulate L4 raw checksums */
+static void
+tap_tx_l4_add_rcksum(char *l4_data, unsigned int l4_len, uint16_t *l4_cksum,
+ uint32_t *l4_raw_cksum)
+{
+ if (l4_cksum == NULL)
+ return;
+
+ *l4_raw_cksum = __rte_raw_cksum(l4_data, l4_len, *l4_raw_cksum);
+}
+
+/* L3 and L4 pseudo headers checksum offloads */
+static void
+tap_tx_l3_cksum(char *packet, uint64_t ol_flags, unsigned int l2_len,
+ unsigned int l3_len, unsigned int l4_len, uint16_t **l4_cksum,
+ uint16_t *l4_phdr_cksum, uint32_t *l4_raw_cksum)
+{
+ void *l3_hdr = packet + l2_len;
+
+ if (ol_flags & (PKT_TX_IP_CKSUM | PKT_TX_IPV4)) {
+ struct ipv4_hdr *iph = l3_hdr;
+ uint16_t cksum;
+
+ iph->hdr_checksum = 0;
+ cksum = rte_raw_cksum(iph, l3_len);
+ iph->hdr_checksum = (cksum == 0xffff) ? cksum : ~cksum;
+ }
+ if (ol_flags & PKT_TX_L4_MASK) {
+ void *l4_hdr;
+
+ l4_hdr = packet + l2_len + l3_len;
+ if ((ol_flags & PKT_TX_L4_MASK) == PKT_TX_UDP_CKSUM)
+ *l4_cksum = &((struct udp_hdr *)l4_hdr)->dgram_cksum;
+ else if ((ol_flags & PKT_TX_L4_MASK) == PKT_TX_TCP_CKSUM)
+ *l4_cksum = &((struct tcp_hdr *)l4_hdr)->cksum;
+ else
+ return;
+ **l4_cksum = 0;
+ if (ol_flags & PKT_TX_IPV4)
+ *l4_phdr_cksum = rte_ipv4_phdr_cksum(l3_hdr, 0);
+ else
+ *l4_phdr_cksum = rte_ipv6_phdr_cksum(l3_hdr, 0);
+ *l4_raw_cksum = __rte_raw_cksum(l4_hdr, l4_len, 0);
+ }
+}
+
+static inline void
+tap_write_mbufs(struct tx_queue *txq, uint16_t num_mbufs,
+ struct rte_mbuf **pmbufs,
+ uint16_t *num_packets, unsigned long *num_tx_bytes)
+{
+ int i;
+ uint16_t l234_hlen;
+
+ for (i = 0; i < num_mbufs; i++) {
+ struct rte_mbuf *mbuf = pmbufs[i];
+ struct iovec iovecs[mbuf->nb_segs + 2];
+ struct tun_pi pi = { .flags = 0, .proto = 0x00 };
+ struct rte_mbuf *seg = mbuf;
+ char m_copy[mbuf->data_len];
+ int proto;
+ int n;
+ int j;
+ int k; /* current index in iovecs for copying segments */
+ uint16_t seg_len; /* length of first segment */
+ uint16_t nb_segs;
+ uint16_t *l4_cksum; /* l4 checksum (pseudo header + payload) */
+ uint32_t l4_raw_cksum = 0; /* TCP/UDP payload raw checksum */
+ uint16_t l4_phdr_cksum = 0; /* TCP/UDP pseudo header checksum */
+ uint16_t is_cksum = 0; /* in case cksum should be offloaded */
+
+ l4_cksum = NULL;
+ if (txq->type == ETH_TUNTAP_TYPE_TUN) {
+ /*
+ * TUN and TAP are created with IFF_NO_PI disabled.
+ * For TUN PMD this mandatory as fields are used by
+ * Kernel tun.c to determine whether its IP or non IP
+ * packets.
+ *
+ * The logic fetches the first byte of data from mbuf
+ * then compares whether its v4 or v6. If first byte
+ * is 4 or 6, then protocol field is updated.
+ */
+ char *buff_data = rte_pktmbuf_mtod(seg, void *);
+ proto = (*buff_data & 0xf0);
+ pi.proto = (proto == 0x40) ?
+ rte_cpu_to_be_16(ETHER_TYPE_IPv4) :
+ ((proto == 0x60) ?
+ rte_cpu_to_be_16(ETHER_TYPE_IPv6) :
+ 0x00);
+ }
+
+ k = 0;
+ iovecs[k].iov_base = &pi;
+ iovecs[k].iov_len = sizeof(pi);
+ k++;
+
+ nb_segs = mbuf->nb_segs;
+ if (txq->csum &&
+ ((mbuf->ol_flags & (PKT_TX_IP_CKSUM | PKT_TX_IPV4) ||
+ (mbuf->ol_flags & PKT_TX_L4_MASK) == PKT_TX_UDP_CKSUM ||
+ (mbuf->ol_flags & PKT_TX_L4_MASK) == PKT_TX_TCP_CKSUM))) {
+ is_cksum = 1;
+
+ /* Support only packets with at least layer 4
+ * header included in the first segment
+ */
+ seg_len = rte_pktmbuf_data_len(mbuf);
+ l234_hlen = mbuf->l2_len + mbuf->l3_len + mbuf->l4_len;
+ if (seg_len < l234_hlen)
+ break;
+
+ /* To change checksums, work on a * copy of l2, l3
+ * headers + l4 pseudo header
+ */
+ rte_memcpy(m_copy, rte_pktmbuf_mtod(mbuf, void *),
+ l234_hlen);
+ tap_tx_l3_cksum(m_copy, mbuf->ol_flags,
+ mbuf->l2_len, mbuf->l3_len, mbuf->l4_len,
+ &l4_cksum, &l4_phdr_cksum,
+ &l4_raw_cksum);
+ iovecs[k].iov_base = m_copy;
+ iovecs[k].iov_len = l234_hlen;
+ k++;
+
+ /* Update next iovecs[] beyond l2, l3, l4 headers */
+ if (seg_len > l234_hlen) {
+ iovecs[k].iov_len = seg_len - l234_hlen;
+ iovecs[k].iov_base =
+ rte_pktmbuf_mtod(seg, char *) +
+ l234_hlen;
+ tap_tx_l4_add_rcksum(iovecs[k].iov_base,
+ iovecs[k].iov_len, l4_cksum,
+ &l4_raw_cksum);
+ k++;
+ nb_segs++;
+ }
+ seg = seg->next;
+ }
+
+ for (j = k; j <= nb_segs; j++) {
+ iovecs[j].iov_len = rte_pktmbuf_data_len(seg);
+ iovecs[j].iov_base = rte_pktmbuf_mtod(seg, void *);
+ if (is_cksum)
+ tap_tx_l4_add_rcksum(iovecs[j].iov_base,
+ iovecs[j].iov_len, l4_cksum,
+ &l4_raw_cksum);
+ seg = seg->next;
+ }
+
+ if (is_cksum)
+ tap_tx_l4_cksum(l4_cksum, l4_phdr_cksum, l4_raw_cksum);
+
+ /* copy the tx frame data */
+ n = writev(txq->fd, iovecs, j);
+ if (n <= 0)
+ break;
+ (*num_packets)++;
+ (*num_tx_bytes) += rte_pktmbuf_pkt_len(mbuf);
+ }
+}
+
+/* Callback to handle sending packets from the tap interface
+ */
+static uint16_t
+pmd_tx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
+{
+ struct tx_queue *txq = queue;
+ uint16_t num_tx = 0;
+ uint16_t num_packets = 0;
+ unsigned long num_tx_bytes = 0;
+ uint32_t max_size;
+ int i;
+
+ if (unlikely(nb_pkts == 0))
+ return 0;
+
+ struct rte_mbuf *gso_mbufs[MAX_GSO_MBUFS];
+ max_size = *txq->mtu + (ETHER_HDR_LEN + ETHER_CRC_LEN + 4);
+ for (i = 0; i < nb_pkts; i++) {
+ struct rte_mbuf *mbuf_in = bufs[num_tx];
+ struct rte_mbuf **mbuf;
+ uint16_t num_mbufs = 0;
+ uint16_t tso_segsz = 0;
+ int ret;
+ uint16_t hdrs_len;
+ int j;
+ uint64_t tso;
+
+ tso = mbuf_in->ol_flags & PKT_TX_TCP_SEG;
+ if (tso) {
+ struct rte_gso_ctx *gso_ctx = &txq->gso_ctx;
+
+ assert(gso_ctx != NULL);
+
+ /* TCP segmentation implies TCP checksum offload */
+ mbuf_in->ol_flags |= PKT_TX_TCP_CKSUM;
+
+ /* gso size is calculated without ETHER_CRC_LEN */
+ hdrs_len = mbuf_in->l2_len + mbuf_in->l3_len +
+ mbuf_in->l4_len;
+ tso_segsz = mbuf_in->tso_segsz + hdrs_len;
+ if (unlikely(tso_segsz == hdrs_len) ||
+ tso_segsz > *txq->mtu) {
+ txq->stats.errs++;
+ break;
+ }
+ gso_ctx->gso_size = tso_segsz;
+ ret = rte_gso_segment(mbuf_in, /* packet to segment */
+ gso_ctx, /* gso control block */
+ (struct rte_mbuf **)&gso_mbufs, /* out mbufs */
+ RTE_DIM(gso_mbufs)); /* max tso mbufs */
+
+ /* ret contains the number of new created mbufs */
+ if (ret < 0)
+ break;
+
+ mbuf = gso_mbufs;
+ num_mbufs = ret;
+ } else {
+ /* stats.errs will be incremented */
+ if (rte_pktmbuf_pkt_len(mbuf_in) > max_size)
+ break;
+
+ /* ret 0 indicates no new mbufs were created */
+ ret = 0;
+ mbuf = &mbuf_in;
+ num_mbufs = 1;
+ }
+
+ tap_write_mbufs(txq, num_mbufs, mbuf,
+ &num_packets, &num_tx_bytes);
+ num_tx++;
+ /* free original mbuf */
+ rte_pktmbuf_free(mbuf_in);
+ /* free tso mbufs */
+ for (j = 0; j < ret; j++)
+ rte_pktmbuf_free(mbuf[j]);
+ }
+
+ txq->stats.opackets += num_packets;
+ txq->stats.errs += nb_pkts - num_tx;
+ txq->stats.obytes += num_tx_bytes;
+
+ return num_tx;
+}
+
+static const char *
+tap_ioctl_req2str(unsigned long request)
+{
+ switch (request) {
+ case SIOCSIFFLAGS:
+ return "SIOCSIFFLAGS";
+ case SIOCGIFFLAGS:
+ return "SIOCGIFFLAGS";
+ case SIOCGIFHWADDR:
+ return "SIOCGIFHWADDR";
+ case SIOCSIFHWADDR:
+ return "SIOCSIFHWADDR";
+ case SIOCSIFMTU:
+ return "SIOCSIFMTU";
+ }
+ return "UNKNOWN";
+}
+
+static int
+tap_ioctl(struct pmd_internals *pmd, unsigned long request,
+ struct ifreq *ifr, int set, enum ioctl_mode mode)
+{
+ short req_flags = ifr->ifr_flags;
+ int remote = pmd->remote_if_index &&
+ (mode == REMOTE_ONLY || mode == LOCAL_AND_REMOTE);
+
+ if (!pmd->remote_if_index && mode == REMOTE_ONLY)
+ return 0;
+ /*
+ * If there is a remote netdevice, apply ioctl on it, then apply it on
+ * the tap netdevice.
+ */
+apply:
+ if (remote)
+ snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->remote_iface);
+ else if (mode == LOCAL_ONLY || mode == LOCAL_AND_REMOTE)
+ snprintf(ifr->ifr_name, IFNAMSIZ, "%s", pmd->name);
+ switch (request) {
+ case SIOCSIFFLAGS:
+ /* fetch current flags to leave other flags untouched */
+ if (ioctl(pmd->ioctl_sock, SIOCGIFFLAGS, ifr) < 0)
+ goto error;
+ if (set)
+ ifr->ifr_flags |= req_flags;
+ else
+ ifr->ifr_flags &= ~req_flags;
+ break;
+ case SIOCGIFFLAGS:
+ case SIOCGIFHWADDR:
+ case SIOCSIFHWADDR:
+ case SIOCSIFMTU:
+ break;
+ default:
+ RTE_LOG(WARNING, PMD, "%s: ioctl() called with wrong arg\n",
+ pmd->name);
+ return -EINVAL;
+ }
+ if (ioctl(pmd->ioctl_sock, request, ifr) < 0)
+ goto error;
+ if (remote-- && mode == LOCAL_AND_REMOTE)
+ goto apply;
+ return 0;
+
+error:
+ TAP_LOG(DEBUG, "%s(%s) failed: %s(%d)", ifr->ifr_name,
+ tap_ioctl_req2str(request), strerror(errno), errno);
+ return -errno;
+}
+
+static int
+tap_link_set_down(struct rte_eth_dev *dev)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct ifreq ifr = { .ifr_flags = IFF_UP };
+
+ dev->data->dev_link.link_status = ETH_LINK_DOWN;
+ return tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 0, LOCAL_ONLY);
+}
+
+static int
+tap_link_set_up(struct rte_eth_dev *dev)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct ifreq ifr = { .ifr_flags = IFF_UP };
+
+ dev->data->dev_link.link_status = ETH_LINK_UP;
+ return tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 1, LOCAL_AND_REMOTE);
+}
+
+static int
+tap_dev_start(struct rte_eth_dev *dev)
+{
+ int err, i;
+
+ err = tap_intr_handle_set(dev, 1);
+ if (err)
+ return err;
+
+ err = tap_link_set_up(dev);
+ if (err)
+ return err;
+
+ for (i = 0; i < dev->data->nb_tx_queues; i++)
+ dev->data->tx_queue_state[i] = RTE_ETH_QUEUE_STATE_STARTED;
+ for (i = 0; i < dev->data->nb_rx_queues; i++)
+ dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STARTED;
+
+ return err;
+}
+
+/* This function gets called when the current port gets stopped.
+ */
+static void
+tap_dev_stop(struct rte_eth_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < dev->data->nb_tx_queues; i++)
+ dev->data->tx_queue_state[i] = RTE_ETH_QUEUE_STATE_STOPPED;
+ for (i = 0; i < dev->data->nb_rx_queues; i++)
+ dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STOPPED;
+
+ tap_intr_handle_set(dev, 0);
+ tap_link_set_down(dev);
+}
+
+static int
+tap_dev_configure(struct rte_eth_dev *dev)
+{
+ if (dev->data->nb_rx_queues > RTE_PMD_TAP_MAX_QUEUES) {
+ TAP_LOG(ERR,
+ "%s: number of rx queues %d exceeds max num of queues %d",
+ dev->device->name,
+ dev->data->nb_rx_queues,
+ RTE_PMD_TAP_MAX_QUEUES);
+ return -1;
+ }
+ if (dev->data->nb_tx_queues > RTE_PMD_TAP_MAX_QUEUES) {
+ TAP_LOG(ERR,
+ "%s: number of tx queues %d exceeds max num of queues %d",
+ dev->device->name,
+ dev->data->nb_tx_queues,
+ RTE_PMD_TAP_MAX_QUEUES);
+ return -1;
+ }
+
+ TAP_LOG(INFO, "%s: %p: TX configured queues number: %u",
+ dev->device->name, (void *)dev, dev->data->nb_tx_queues);
+
+ TAP_LOG(INFO, "%s: %p: RX configured queues number: %u",
+ dev->device->name, (void *)dev, dev->data->nb_rx_queues);
+
+ return 0;
+}
+
+static uint32_t
+tap_dev_speed_capa(void)
+{
+ uint32_t speed = pmd_link.link_speed;
+ uint32_t capa = 0;
+
+ if (speed >= ETH_SPEED_NUM_10M)
+ capa |= ETH_LINK_SPEED_10M;
+ if (speed >= ETH_SPEED_NUM_100M)
+ capa |= ETH_LINK_SPEED_100M;
+ if (speed >= ETH_SPEED_NUM_1G)
+ capa |= ETH_LINK_SPEED_1G;
+ if (speed >= ETH_SPEED_NUM_5G)
+ capa |= ETH_LINK_SPEED_2_5G;
+ if (speed >= ETH_SPEED_NUM_5G)
+ capa |= ETH_LINK_SPEED_5G;
+ if (speed >= ETH_SPEED_NUM_10G)
+ capa |= ETH_LINK_SPEED_10G;
+ if (speed >= ETH_SPEED_NUM_20G)
+ capa |= ETH_LINK_SPEED_20G;
+ if (speed >= ETH_SPEED_NUM_25G)
+ capa |= ETH_LINK_SPEED_25G;
+ if (speed >= ETH_SPEED_NUM_40G)
+ capa |= ETH_LINK_SPEED_40G;
+ if (speed >= ETH_SPEED_NUM_50G)
+ capa |= ETH_LINK_SPEED_50G;
+ if (speed >= ETH_SPEED_NUM_56G)
+ capa |= ETH_LINK_SPEED_56G;
+ if (speed >= ETH_SPEED_NUM_100G)
+ capa |= ETH_LINK_SPEED_100G;
+
+ return capa;
+}
+
+static void
+tap_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
+{
+ struct pmd_internals *internals = dev->data->dev_private;
+
+ dev_info->if_index = internals->if_index;
+ dev_info->max_mac_addrs = 1;
+ dev_info->max_rx_pktlen = (uint32_t)ETHER_MAX_VLAN_FRAME_LEN;
+ dev_info->max_rx_queues = RTE_PMD_TAP_MAX_QUEUES;
+ dev_info->max_tx_queues = RTE_PMD_TAP_MAX_QUEUES;
+ dev_info->min_rx_bufsize = 0;
+ dev_info->speed_capa = tap_dev_speed_capa();
+ dev_info->rx_queue_offload_capa = tap_rx_offload_get_queue_capa();
+ dev_info->rx_offload_capa = tap_rx_offload_get_port_capa() |
+ dev_info->rx_queue_offload_capa;
+ dev_info->tx_queue_offload_capa = tap_tx_offload_get_queue_capa();
+ dev_info->tx_offload_capa = tap_tx_offload_get_port_capa() |
+ dev_info->tx_queue_offload_capa;
+ dev_info->hash_key_size = TAP_RSS_HASH_KEY_SIZE;
+ /*
+ * limitation: TAP supports all of IP, UDP and TCP hash
+ * functions together and not in partial combinations
+ */
+ dev_info->flow_type_rss_offloads = ~TAP_RSS_HF_MASK;
+}
+
+static int
+tap_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *tap_stats)
+{
+ unsigned int i, imax;
+ unsigned long rx_total = 0, tx_total = 0, tx_err_total = 0;
+ unsigned long rx_bytes_total = 0, tx_bytes_total = 0;
+ unsigned long rx_nombuf = 0, ierrors = 0;
+ const struct pmd_internals *pmd = dev->data->dev_private;
+
+ /* rx queue statistics */
+ imax = (dev->data->nb_rx_queues < RTE_ETHDEV_QUEUE_STAT_CNTRS) ?
+ dev->data->nb_rx_queues : RTE_ETHDEV_QUEUE_STAT_CNTRS;
+ for (i = 0; i < imax; i++) {
+ tap_stats->q_ipackets[i] = pmd->rxq[i].stats.ipackets;
+ tap_stats->q_ibytes[i] = pmd->rxq[i].stats.ibytes;
+ rx_total += tap_stats->q_ipackets[i];
+ rx_bytes_total += tap_stats->q_ibytes[i];
+ rx_nombuf += pmd->rxq[i].stats.rx_nombuf;
+ ierrors += pmd->rxq[i].stats.ierrors;
+ }
+
+ /* tx queue statistics */
+ imax = (dev->data->nb_tx_queues < RTE_ETHDEV_QUEUE_STAT_CNTRS) ?
+ dev->data->nb_tx_queues : RTE_ETHDEV_QUEUE_STAT_CNTRS;
+
+ for (i = 0; i < imax; i++) {
+ tap_stats->q_opackets[i] = pmd->txq[i].stats.opackets;
+ tap_stats->q_errors[i] = pmd->txq[i].stats.errs;
+ tap_stats->q_obytes[i] = pmd->txq[i].stats.obytes;
+ tx_total += tap_stats->q_opackets[i];
+ tx_err_total += tap_stats->q_errors[i];
+ tx_bytes_total += tap_stats->q_obytes[i];
+ }
+
+ tap_stats->ipackets = rx_total;
+ tap_stats->ibytes = rx_bytes_total;
+ tap_stats->ierrors = ierrors;
+ tap_stats->rx_nombuf = rx_nombuf;
+ tap_stats->opackets = tx_total;
+ tap_stats->oerrors = tx_err_total;
+ tap_stats->obytes = tx_bytes_total;
+ return 0;
+}
+
+static void
+tap_stats_reset(struct rte_eth_dev *dev)
+{
+ int i;
+ struct pmd_internals *pmd = dev->data->dev_private;
+
+ for (i = 0; i < RTE_PMD_TAP_MAX_QUEUES; i++) {
+ pmd->rxq[i].stats.ipackets = 0;
+ pmd->rxq[i].stats.ibytes = 0;
+ pmd->rxq[i].stats.ierrors = 0;
+ pmd->rxq[i].stats.rx_nombuf = 0;
+
+ pmd->txq[i].stats.opackets = 0;
+ pmd->txq[i].stats.errs = 0;
+ pmd->txq[i].stats.obytes = 0;
+ }
+}
+
+static void
+tap_dev_close(struct rte_eth_dev *dev)
+{
+ int i;
+ struct pmd_internals *internals = dev->data->dev_private;
+
+ tap_link_set_down(dev);
+ tap_flow_flush(dev, NULL);
+ tap_flow_implicit_flush(internals, NULL);
+
+ for (i = 0; i < RTE_PMD_TAP_MAX_QUEUES; i++) {
+ if (internals->rxq[i].fd != -1) {
+ close(internals->rxq[i].fd);
+ internals->rxq[i].fd = -1;
+ }
+ if (internals->txq[i].fd != -1) {
+ close(internals->txq[i].fd);
+ internals->txq[i].fd = -1;
+ }
+ }
+
+ if (internals->remote_if_index) {
+ /* Restore initial remote state */
+ ioctl(internals->ioctl_sock, SIOCSIFFLAGS,
+ &internals->remote_initial_flags);
+ }
+
+ if (internals->ka_fd != -1) {
+ close(internals->ka_fd);
+ internals->ka_fd = -1;
+ }
+ /*
+ * Since TUN device has no more opened file descriptors
+ * it will be removed from kernel
+ */
+}
+
+static void
+tap_rx_queue_release(void *queue)
+{
+ struct rx_queue *rxq = queue;
+
+ if (rxq && (rxq->fd > 0)) {
+ close(rxq->fd);
+ rxq->fd = -1;
+ rte_pktmbuf_free(rxq->pool);
+ rte_free(rxq->iovecs);
+ rxq->pool = NULL;
+ rxq->iovecs = NULL;
+ }
+}
+
+static void
+tap_tx_queue_release(void *queue)
+{
+ struct tx_queue *txq = queue;
+
+ if (txq && (txq->fd > 0)) {
+ close(txq->fd);
+ txq->fd = -1;
+ }
+}
+
+static int
+tap_link_update(struct rte_eth_dev *dev, int wait_to_complete __rte_unused)
+{
+ struct rte_eth_link *dev_link = &dev->data->dev_link;
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct ifreq ifr = { .ifr_flags = 0 };
+
+ if (pmd->remote_if_index) {
+ tap_ioctl(pmd, SIOCGIFFLAGS, &ifr, 0, REMOTE_ONLY);
+ if (!(ifr.ifr_flags & IFF_UP) ||
+ !(ifr.ifr_flags & IFF_RUNNING)) {
+ dev_link->link_status = ETH_LINK_DOWN;
+ return 0;
+ }
+ }
+ tap_ioctl(pmd, SIOCGIFFLAGS, &ifr, 0, LOCAL_ONLY);
+ dev_link->link_status =
+ ((ifr.ifr_flags & IFF_UP) && (ifr.ifr_flags & IFF_RUNNING) ?
+ ETH_LINK_UP :
+ ETH_LINK_DOWN);
+ return 0;
+}
+
+static void
+tap_promisc_enable(struct rte_eth_dev *dev)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct ifreq ifr = { .ifr_flags = IFF_PROMISC };
+
+ dev->data->promiscuous = 1;
+ tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 1, LOCAL_AND_REMOTE);
+ if (pmd->remote_if_index && !pmd->flow_isolate)
+ tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC);
+}
+
+static void
+tap_promisc_disable(struct rte_eth_dev *dev)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct ifreq ifr = { .ifr_flags = IFF_PROMISC };
+
+ dev->data->promiscuous = 0;
+ tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 0, LOCAL_AND_REMOTE);
+ if (pmd->remote_if_index && !pmd->flow_isolate)
+ tap_flow_implicit_destroy(pmd, TAP_REMOTE_PROMISC);
+}
+
+static void
+tap_allmulti_enable(struct rte_eth_dev *dev)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct ifreq ifr = { .ifr_flags = IFF_ALLMULTI };
+
+ dev->data->all_multicast = 1;
+ tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 1, LOCAL_AND_REMOTE);
+ if (pmd->remote_if_index && !pmd->flow_isolate)
+ tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI);
+}
+
+static void
+tap_allmulti_disable(struct rte_eth_dev *dev)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct ifreq ifr = { .ifr_flags = IFF_ALLMULTI };
+
+ dev->data->all_multicast = 0;
+ tap_ioctl(pmd, SIOCSIFFLAGS, &ifr, 0, LOCAL_AND_REMOTE);
+ if (pmd->remote_if_index && !pmd->flow_isolate)
+ tap_flow_implicit_destroy(pmd, TAP_REMOTE_ALLMULTI);
+}
+
+static int
+tap_mac_set(struct rte_eth_dev *dev, struct ether_addr *mac_addr)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ enum ioctl_mode mode = LOCAL_ONLY;
+ struct ifreq ifr;
+ int ret;
+
+ if (pmd->type == ETH_TUNTAP_TYPE_TUN) {
+ TAP_LOG(ERR, "%s: can't MAC address for TUN",
+ dev->device->name);
+ return -ENOTSUP;
+ }
+
+ if (is_zero_ether_addr(mac_addr)) {
+ TAP_LOG(ERR, "%s: can't set an empty MAC address",
+ dev->device->name);
+ return -EINVAL;
+ }
+ /* Check the actual current MAC address on the tap netdevice */
+ ret = tap_ioctl(pmd, SIOCGIFHWADDR, &ifr, 0, LOCAL_ONLY);
+ if (ret < 0)
+ return ret;
+ if (is_same_ether_addr((struct ether_addr *)&ifr.ifr_hwaddr.sa_data,
+ mac_addr))
+ return 0;
+ /* Check the current MAC address on the remote */
+ ret = tap_ioctl(pmd, SIOCGIFHWADDR, &ifr, 0, REMOTE_ONLY);
+ if (ret < 0)
+ return ret;
+ if (!is_same_ether_addr((struct ether_addr *)&ifr.ifr_hwaddr.sa_data,
+ mac_addr))
+ mode = LOCAL_AND_REMOTE;
+ ifr.ifr_hwaddr.sa_family = AF_LOCAL;
+ rte_memcpy(ifr.ifr_hwaddr.sa_data, mac_addr, ETHER_ADDR_LEN);
+ ret = tap_ioctl(pmd, SIOCSIFHWADDR, &ifr, 1, mode);
+ if (ret < 0)
+ return ret;
+ rte_memcpy(&pmd->eth_addr, mac_addr, ETHER_ADDR_LEN);
+ if (pmd->remote_if_index && !pmd->flow_isolate) {
+ /* Replace MAC redirection rule after a MAC change */
+ ret = tap_flow_implicit_destroy(pmd, TAP_REMOTE_LOCAL_MAC);
+ if (ret < 0) {
+ TAP_LOG(ERR,
+ "%s: Couldn't delete MAC redirection rule",
+ dev->device->name);
+ return ret;
+ }
+ ret = tap_flow_implicit_create(pmd, TAP_REMOTE_LOCAL_MAC);
+ if (ret < 0) {
+ TAP_LOG(ERR,
+ "%s: Couldn't add MAC redirection rule",
+ dev->device->name);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int
+tap_gso_ctx_setup(struct rte_gso_ctx *gso_ctx, struct rte_eth_dev *dev)
+{
+ uint32_t gso_types;
+ char pool_name[64];
+
+ /*
+ * Create private mbuf pool with TAP_GSO_MBUF_SEG_SIZE bytes
+ * size per mbuf use this pool for both direct and indirect mbufs
+ */
+
+ struct rte_mempool *mp; /* Mempool for GSO packets */
+
+ /* initialize GSO context */
+ gso_types = DEV_TX_OFFLOAD_TCP_TSO;
+ snprintf(pool_name, sizeof(pool_name), "mp_%s", dev->device->name);
+ mp = rte_mempool_lookup((const char *)pool_name);
+ if (!mp) {
+ mp = rte_pktmbuf_pool_create(pool_name, TAP_GSO_MBUFS_NUM,
+ TAP_GSO_MBUF_CACHE_SIZE, 0,
+ RTE_PKTMBUF_HEADROOM + TAP_GSO_MBUF_SEG_SIZE,
+ SOCKET_ID_ANY);
+ if (!mp) {
+ struct pmd_internals *pmd = dev->data->dev_private;
+ RTE_LOG(DEBUG, PMD, "%s: failed to create mbuf pool for device %s\n",
+ pmd->name, dev->device->name);
+ return -1;
+ }
+ }
+
+ gso_ctx->direct_pool = mp;
+ gso_ctx->indirect_pool = mp;
+ gso_ctx->gso_types = gso_types;
+ gso_ctx->gso_size = 0; /* gso_size is set in tx_burst() per packet */
+ gso_ctx->flag = 0;
+
+ return 0;
+}
+
+static int
+tap_setup_queue(struct rte_eth_dev *dev,
+ struct pmd_internals *internals,
+ uint16_t qid,
+ int is_rx)
+{
+ int ret;
+ int *fd;
+ int *other_fd;
+ const char *dir;
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct rx_queue *rx = &internals->rxq[qid];
+ struct tx_queue *tx = &internals->txq[qid];
+ struct rte_gso_ctx *gso_ctx;
+
+ if (is_rx) {
+ fd = &rx->fd;
+ other_fd = &tx->fd;
+ dir = "rx";
+ gso_ctx = NULL;
+ } else {
+ fd = &tx->fd;
+ other_fd = &rx->fd;
+ dir = "tx";
+ gso_ctx = &tx->gso_ctx;
+ }
+ if (*fd != -1) {
+ /* fd for this queue already exists */
+ TAP_LOG(DEBUG, "%s: fd %d for %s queue qid %d exists",
+ pmd->name, *fd, dir, qid);
+ gso_ctx = NULL;
+ } else if (*other_fd != -1) {
+ /* Only other_fd exists. dup it */
+ *fd = dup(*other_fd);
+ if (*fd < 0) {
+ *fd = -1;
+ TAP_LOG(ERR, "%s: dup() failed.", pmd->name);
+ return -1;
+ }
+ TAP_LOG(DEBUG, "%s: dup fd %d for %s queue qid %d (%d)",
+ pmd->name, *other_fd, dir, qid, *fd);
+ } else {
+ /* Both RX and TX fds do not exist (equal -1). Create fd */
+ *fd = tun_alloc(pmd, 0);
+ if (*fd < 0) {
+ *fd = -1; /* restore original value */
+ TAP_LOG(ERR, "%s: tun_alloc() failed.", pmd->name);
+ return -1;
+ }
+ TAP_LOG(DEBUG, "%s: add %s queue for qid %d fd %d",
+ pmd->name, dir, qid, *fd);
+ }
+
+ tx->mtu = &dev->data->mtu;
+ rx->rxmode = &dev->data->dev_conf.rxmode;
+ if (gso_ctx) {
+ ret = tap_gso_ctx_setup(gso_ctx, dev);
+ if (ret)
+ return -1;
+ }
+
+ tx->type = pmd->type;
+
+ return *fd;
+}
+
+static int
+tap_rx_queue_setup(struct rte_eth_dev *dev,
+ uint16_t rx_queue_id,
+ uint16_t nb_rx_desc,
+ unsigned int socket_id,
+ const struct rte_eth_rxconf *rx_conf __rte_unused,
+ struct rte_mempool *mp)
+{
+ struct pmd_internals *internals = dev->data->dev_private;
+ struct rx_queue *rxq = &internals->rxq[rx_queue_id];
+ struct rte_mbuf **tmp = &rxq->pool;
+ long iov_max = sysconf(_SC_IOV_MAX);
+ uint16_t nb_desc = RTE_MIN(nb_rx_desc, iov_max - 1);
+ struct iovec (*iovecs)[nb_desc + 1];
+ int data_off = RTE_PKTMBUF_HEADROOM;
+ int ret = 0;
+ int fd;
+ int i;
+
+ if (rx_queue_id >= dev->data->nb_rx_queues || !mp) {
+ TAP_LOG(WARNING,
+ "nb_rx_queues %d too small or mempool NULL",
+ dev->data->nb_rx_queues);
+ return -1;
+ }
+
+ rxq->mp = mp;
+ rxq->trigger_seen = 1; /* force initial burst */
+ rxq->in_port = dev->data->port_id;
+ rxq->nb_rx_desc = nb_desc;
+ iovecs = rte_zmalloc_socket(dev->device->name, sizeof(*iovecs), 0,
+ socket_id);
+ if (!iovecs) {
+ TAP_LOG(WARNING,
+ "%s: Couldn't allocate %d RX descriptors",
+ dev->device->name, nb_desc);
+ return -ENOMEM;
+ }
+ rxq->iovecs = iovecs;
+
+ dev->data->rx_queues[rx_queue_id] = rxq;
+ fd = tap_setup_queue(dev, internals, rx_queue_id, 1);
+ if (fd == -1) {
+ ret = fd;
+ goto error;
+ }
+
+ (*rxq->iovecs)[0].iov_len = sizeof(struct tun_pi);
+ (*rxq->iovecs)[0].iov_base = &rxq->pi;
+
+ for (i = 1; i <= nb_desc; i++) {
+ *tmp = rte_pktmbuf_alloc(rxq->mp);
+ if (!*tmp) {
+ TAP_LOG(WARNING,
+ "%s: couldn't allocate memory for queue %d",
+ dev->device->name, rx_queue_id);
+ ret = -ENOMEM;
+ goto error;
+ }
+ (*rxq->iovecs)[i].iov_len = (*tmp)->buf_len - data_off;
+ (*rxq->iovecs)[i].iov_base =
+ (char *)(*tmp)->buf_addr + data_off;
+ data_off = 0;
+ tmp = &(*tmp)->next;
+ }
+
+ TAP_LOG(DEBUG, " RX TUNTAP device name %s, qid %d on fd %d",
+ internals->name, rx_queue_id, internals->rxq[rx_queue_id].fd);
+
+ return 0;
+
+error:
+ rte_pktmbuf_free(rxq->pool);
+ rxq->pool = NULL;
+ rte_free(rxq->iovecs);
+ rxq->iovecs = NULL;
+ return ret;
+}
+
+static int
+tap_tx_queue_setup(struct rte_eth_dev *dev,
+ uint16_t tx_queue_id,
+ uint16_t nb_tx_desc __rte_unused,
+ unsigned int socket_id __rte_unused,
+ const struct rte_eth_txconf *tx_conf)
+{
+ struct pmd_internals *internals = dev->data->dev_private;
+ struct tx_queue *txq;
+ int ret;
+ uint64_t offloads;
+
+ if (tx_queue_id >= dev->data->nb_tx_queues)
+ return -1;
+ dev->data->tx_queues[tx_queue_id] = &internals->txq[tx_queue_id];
+ txq = dev->data->tx_queues[tx_queue_id];
+
+ offloads = tx_conf->offloads | dev->data->dev_conf.txmode.offloads;
+ txq->csum = !!(offloads &
+ (DEV_TX_OFFLOAD_IPV4_CKSUM |
+ DEV_TX_OFFLOAD_UDP_CKSUM |
+ DEV_TX_OFFLOAD_TCP_CKSUM));
+
+ ret = tap_setup_queue(dev, internals, tx_queue_id, 0);
+ if (ret == -1)
+ return -1;
+ TAP_LOG(DEBUG,
+ " TX TUNTAP device name %s, qid %d on fd %d csum %s",
+ internals->name, tx_queue_id, internals->txq[tx_queue_id].fd,
+ txq->csum ? "on" : "off");
+
+ return 0;
+}
+
+static int
+tap_mtu_set(struct rte_eth_dev *dev, uint16_t mtu)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct ifreq ifr = { .ifr_mtu = mtu };
+ int err = 0;
+
+ err = tap_ioctl(pmd, SIOCSIFMTU, &ifr, 1, LOCAL_AND_REMOTE);
+ if (!err)
+ dev->data->mtu = mtu;
+
+ return err;
+}
+
+static int
+tap_set_mc_addr_list(struct rte_eth_dev *dev __rte_unused,
+ struct ether_addr *mc_addr_set __rte_unused,
+ uint32_t nb_mc_addr __rte_unused)
+{
+ /*
+ * Nothing to do actually: the tap has no filtering whatsoever, every
+ * packet is received.
+ */
+ return 0;
+}
+
+static int
+tap_nl_msg_handler(struct nlmsghdr *nh, void *arg)
+{
+ struct rte_eth_dev *dev = arg;
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct ifinfomsg *info = NLMSG_DATA(nh);
+
+ if (nh->nlmsg_type != RTM_NEWLINK ||
+ (info->ifi_index != pmd->if_index &&
+ info->ifi_index != pmd->remote_if_index))
+ return 0;
+ return tap_link_update(dev, 0);
+}
+
+static void
+tap_dev_intr_handler(void *cb_arg)
+{
+ struct rte_eth_dev *dev = cb_arg;
+ struct pmd_internals *pmd = dev->data->dev_private;
+
+ tap_nl_recv(pmd->intr_handle.fd, tap_nl_msg_handler, dev);
+}
+
+static int
+tap_lsc_intr_handle_set(struct rte_eth_dev *dev, int set)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+
+ /* In any case, disable interrupt if the conf is no longer there. */
+ if (!dev->data->dev_conf.intr_conf.lsc) {
+ if (pmd->intr_handle.fd != -1) {
+ tap_nl_final(pmd->intr_handle.fd);
+ rte_intr_callback_unregister(&pmd->intr_handle,
+ tap_dev_intr_handler, dev);
+ }
+ return 0;
+ }
+ if (set) {
+ pmd->intr_handle.fd = tap_nl_init(RTMGRP_LINK);
+ if (unlikely(pmd->intr_handle.fd == -1))
+ return -EBADF;
+ return rte_intr_callback_register(
+ &pmd->intr_handle, tap_dev_intr_handler, dev);
+ }
+ tap_nl_final(pmd->intr_handle.fd);
+ return rte_intr_callback_unregister(&pmd->intr_handle,
+ tap_dev_intr_handler, dev);
+}
+
+static int
+tap_intr_handle_set(struct rte_eth_dev *dev, int set)
+{
+ int err;
+
+ err = tap_lsc_intr_handle_set(dev, set);
+ if (err)
+ return err;
+ err = tap_rx_intr_vec_set(dev, set);
+ if (err && set)
+ tap_lsc_intr_handle_set(dev, 0);
+ return err;
+}
+
+static const uint32_t*
+tap_dev_supported_ptypes_get(struct rte_eth_dev *dev __rte_unused)
+{
+ static const uint32_t ptypes[] = {
+ RTE_PTYPE_INNER_L2_ETHER,
+ RTE_PTYPE_INNER_L2_ETHER_VLAN,
+ RTE_PTYPE_INNER_L2_ETHER_QINQ,
+ RTE_PTYPE_INNER_L3_IPV4,
+ RTE_PTYPE_INNER_L3_IPV4_EXT,
+ RTE_PTYPE_INNER_L3_IPV6,
+ RTE_PTYPE_INNER_L3_IPV6_EXT,
+ RTE_PTYPE_INNER_L4_FRAG,
+ RTE_PTYPE_INNER_L4_UDP,
+ RTE_PTYPE_INNER_L4_TCP,
+ RTE_PTYPE_INNER_L4_SCTP,
+ RTE_PTYPE_L2_ETHER,
+ RTE_PTYPE_L2_ETHER_VLAN,
+ RTE_PTYPE_L2_ETHER_QINQ,
+ RTE_PTYPE_L3_IPV4,
+ RTE_PTYPE_L3_IPV4_EXT,
+ RTE_PTYPE_L3_IPV6_EXT,
+ RTE_PTYPE_L3_IPV6,
+ RTE_PTYPE_L4_FRAG,
+ RTE_PTYPE_L4_UDP,
+ RTE_PTYPE_L4_TCP,
+ RTE_PTYPE_L4_SCTP,
+ };
+
+ return ptypes;
+}
+
+static int
+tap_flow_ctrl_get(struct rte_eth_dev *dev __rte_unused,
+ struct rte_eth_fc_conf *fc_conf)
+{
+ fc_conf->mode = RTE_FC_NONE;
+ return 0;
+}
+
+static int
+tap_flow_ctrl_set(struct rte_eth_dev *dev __rte_unused,
+ struct rte_eth_fc_conf *fc_conf)
+{
+ if (fc_conf->mode != RTE_FC_NONE)
+ return -ENOTSUP;
+ return 0;
+}
+
+/**
+ * DPDK callback to update the RSS hash configuration.
+ *
+ * @param dev
+ * Pointer to Ethernet device structure.
+ * @param[in] rss_conf
+ * RSS configuration data.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+tap_rss_hash_update(struct rte_eth_dev *dev,
+ struct rte_eth_rss_conf *rss_conf)
+{
+ if (rss_conf->rss_hf & TAP_RSS_HF_MASK) {
+ rte_errno = EINVAL;
+ return -rte_errno;
+ }
+ if (rss_conf->rss_key && rss_conf->rss_key_len) {
+ /*
+ * Currently TAP RSS key is hard coded
+ * and cannot be updated
+ */
+ TAP_LOG(ERR,
+ "port %u RSS key cannot be updated",
+ dev->data->port_id);
+ rte_errno = EINVAL;
+ return -rte_errno;
+ }
+ return 0;
+}
+
+static int
+tap_rx_queue_start(struct rte_eth_dev *dev, uint16_t rx_queue_id)
+{
+ dev->data->rx_queue_state[rx_queue_id] = RTE_ETH_QUEUE_STATE_STARTED;
+
+ return 0;
+}
+
+static int
+tap_tx_queue_start(struct rte_eth_dev *dev, uint16_t tx_queue_id)
+{
+ dev->data->tx_queue_state[tx_queue_id] = RTE_ETH_QUEUE_STATE_STARTED;
+
+ return 0;
+}
+
+static int
+tap_rx_queue_stop(struct rte_eth_dev *dev, uint16_t rx_queue_id)
+{
+ dev->data->rx_queue_state[rx_queue_id] = RTE_ETH_QUEUE_STATE_STOPPED;
+
+ return 0;
+}
+
+static int
+tap_tx_queue_stop(struct rte_eth_dev *dev, uint16_t tx_queue_id)
+{
+ dev->data->tx_queue_state[tx_queue_id] = RTE_ETH_QUEUE_STATE_STOPPED;
+
+ return 0;
+}
+static const struct eth_dev_ops ops = {
+ .dev_start = tap_dev_start,
+ .dev_stop = tap_dev_stop,
+ .dev_close = tap_dev_close,
+ .dev_configure = tap_dev_configure,
+ .dev_infos_get = tap_dev_info,
+ .rx_queue_setup = tap_rx_queue_setup,
+ .tx_queue_setup = tap_tx_queue_setup,
+ .rx_queue_start = tap_rx_queue_start,
+ .tx_queue_start = tap_tx_queue_start,
+ .rx_queue_stop = tap_rx_queue_stop,
+ .tx_queue_stop = tap_tx_queue_stop,
+ .rx_queue_release = tap_rx_queue_release,
+ .tx_queue_release = tap_tx_queue_release,
+ .flow_ctrl_get = tap_flow_ctrl_get,
+ .flow_ctrl_set = tap_flow_ctrl_set,
+ .link_update = tap_link_update,
+ .dev_set_link_up = tap_link_set_up,
+ .dev_set_link_down = tap_link_set_down,
+ .promiscuous_enable = tap_promisc_enable,
+ .promiscuous_disable = tap_promisc_disable,
+ .allmulticast_enable = tap_allmulti_enable,
+ .allmulticast_disable = tap_allmulti_disable,
+ .mac_addr_set = tap_mac_set,
+ .mtu_set = tap_mtu_set,
+ .set_mc_addr_list = tap_set_mc_addr_list,
+ .stats_get = tap_stats_get,
+ .stats_reset = tap_stats_reset,
+ .dev_supported_ptypes_get = tap_dev_supported_ptypes_get,
+ .rss_hash_update = tap_rss_hash_update,
+ .filter_ctrl = tap_dev_filter_ctrl,
+};
+
+static int
+eth_dev_tap_create(struct rte_vdev_device *vdev, char *tap_name,
+ char *remote_iface, struct ether_addr *mac_addr,
+ enum rte_tuntap_type type)
+{
+ int numa_node = rte_socket_id();
+ struct rte_eth_dev *dev;
+ struct pmd_internals *pmd;
+ struct rte_eth_dev_data *data;
+ struct ifreq ifr;
+ int i;
+
+ TAP_LOG(DEBUG, "%s device on numa %u",
+ tuntap_name, rte_socket_id());
+
+ dev = rte_eth_vdev_allocate(vdev, sizeof(*pmd));
+ if (!dev) {
+ TAP_LOG(ERR, "%s Unable to allocate device struct",
+ tuntap_name);
+ goto error_exit_nodev;
+ }
+
+ pmd = dev->data->dev_private;
+ pmd->dev = dev;
+ snprintf(pmd->name, sizeof(pmd->name), "%s", tap_name);
+ pmd->type = type;
+
+ pmd->ioctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (pmd->ioctl_sock == -1) {
+ TAP_LOG(ERR,
+ "%s Unable to get a socket for management: %s",
+ tuntap_name, strerror(errno));
+ goto error_exit;
+ }
+
+ /* Setup some default values */
+ data = dev->data;
+ data->dev_private = pmd;
+ data->dev_flags = RTE_ETH_DEV_INTR_LSC;
+ data->numa_node = numa_node;
+
+ data->dev_link = pmd_link;
+ data->mac_addrs = &pmd->eth_addr;
+ /* Set the number of RX and TX queues */
+ data->nb_rx_queues = 0;
+ data->nb_tx_queues = 0;
+
+ dev->dev_ops = &ops;
+ dev->rx_pkt_burst = pmd_rx_burst;
+ dev->tx_pkt_burst = pmd_tx_burst;
+
+ pmd->intr_handle.type = RTE_INTR_HANDLE_EXT;
+ pmd->intr_handle.fd = -1;
+ dev->intr_handle = &pmd->intr_handle;
+
+ /* Presetup the fds to -1 as being not valid */
+ pmd->ka_fd = -1;
+ for (i = 0; i < RTE_PMD_TAP_MAX_QUEUES; i++) {
+ pmd->rxq[i].fd = -1;
+ pmd->txq[i].fd = -1;
+ }
+
+ if (pmd->type == ETH_TUNTAP_TYPE_TAP) {
+ if (is_zero_ether_addr(mac_addr))
+ eth_random_addr((uint8_t *)&pmd->eth_addr);
+ else
+ rte_memcpy(&pmd->eth_addr, mac_addr, sizeof(*mac_addr));
+ }
+
+ /*
+ * Allocate a TUN device keep-alive file descriptor that will only be
+ * closed when the TUN device itself is closed or removed.
+ * This keep-alive file descriptor will guarantee that the TUN device
+ * exists even when all of its queues are closed
+ */
+ pmd->ka_fd = tun_alloc(pmd, 1);
+ if (pmd->ka_fd == -1) {
+ TAP_LOG(ERR, "Unable to create %s interface", tuntap_name);
+ goto error_exit;
+ }
+
+ ifr.ifr_mtu = dev->data->mtu;
+ if (tap_ioctl(pmd, SIOCSIFMTU, &ifr, 1, LOCAL_AND_REMOTE) < 0)
+ goto error_exit;
+
+ if (pmd->type == ETH_TUNTAP_TYPE_TAP) {
+ memset(&ifr, 0, sizeof(struct ifreq));
+ ifr.ifr_hwaddr.sa_family = AF_LOCAL;
+ rte_memcpy(ifr.ifr_hwaddr.sa_data, &pmd->eth_addr,
+ ETHER_ADDR_LEN);
+ if (tap_ioctl(pmd, SIOCSIFHWADDR, &ifr, 0, LOCAL_ONLY) < 0)
+ goto error_exit;
+ }
+
+ /*
+ * Set up everything related to rte_flow:
+ * - netlink socket
+ * - tap / remote if_index
+ * - mandatory QDISCs
+ * - rte_flow actual/implicit lists
+ * - implicit rules
+ */
+ pmd->nlsk_fd = tap_nl_init(0);
+ if (pmd->nlsk_fd == -1) {
+ TAP_LOG(WARNING, "%s: failed to create netlink socket.",
+ pmd->name);
+ goto disable_rte_flow;
+ }
+ pmd->if_index = if_nametoindex(pmd->name);
+ if (!pmd->if_index) {
+ TAP_LOG(ERR, "%s: failed to get if_index.", pmd->name);
+ goto disable_rte_flow;
+ }
+ if (qdisc_create_multiq(pmd->nlsk_fd, pmd->if_index) < 0) {
+ TAP_LOG(ERR, "%s: failed to create multiq qdisc.",
+ pmd->name);
+ goto disable_rte_flow;
+ }
+ if (qdisc_create_ingress(pmd->nlsk_fd, pmd->if_index) < 0) {
+ TAP_LOG(ERR, "%s: failed to create ingress qdisc.",
+ pmd->name);
+ goto disable_rte_flow;
+ }
+ LIST_INIT(&pmd->flows);
+
+ if (strlen(remote_iface)) {
+ pmd->remote_if_index = if_nametoindex(remote_iface);
+ if (!pmd->remote_if_index) {
+ TAP_LOG(ERR, "%s: failed to get %s if_index.",
+ pmd->name, remote_iface);
+ goto error_remote;
+ }
+ snprintf(pmd->remote_iface, RTE_ETH_NAME_MAX_LEN,
+ "%s", remote_iface);
+
+ /* Save state of remote device */
+ tap_ioctl(pmd, SIOCGIFFLAGS, &pmd->remote_initial_flags, 0, REMOTE_ONLY);
+
+ /* Replicate remote MAC address */
+ if (tap_ioctl(pmd, SIOCGIFHWADDR, &ifr, 0, REMOTE_ONLY) < 0) {
+ TAP_LOG(ERR, "%s: failed to get %s MAC address.",
+ pmd->name, pmd->remote_iface);
+ goto error_remote;
+ }
+ rte_memcpy(&pmd->eth_addr, ifr.ifr_hwaddr.sa_data,
+ ETHER_ADDR_LEN);
+ /* The desired MAC is already in ifreq after SIOCGIFHWADDR. */
+ if (tap_ioctl(pmd, SIOCSIFHWADDR, &ifr, 0, LOCAL_ONLY) < 0) {
+ TAP_LOG(ERR, "%s: failed to get %s MAC address.",
+ pmd->name, remote_iface);
+ goto error_remote;
+ }
+
+ /*
+ * Flush usually returns negative value because it tries to
+ * delete every QDISC (and on a running device, one QDISC at
+ * least is needed). Ignore negative return value.
+ */
+ qdisc_flush(pmd->nlsk_fd, pmd->remote_if_index);
+ if (qdisc_create_ingress(pmd->nlsk_fd,
+ pmd->remote_if_index) < 0) {
+ TAP_LOG(ERR, "%s: failed to create ingress qdisc.",
+ pmd->remote_iface);
+ goto error_remote;
+ }
+ LIST_INIT(&pmd->implicit_flows);
+ if (tap_flow_implicit_create(pmd, TAP_REMOTE_TX) < 0 ||
+ tap_flow_implicit_create(pmd, TAP_REMOTE_LOCAL_MAC) < 0 ||
+ tap_flow_implicit_create(pmd, TAP_REMOTE_BROADCAST) < 0 ||
+ tap_flow_implicit_create(pmd, TAP_REMOTE_BROADCASTV6) < 0) {
+ TAP_LOG(ERR,
+ "%s: failed to create implicit rules.",
+ pmd->name);
+ goto error_remote;
+ }
+ }
+
+ rte_eth_dev_probing_finish(dev);
+ return 0;
+
+disable_rte_flow:
+ TAP_LOG(ERR, " Disabling rte flow support: %s(%d)",
+ strerror(errno), errno);
+ if (strlen(remote_iface)) {
+ TAP_LOG(ERR, "Remote feature requires flow support.");
+ goto error_exit;
+ }
+ return 0;
+
+error_remote:
+ TAP_LOG(ERR, " Can't set up remote feature: %s(%d)",
+ strerror(errno), errno);
+ tap_flow_implicit_flush(pmd, NULL);
+
+error_exit:
+ if (pmd->ioctl_sock > 0)
+ close(pmd->ioctl_sock);
+ rte_eth_dev_release_port(dev);
+
+error_exit_nodev:
+ TAP_LOG(ERR, "%s Unable to initialize %s",
+ tuntap_name, rte_vdev_device_name(vdev));
+
+ return -EINVAL;
+}
+
+static int
+set_interface_name(const char *key __rte_unused,
+ const char *value,
+ void *extra_args)
+{
+ char *name = (char *)extra_args;
+
+ if (value)
+ strlcpy(name, value, RTE_ETH_NAME_MAX_LEN - 1);
+ else
+ snprintf(name, RTE_ETH_NAME_MAX_LEN - 1, "%s%d",
+ DEFAULT_TAP_NAME, (tap_unit - 1));
+
+ return 0;
+}
+
+static int
+set_remote_iface(const char *key __rte_unused,
+ const char *value,
+ void *extra_args)
+{
+ char *name = (char *)extra_args;
+
+ if (value)
+ strlcpy(name, value, RTE_ETH_NAME_MAX_LEN);
+
+ return 0;
+}
+
+static int parse_user_mac(struct ether_addr *user_mac,
+ const char *value)
+{
+ unsigned int index = 0;
+ char mac_temp[strlen(ETH_TAP_USR_MAC_FMT) + 1], *mac_byte = NULL;
+
+ if (user_mac == NULL || value == NULL)
+ return 0;
+
+ strlcpy(mac_temp, value, sizeof(mac_temp));
+ mac_byte = strtok(mac_temp, ":");
+
+ while ((mac_byte != NULL) &&
+ (strlen(mac_byte) <= 2) &&
+ (strlen(mac_byte) == strspn(mac_byte,
+ ETH_TAP_CMP_MAC_FMT))) {
+ user_mac->addr_bytes[index++] = strtoul(mac_byte, NULL, 16);
+ mac_byte = strtok(NULL, ":");
+ }
+
+ return index;
+}
+
+static int
+set_mac_type(const char *key __rte_unused,
+ const char *value,
+ void *extra_args)
+{
+ struct ether_addr *user_mac = extra_args;
+
+ if (!value)
+ return 0;
+
+ if (!strncasecmp(ETH_TAP_MAC_FIXED, value, strlen(ETH_TAP_MAC_FIXED))) {
+ static int iface_idx;
+
+ /* fixed mac = 00:64:74:61:70:<iface_idx> */
+ memcpy((char *)user_mac->addr_bytes, "\0dtap", ETHER_ADDR_LEN);
+ user_mac->addr_bytes[ETHER_ADDR_LEN - 1] = iface_idx++ + '0';
+ goto success;
+ }
+
+ if (parse_user_mac(user_mac, value) != 6)
+ goto error;
+success:
+ TAP_LOG(DEBUG, "TAP user MAC param (%s)", value);
+ return 0;
+
+error:
+ TAP_LOG(ERR, "TAP user MAC (%s) is not in format (%s|%s)",
+ value, ETH_TAP_MAC_FIXED, ETH_TAP_USR_MAC_FMT);
+ return -1;
+}
+
+/*
+ * Open a TUN interface device. TUN PMD
+ * 1) sets tap_type as false
+ * 2) intakes iface as argument.
+ * 3) as interface is virtual set speed to 10G
+ */
+static int
+rte_pmd_tun_probe(struct rte_vdev_device *dev)
+{
+ const char *name, *params;
+ int ret;
+ struct rte_kvargs *kvlist = NULL;
+ char tun_name[RTE_ETH_NAME_MAX_LEN];
+ char remote_iface[RTE_ETH_NAME_MAX_LEN];
+ struct rte_eth_dev *eth_dev;
+
+ strcpy(tuntap_name, "TUN");
+
+ name = rte_vdev_device_name(dev);
+ params = rte_vdev_device_args(dev);
+ memset(remote_iface, 0, RTE_ETH_NAME_MAX_LEN);
+
+ if (rte_eal_process_type() == RTE_PROC_SECONDARY &&
+ strlen(params) == 0) {
+ eth_dev = rte_eth_dev_attach_secondary(name);
+ if (!eth_dev) {
+ TAP_LOG(ERR, "Failed to probe %s", name);
+ return -1;
+ }
+ eth_dev->dev_ops = &ops;
+ eth_dev->device = &dev->device;
+ rte_eth_dev_probing_finish(eth_dev);
+ return 0;
+ }
+
+ snprintf(tun_name, sizeof(tun_name), "%s%u",
+ DEFAULT_TUN_NAME, tun_unit++);
+
+ if (params && (params[0] != '\0')) {
+ TAP_LOG(DEBUG, "parameters (%s)", params);
+
+ kvlist = rte_kvargs_parse(params, valid_arguments);
+ if (kvlist) {
+ if (rte_kvargs_count(kvlist, ETH_TAP_IFACE_ARG) == 1) {
+ ret = rte_kvargs_process(kvlist,
+ ETH_TAP_IFACE_ARG,
+ &set_interface_name,
+ tun_name);
+
+ if (ret == -1)
+ goto leave;
+ }
+ }
+ }
+ pmd_link.link_speed = ETH_SPEED_NUM_10G;
+
+ TAP_LOG(NOTICE, "Initializing pmd_tun for %s as %s",
+ name, tun_name);
+
+ ret = eth_dev_tap_create(dev, tun_name, remote_iface, 0,
+ ETH_TUNTAP_TYPE_TUN);
+
+leave:
+ if (ret == -1) {
+ TAP_LOG(ERR, "Failed to create pmd for %s as %s",
+ name, tun_name);
+ tun_unit--; /* Restore the unit number */
+ }
+ rte_kvargs_free(kvlist);
+
+ return ret;
+}
+
+/* Open a TAP interface device.
+ */
+static int
+rte_pmd_tap_probe(struct rte_vdev_device *dev)
+{
+ const char *name, *params;
+ int ret;
+ struct rte_kvargs *kvlist = NULL;
+ int speed;
+ char tap_name[RTE_ETH_NAME_MAX_LEN];
+ char remote_iface[RTE_ETH_NAME_MAX_LEN];
+ struct ether_addr user_mac = { .addr_bytes = {0} };
+ struct rte_eth_dev *eth_dev;
+
+ strcpy(tuntap_name, "TAP");
+
+ name = rte_vdev_device_name(dev);
+ params = rte_vdev_device_args(dev);
+
+ if (rte_eal_process_type() == RTE_PROC_SECONDARY &&
+ strlen(params) == 0) {
+ eth_dev = rte_eth_dev_attach_secondary(name);
+ if (!eth_dev) {
+ TAP_LOG(ERR, "Failed to probe %s", name);
+ return -1;
+ }
+ /* TODO: request info from primary to set up Rx and Tx */
+ eth_dev->dev_ops = &ops;
+ eth_dev->device = &dev->device;
+ rte_eth_dev_probing_finish(eth_dev);
+ return 0;
+ }
+
+ speed = ETH_SPEED_NUM_10G;
+ snprintf(tap_name, sizeof(tap_name), "%s%u",
+ DEFAULT_TAP_NAME, tap_unit++);
+ memset(remote_iface, 0, RTE_ETH_NAME_MAX_LEN);
+
+ if (params && (params[0] != '\0')) {
+ TAP_LOG(DEBUG, "parameters (%s)", params);
+
+ kvlist = rte_kvargs_parse(params, valid_arguments);
+ if (kvlist) {
+ if (rte_kvargs_count(kvlist, ETH_TAP_IFACE_ARG) == 1) {
+ ret = rte_kvargs_process(kvlist,
+ ETH_TAP_IFACE_ARG,
+ &set_interface_name,
+ tap_name);
+ if (ret == -1)
+ goto leave;
+ }
+
+ if (rte_kvargs_count(kvlist, ETH_TAP_REMOTE_ARG) == 1) {
+ ret = rte_kvargs_process(kvlist,
+ ETH_TAP_REMOTE_ARG,
+ &set_remote_iface,
+ remote_iface);
+ if (ret == -1)
+ goto leave;
+ }
+
+ if (rte_kvargs_count(kvlist, ETH_TAP_MAC_ARG) == 1) {
+ ret = rte_kvargs_process(kvlist,
+ ETH_TAP_MAC_ARG,
+ &set_mac_type,
+ &user_mac);
+ if (ret == -1)
+ goto leave;
+ }
+ }
+ }
+ pmd_link.link_speed = speed;
+
+ TAP_LOG(NOTICE, "Initializing pmd_tap for %s as %s",
+ name, tap_name);
+
+ ret = eth_dev_tap_create(dev, tap_name, remote_iface, &user_mac,
+ ETH_TUNTAP_TYPE_TAP);
+
+leave:
+ if (ret == -1) {
+ TAP_LOG(ERR, "Failed to create pmd for %s as %s",
+ name, tap_name);
+ tap_unit--; /* Restore the unit number */
+ }
+ rte_kvargs_free(kvlist);
+
+ return ret;
+}
+
+/* detach a TUNTAP device.
+ */
+static int
+rte_pmd_tap_remove(struct rte_vdev_device *dev)
+{
+ struct rte_eth_dev *eth_dev = NULL;
+ struct pmd_internals *internals;
+ int i;
+
+ /* find the ethdev entry */
+ eth_dev = rte_eth_dev_allocated(rte_vdev_device_name(dev));
+ if (!eth_dev)
+ return 0;
+
+ internals = eth_dev->data->dev_private;
+
+ TAP_LOG(DEBUG, "Closing %s Ethernet device on numa %u",
+ (internals->type == ETH_TUNTAP_TYPE_TAP) ? "TAP" : "TUN",
+ rte_socket_id());
+
+ if (internals->nlsk_fd) {
+ tap_flow_flush(eth_dev, NULL);
+ tap_flow_implicit_flush(internals, NULL);
+ tap_nl_final(internals->nlsk_fd);
+ }
+ for (i = 0; i < RTE_PMD_TAP_MAX_QUEUES; i++) {
+ if (internals->rxq[i].fd != -1) {
+ close(internals->rxq[i].fd);
+ internals->rxq[i].fd = -1;
+ }
+ if (internals->txq[i].fd != -1) {
+ close(internals->txq[i].fd);
+ internals->txq[i].fd = -1;
+ }
+ }
+
+ close(internals->ioctl_sock);
+ rte_free(eth_dev->data->dev_private);
+ rte_eth_dev_release_port(eth_dev);
+
+ if (internals->ka_fd != -1) {
+ close(internals->ka_fd);
+ internals->ka_fd = -1;
+ }
+ return 0;
+}
+
+static struct rte_vdev_driver pmd_tun_drv = {
+ .probe = rte_pmd_tun_probe,
+ .remove = rte_pmd_tap_remove,
+};
+
+static struct rte_vdev_driver pmd_tap_drv = {
+ .probe = rte_pmd_tap_probe,
+ .remove = rte_pmd_tap_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(net_tap, pmd_tap_drv);
+RTE_PMD_REGISTER_VDEV(net_tun, pmd_tun_drv);
+RTE_PMD_REGISTER_ALIAS(net_tap, eth_tap);
+RTE_PMD_REGISTER_PARAM_STRING(net_tun,
+ ETH_TAP_IFACE_ARG "=<string> ");
+RTE_PMD_REGISTER_PARAM_STRING(net_tap,
+ ETH_TAP_IFACE_ARG "=<string> "
+ ETH_TAP_MAC_ARG "=" ETH_TAP_MAC_ARG_FMT " "
+ ETH_TAP_REMOTE_ARG "=<string>");
+int tap_logtype;
+
+RTE_INIT(tap_init_log)
+{
+ tap_logtype = rte_log_register("pmd.net.tap");
+ if (tap_logtype >= 0)
+ rte_log_set_level(tap_logtype, RTE_LOG_NOTICE);
+}
diff --git a/src/spdk/dpdk/drivers/net/tap/rte_eth_tap.h b/src/spdk/dpdk/drivers/net/tap/rte_eth_tap.h
new file mode 100644
index 00000000..44e2773f
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/rte_eth_tap.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 6WIND S.A.
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#ifndef _RTE_ETH_TAP_H_
+#define _RTE_ETH_TAP_H_
+
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <inttypes.h>
+#include <net/if.h>
+
+#include <linux/if_tun.h>
+
+#include <rte_ethdev_driver.h>
+#include <rte_ether.h>
+#include <rte_gso.h>
+#include "tap_log.h"
+
+#ifdef IFF_MULTI_QUEUE
+#define RTE_PMD_TAP_MAX_QUEUES TAP_MAX_QUEUES
+#else
+#define RTE_PMD_TAP_MAX_QUEUES 1
+#endif
+#define MAX_GSO_MBUFS 64
+
+enum rte_tuntap_type {
+ ETH_TUNTAP_TYPE_UNKNOWN,
+ ETH_TUNTAP_TYPE_TUN,
+ ETH_TUNTAP_TYPE_TAP,
+ ETH_TUNTAP_TYPE_MAX,
+};
+
+struct pkt_stats {
+ uint64_t opackets; /* Number of output packets */
+ uint64_t ipackets; /* Number of input packets */
+ uint64_t obytes; /* Number of bytes on output */
+ uint64_t ibytes; /* Number of bytes on input */
+ uint64_t errs; /* Number of TX error packets */
+ uint64_t ierrors; /* Number of RX error packets */
+ uint64_t rx_nombuf; /* Nb of RX mbuf alloc failures */
+};
+
+struct rx_queue {
+ struct rte_mempool *mp; /* Mempool for RX packets */
+ uint32_t trigger_seen; /* Last seen Rx trigger value */
+ uint16_t in_port; /* Port ID */
+ int fd;
+ struct pkt_stats stats; /* Stats for this RX queue */
+ uint16_t nb_rx_desc; /* max number of mbufs available */
+ struct rte_eth_rxmode *rxmode; /* RX features */
+ struct rte_mbuf *pool; /* mbufs pool for this queue */
+ struct iovec (*iovecs)[]; /* descriptors for this queue */
+ struct tun_pi pi; /* packet info for iovecs */
+};
+
+struct tx_queue {
+ int fd;
+ int type; /* Type field - TUN|TAP */
+ uint16_t *mtu; /* Pointer to MTU from dev_data */
+ uint16_t csum:1; /* Enable checksum offloading */
+ struct pkt_stats stats; /* Stats for this TX queue */
+ struct rte_gso_ctx gso_ctx; /* GSO context */
+};
+
+struct pmd_internals {
+ struct rte_eth_dev *dev; /* Ethernet device. */
+ char remote_iface[RTE_ETH_NAME_MAX_LEN]; /* Remote netdevice name */
+ char name[RTE_ETH_NAME_MAX_LEN]; /* Internal Tap device name */
+ int type; /* Type field - TUN|TAP */
+ struct ether_addr eth_addr; /* Mac address of the device port */
+ struct ifreq remote_initial_flags; /* Remote netdevice flags on init */
+ int remote_if_index; /* remote netdevice IF_INDEX */
+ int if_index; /* IF_INDEX for the port */
+ int ioctl_sock; /* socket for ioctl calls */
+ int nlsk_fd; /* Netlink socket fd */
+ int flow_isolate; /* 1 if flow isolation is enabled */
+ int flower_support; /* 1 if kernel supports, else 0 */
+ int flower_vlan_support; /* 1 if kernel supports, else 0 */
+ int rss_enabled; /* 1 if RSS is enabled, else 0 */
+ /* implicit rules set when RSS is enabled */
+ int map_fd; /* BPF RSS map fd */
+ int bpf_fd[RTE_PMD_TAP_MAX_QUEUES];/* List of bpf fds per queue */
+ LIST_HEAD(tap_rss_flows, rte_flow) rss_flows;
+ LIST_HEAD(tap_flows, rte_flow) flows; /* rte_flow rules */
+ /* implicit rte_flow rules set when a remote device is active */
+ LIST_HEAD(tap_implicit_flows, rte_flow) implicit_flows;
+ struct rx_queue rxq[RTE_PMD_TAP_MAX_QUEUES]; /* List of RX queues */
+ struct tx_queue txq[RTE_PMD_TAP_MAX_QUEUES]; /* List of TX queues */
+ struct rte_intr_handle intr_handle; /* LSC interrupt handle. */
+ int ka_fd; /* keep-alive file descriptor */
+};
+
+/* tap_intr.c */
+
+int tap_rx_intr_vec_set(struct rte_eth_dev *dev, int set);
+
+#endif /* _RTE_ETH_TAP_H_ */
diff --git a/src/spdk/dpdk/drivers/net/tap/rte_pmd_tap_version.map b/src/spdk/dpdk/drivers/net/tap/rte_pmd_tap_version.map
new file mode 100644
index 00000000..31eca32e
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/rte_pmd_tap_version.map
@@ -0,0 +1,4 @@
+DPDK_17.02 {
+
+ local: *;
+};
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_bpf.h b/src/spdk/dpdk/drivers/net/tap/tap_bpf.h
new file mode 100644
index 00000000..9192686a
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_bpf.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#ifndef __TAP_BPF_H__
+#define __TAP_BPF_H__
+
+#include <tap_autoconf.h>
+
+/* Do not #include <linux/bpf.h> since eBPF must compile on different
+ * distros which may include partial definitions for eBPF (while the
+ * kernel itself may support eBPF). Instead define here all that is needed
+ */
+
+/* BPF_MAP_UPDATE_ELEM command flags */
+#define BPF_ANY 0 /* create a new element or update an existing */
+
+/* BPF architecture instruction struct */
+struct bpf_insn {
+ __u8 code;
+ __u8 dst_reg:4;
+ __u8 src_reg:4;
+ __s16 off;
+ __s32 imm; /* immediate value */
+};
+
+/* BPF program types */
+enum bpf_prog_type {
+ BPF_PROG_TYPE_UNSPEC,
+ BPF_PROG_TYPE_SOCKET_FILTER,
+ BPF_PROG_TYPE_KPROBE,
+ BPF_PROG_TYPE_SCHED_CLS,
+ BPF_PROG_TYPE_SCHED_ACT,
+};
+
+/* BPF commands types */
+enum bpf_cmd {
+ BPF_MAP_CREATE,
+ BPF_MAP_LOOKUP_ELEM,
+ BPF_MAP_UPDATE_ELEM,
+ BPF_MAP_DELETE_ELEM,
+ BPF_MAP_GET_NEXT_KEY,
+ BPF_PROG_LOAD,
+};
+
+/* BPF maps types */
+enum bpf_map_type {
+ BPF_MAP_TYPE_UNSPEC,
+ BPF_MAP_TYPE_HASH,
+};
+
+/* union of anonymous structs used with TAP BPF commands */
+union bpf_attr {
+ /* BPF_MAP_CREATE command */
+ struct {
+ __u32 map_type;
+ __u32 key_size;
+ __u32 value_size;
+ __u32 max_entries;
+ __u32 map_flags;
+ __u32 inner_map_fd;
+ };
+
+ /* BPF_MAP_UPDATE_ELEM, BPF_MAP_DELETE_ELEM commands */
+ struct {
+ __u32 map_fd;
+ __aligned_u64 key;
+ union {
+ __aligned_u64 value;
+ __aligned_u64 next_key;
+ };
+ __u64 flags;
+ };
+
+ /* BPF_PROG_LOAD command */
+ struct {
+ __u32 prog_type;
+ __u32 insn_cnt;
+ __aligned_u64 insns;
+ __aligned_u64 license;
+ __u32 log_level;
+ __u32 log_size;
+ __aligned_u64 log_buf;
+ __u32 kern_version;
+ __u32 prog_flags;
+ };
+} __attribute__((aligned(8)));
+
+#ifndef __NR_bpf
+# if defined(__i386__)
+# define __NR_bpf 357
+# elif defined(__x86_64__)
+# define __NR_bpf 321
+# elif defined(__arm__)
+# define __NR_bpf 386
+# elif defined(__aarch64__)
+# define __NR_bpf 280
+# elif defined(__sparc__)
+# define __NR_bpf 349
+# elif defined(__s390__)
+# define __NR_bpf 351
+# elif defined(__powerpc__)
+# define __NR_bpf 361
+# else
+# error __NR_bpf not defined
+# endif
+#endif
+
+enum {
+ BPF_MAP_ID_KEY,
+ BPF_MAP_ID_SIMPLE,
+};
+
+static int bpf_load(enum bpf_prog_type type, const struct bpf_insn *insns,
+ size_t insns_cnt, const char *license);
+
+#endif /* __TAP_BPF_H__ */
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_bpf_api.c b/src/spdk/dpdk/drivers/net/tap/tap_bpf_api.c
new file mode 100644
index 00000000..98f6a760
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_bpf_api.c
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include <rte_malloc.h>
+#include <rte_eth_tap.h>
+#include <tap_flow.h>
+#include <tap_autoconf.h>
+#include <tap_tcmsgs.h>
+#include <tap_bpf.h>
+#include <tap_bpf_insns.h>
+
+/**
+ * Load BPF program (section cls_q) into the kernel and return a bpf fd
+ *
+ * @param queue_idx
+ * Queue index matching packet cb
+ *
+ * @return
+ * -1 if the BPF program couldn't be loaded. An fd (int) otherwise.
+ */
+int tap_flow_bpf_cls_q(__u32 queue_idx)
+{
+ cls_q_insns[1].imm = queue_idx;
+
+ return bpf_load(BPF_PROG_TYPE_SCHED_CLS,
+ (struct bpf_insn *)cls_q_insns,
+ RTE_DIM(cls_q_insns),
+ "Dual BSD/GPL");
+}
+
+/**
+ * Load BPF program (section l3_l4) into the kernel and return a bpf fd.
+ *
+ * @param[in] key_idx
+ * RSS MAP key index
+ *
+ * @param[in] map_fd
+ * BPF RSS map file descriptor
+ *
+ * @return
+ * -1 if the BPF program couldn't be loaded. An fd (int) otherwise.
+ */
+int tap_flow_bpf_calc_l3_l4_hash(__u32 key_idx, int map_fd)
+{
+ l3_l4_hash_insns[4].imm = key_idx;
+ l3_l4_hash_insns[9].imm = map_fd;
+
+ return bpf_load(BPF_PROG_TYPE_SCHED_ACT,
+ (struct bpf_insn *)l3_l4_hash_insns,
+ RTE_DIM(l3_l4_hash_insns),
+ "Dual BSD/GPL");
+}
+
+/**
+ * Helper function to convert a pointer to unsigned 64 bits
+ *
+ * @param[in] ptr
+ * pointer to address
+ *
+ * @return
+ * 64 bit unsigned long type of pointer address
+ */
+static inline __u64 ptr_to_u64(const void *ptr)
+{
+ return (__u64)(unsigned long)ptr;
+}
+
+/**
+ * Call BPF system call
+ *
+ * @param[in] cmd
+ * BPF command for program loading, map creation, map entry update, etc
+ *
+ * @param[in] attr
+ * System call attributes relevant to system call command
+ *
+ * @param[in] size
+ * size of attr parameter
+ *
+ * @return
+ * -1 if BPF system call failed, 0 otherwise
+ */
+static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
+ unsigned int size)
+{
+ return syscall(__NR_bpf, cmd, attr, size);
+}
+
+/**
+ * Load BPF instructions to kernel
+ *
+ * @param[in] type
+ * BPF program type: classifieir or action
+ *
+ * @param[in] insns
+ * Array of BPF instructions (equivalent to BPF instructions)
+ *
+ * @param[in] insns_cnt
+ * Number of BPF instructions (size of array)
+ *
+ * @param[in] lincense
+ * License string that must be acknowledged by the kernel
+ *
+ * @return
+ * -1 if the BPF program couldn't be loaded, fd (file descriptor) otherwise
+ */
+static int bpf_load(enum bpf_prog_type type,
+ const struct bpf_insn *insns,
+ size_t insns_cnt,
+ const char *license)
+{
+ union bpf_attr attr = {};
+
+ bzero(&attr, sizeof(attr));
+ attr.prog_type = type;
+ attr.insn_cnt = (__u32)insns_cnt;
+ attr.insns = ptr_to_u64(insns);
+ attr.license = ptr_to_u64(license);
+ attr.log_buf = ptr_to_u64(NULL);
+ attr.log_level = 0;
+ attr.kern_version = 0;
+
+ return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+}
+
+/**
+ * Create BPF map for RSS rules
+ *
+ * @param[in] key_size
+ * map RSS key size
+ *
+ * @param[in] value_size
+ * Map RSS value size
+ *
+ * @param[in] max_entries
+ * Map max number of RSS entries (limit on max RSS rules)
+ *
+ * @return
+ * -1 if BPF map couldn't be created, map fd otherwise
+ */
+int tap_flow_bpf_rss_map_create(unsigned int key_size,
+ unsigned int value_size,
+ unsigned int max_entries)
+{
+ union bpf_attr attr = {};
+
+ bzero(&attr, sizeof(attr));
+ attr.map_type = BPF_MAP_TYPE_HASH;
+ attr.key_size = key_size;
+ attr.value_size = value_size;
+ attr.max_entries = max_entries;
+
+ return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
+}
+
+/**
+ * Update RSS entry in BPF map
+ *
+ * @param[in] fd
+ * RSS map fd
+ *
+ * @param[in] key
+ * Pointer to RSS key whose entry is updated
+ *
+ * @param[in] value
+ * Pointer to RSS new updated value
+ *
+ * @return
+ * -1 if RSS entry failed to be updated, 0 otherwise
+ */
+int tap_flow_bpf_update_rss_elem(int fd, void *key, void *value)
+{
+ union bpf_attr attr = {};
+
+ bzero(&attr, sizeof(attr));
+
+ attr.map_type = BPF_MAP_TYPE_HASH;
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+ attr.flags = BPF_ANY;
+
+ return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
+}
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_bpf_insns.h b/src/spdk/dpdk/drivers/net/tap/tap_bpf_insns.h
new file mode 100644
index 00000000..79e3e66b
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_bpf_insns.h
@@ -0,0 +1,1696 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#include <tap_bpf.h>
+
+/* bpf_insn array matching cls_q section. See tap_bpf_program.c file */
+struct bpf_insn cls_q_insns[] = {
+ {0x61, 2, 1, 52, 0x00000000},
+ {0x18, 3, 0, 0, 0xdeadbeef},
+ {0x00, 0, 0, 0, 0x00000000},
+ {0x63, 10, 3, -4, 0x00000000},
+ {0xb7, 0, 0, 0, 0x00000000},
+ {0x61, 3, 10, -4, 0x00000000},
+ {0x07, 3, 0, 0, 0x7cafe800},
+ {0x67, 3, 0, 0, 0x00000020},
+ {0x77, 3, 0, 0, 0x00000020},
+ {0x5d, 2, 3, 4, 0x00000000},
+ {0xb7, 2, 0, 0, 0x00000000},
+ {0x63, 1, 2, 52, 0x00000000},
+ {0x18, 0, 0, 0, 0xffffffff},
+ {0x00, 0, 0, 0, 0x00000000},
+ {0x95, 0, 0, 0, 0x00000000},
+};
+
+/* bpf_insn array matching l3_l4 section. see tap_bpf_program.c file */
+struct bpf_insn l3_l4_hash_insns[] = {
+ {0xbf, 7, 1, 0, 0x00000000},
+ {0x61, 8, 7, 16, 0x00000000},
+ {0x61, 6, 7, 76, 0x00000000},
+ {0x61, 9, 7, 80, 0x00000000},
+ {0x18, 1, 0, 0, 0xdeadbeef},
+ {0x00, 0, 0, 0, 0x00000000},
+ {0x63, 10, 1, -4, 0x00000000},
+ {0xbf, 2, 10, 0, 0x00000000},
+ {0x07, 2, 0, 0, 0xfffffffc},
+ {0x18, 1, 1, 0, 0x0000cafe},
+ {0x00, 0, 0, 0, 0x00000000},
+ {0x85, 0, 0, 0, 0x00000001},
+ {0x55, 0, 0, 21, 0x00000000},
+ {0xb7, 1, 0, 0, 0x00000a64},
+ {0x6b, 10, 1, -16, 0x00000000},
+ {0x18, 1, 0, 0, 0x69666e6f},
+ {0x00, 0, 0, 0, 0x65727567},
+ {0x7b, 10, 1, -24, 0x00000000},
+ {0x18, 1, 0, 0, 0x6e207369},
+ {0x00, 0, 0, 0, 0x6320746f},
+ {0x7b, 10, 1, -32, 0x00000000},
+ {0x18, 1, 0, 0, 0x20737372},
+ {0x00, 0, 0, 0, 0x2079656b},
+ {0x7b, 10, 1, -40, 0x00000000},
+ {0x18, 1, 0, 0, 0x68736168},
+ {0x00, 0, 0, 0, 0x203a2928},
+ {0x7b, 10, 1, -48, 0x00000000},
+ {0xb7, 7, 0, 0, 0x00000000},
+ {0x73, 10, 7, -14, 0x00000000},
+ {0xbf, 1, 10, 0, 0x00000000},
+ {0x07, 1, 0, 0, 0xffffffd0},
+ {0xb7, 2, 0, 0, 0x00000023},
+ {0x85, 0, 0, 0, 0x00000006},
+ {0x05, 0, 0, 1632, 0x00000000},
+ {0xb7, 1, 0, 0, 0x0000000e},
+ {0x61, 2, 7, 20, 0x00000000},
+ {0x15, 2, 0, 10, 0x00000000},
+ {0x61, 2, 7, 28, 0x00000000},
+ {0x55, 2, 0, 8, 0x0000a888},
+ {0xbf, 2, 7, 0, 0x00000000},
+ {0xb7, 7, 0, 0, 0x00000000},
+ {0xbf, 1, 6, 0, 0x00000000},
+ {0x07, 1, 0, 0, 0x00000012},
+ {0x2d, 1, 9, 1622, 0x00000000},
+ {0xb7, 1, 0, 0, 0x00000012},
+ {0x69, 8, 6, 16, 0x00000000},
+ {0xbf, 7, 2, 0, 0x00000000},
+ {0x7b, 10, 7, -56, 0x00000000},
+ {0x57, 8, 0, 0, 0x0000ffff},
+ {0x15, 8, 0, 409, 0x0000dd86},
+ {0xb7, 7, 0, 0, 0x00000003},
+ {0x55, 8, 0, 1614, 0x00000008},
+ {0x0f, 6, 1, 0, 0x00000000},
+ {0xb7, 7, 0, 0, 0x00000000},
+ {0xbf, 1, 6, 0, 0x00000000},
+ {0x07, 1, 0, 0, 0x00000018},
+ {0x2d, 1, 9, 1609, 0x00000000},
+ {0x71, 3, 6, 12, 0x00000000},
+ {0xbf, 1, 3, 0, 0x00000000},
+ {0x67, 1, 0, 0, 0x00000038},
+ {0xc7, 1, 0, 0, 0x00000020},
+ {0x77, 1, 0, 0, 0x0000001f},
+ {0x57, 1, 0, 0, 0x2cc681d1},
+ {0x67, 3, 0, 0, 0x00000018},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x40000000},
+ {0xb7, 2, 0, 0, 0x00000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x598d03a2},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x20000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb31a0745},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x10000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x66340e8a},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x08000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xcc681d15},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x04000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x98d03a2b},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x02000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x31a07456},
+ {0x57, 3, 0, 0, 0x01000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6340e8ad},
+ {0x71, 3, 6, 13, 0x00000000},
+ {0x67, 3, 0, 0, 0x00000010},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00800000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc681d15b},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00400000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8d03a2b7},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00200000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1a07456f},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00100000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x340e8ade},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00080000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x681d15bd},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00040000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd03a2b7b},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00020000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa07456f6},
+ {0x57, 3, 0, 0, 0x00010000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x40e8aded},
+ {0x71, 3, 6, 14, 0x00000000},
+ {0x67, 3, 0, 0, 0x00000008},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00008000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x81d15bdb},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00004000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x03a2b7b7},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00002000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x07456f6f},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00001000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x0e8adedf},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000800},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1d15bdbf},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000400},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3a2b7b7e},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000200},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7456f6fd},
+ {0x57, 3, 0, 0, 0x00000100},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe8adedfa},
+ {0x71, 3, 6, 15, 0x00000000},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000080},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd15bdbf4},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000040},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa2b7b7e9},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000020},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x456f6fd3},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000010},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8adedfa7},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000008},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x15bdbf4f},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000004},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x2b7b7e9e},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000002},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x56f6fd3d},
+ {0x57, 3, 0, 0, 0x00000001},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xadedfa7b},
+ {0x71, 4, 6, 16, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000038},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0xb7, 3, 0, 0, 0xffffffff},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x5bdbf4f7},
+ {0x67, 4, 0, 0, 0x00000018},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x40000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb7b7e9ef},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x20000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6f6fd3df},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x10000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xdedfa7bf},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x08000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xbdbf4f7f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x04000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7b7e9eff},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x02000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf6fd3dff},
+ {0x57, 4, 0, 0, 0x01000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xedfa7bfe},
+ {0x71, 4, 6, 17, 0x00000000},
+ {0x67, 4, 0, 0, 0x00000010},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00800000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xdbf4f7fc},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00400000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb7e9eff9},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00200000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6fd3dff2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00100000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xdfa7bfe5},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00080000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xbf4f7fca},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00040000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7e9eff94},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00020000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfd3dff28},
+ {0x57, 4, 0, 0, 0x00010000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfa7bfe51},
+ {0x71, 4, 6, 18, 0x00000000},
+ {0x67, 4, 0, 0, 0x00000008},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00008000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf4f7fca2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00004000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe9eff945},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00002000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd3dff28a},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00001000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa7bfe514},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000800},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x4f7fca28},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000400},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9eff9450},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000200},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3dff28a0},
+ {0x57, 4, 0, 0, 0x00000100},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7bfe5141},
+ {0x71, 4, 6, 19, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000080},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf7fca283},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000040},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xeff94506},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000020},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xdff28a0c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000010},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xbfe51418},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000008},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7fca2831},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000004},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xff945063},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000002},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xff28a0c6},
+ {0x57, 4, 0, 0, 0x00000001},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfe51418c},
+ {0x71, 4, 6, 20, 0x00000000},
+ {0x67, 4, 0, 0, 0x00000008},
+ {0x71, 5, 6, 21, 0x00000000},
+ {0x4f, 4, 5, 0, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000030},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfca28319},
+ {0x67, 4, 0, 0, 0x00000010},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x40000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf9450633},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x20000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf28a0c67},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x10000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe51418ce},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x08000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xca28319d},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x04000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9450633b},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x02000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x28a0c676},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x01000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x51418ced},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00800000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa28319db},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00400000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x450633b6},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00200000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8a0c676c},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00100000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1418ced8},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00080000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x28319db1},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00040000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x50633b63},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00020000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa0c676c6},
+ {0x57, 4, 0, 0, 0x00010000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x418ced8d},
+ {0x71, 3, 6, 22, 0x00000000},
+ {0x67, 3, 0, 0, 0x00000008},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00008000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8319db1a},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00004000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x0633b634},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00002000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x0c676c68},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00001000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x18ced8d1},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000800},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x319db1a3},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000400},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x633b6347},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000200},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc676c68f},
+ {0x57, 3, 0, 0, 0x00000100},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8ced8d1f},
+ {0x71, 3, 6, 23, 0x00000000},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000080},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x19db1a3e},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000040},
+ {0x79, 5, 10, -56, 0x00000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x33b6347d},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000020},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x676c68fa},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000010},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xced8d1f4},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000008},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9db1a3e9},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000004},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3b6347d2},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000002},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x76c68fa5},
+ {0x57, 3, 0, 0, 0x00000001},
+ {0x1d, 3, 2, 1177, 0x00000000},
+ {0xa7, 1, 0, 0, 0xed8d1f4a},
+ {0x05, 0, 0, 1175, 0x00000000},
+ {0x0f, 6, 1, 0, 0x00000000},
+ {0xb7, 7, 0, 0, 0x00000000},
+ {0xbf, 1, 6, 0, 0x00000000},
+ {0x07, 1, 0, 0, 0x0000002c},
+ {0x2d, 1, 9, 1202, 0x00000000},
+ {0x61, 4, 6, 8, 0x00000000},
+ {0xbf, 1, 4, 0, 0x00000000},
+ {0x67, 1, 0, 0, 0x00000038},
+ {0xc7, 1, 0, 0, 0x00000020},
+ {0x77, 1, 0, 0, 0x0000001f},
+ {0x57, 1, 0, 0, 0x2cc681d1},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000040},
+ {0xb7, 2, 0, 0, 0x00000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x598d03a2},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000020},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb31a0745},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000010},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x66340e8a},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000008},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xcc681d15},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000004},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x98d03a2b},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000002},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x31a07456},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000001},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6340e8ad},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00008000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc681d15b},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00004000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8d03a2b7},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00002000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1a07456f},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00001000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x340e8ade},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000800},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x681d15bd},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000400},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd03a2b7b},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000200},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa07456f6},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00000100},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x40e8aded},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00800000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x81d15bdb},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00400000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x03a2b7b7},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00200000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x07456f6f},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00100000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x0e8adedf},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00080000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1d15bdbf},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00040000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3a2b7b7e},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00020000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7456f6fd},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00010000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe8adedfa},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000020},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0xb7, 3, 0, 0, 0xffffffff},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd15bdbf4},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x40000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa2b7b7e9},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x20000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x456f6fd3},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x10000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8adedfa7},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x08000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x15bdbf4f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x04000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x2b7b7e9e},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x02000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x56f6fd3d},
+ {0x57, 4, 0, 0, 0x01000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xadedfa7b},
+ {0x61, 4, 6, 12, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000080},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x5bdbf4f7},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000040},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb7b7e9ef},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000020},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6f6fd3df},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000010},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xdedfa7bf},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000008},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xbdbf4f7f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000004},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7b7e9eff},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000002},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf6fd3dff},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000001},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xedfa7bfe},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00008000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xdbf4f7fc},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00004000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb7e9eff9},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00002000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6fd3dff2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00001000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xdfa7bfe5},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000800},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xbf4f7fca},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000400},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7e9eff94},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000200},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfd3dff28},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000100},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfa7bfe51},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00800000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf4f7fca2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00400000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe9eff945},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00200000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd3dff28a},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00100000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa7bfe514},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00080000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x4f7fca28},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00040000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9eff9450},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00020000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3dff28a0},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00010000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7bfe5141},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000020},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf7fca283},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x40000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xeff94506},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x20000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xdff28a0c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x10000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xbfe51418},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x08000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7fca2831},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x04000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xff945063},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x02000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xff28a0c6},
+ {0x57, 4, 0, 0, 0x01000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfe51418c},
+ {0x61, 4, 6, 16, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000080},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfca28319},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000040},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf9450633},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000020},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf28a0c67},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000010},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe51418ce},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000008},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xca28319d},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000004},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9450633b},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000002},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x28a0c676},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000001},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x51418ced},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00008000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa28319db},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00004000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x450633b6},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00002000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8a0c676c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00001000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1418ced8},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000800},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x28319db1},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000400},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x50633b63},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000200},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa0c676c6},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000100},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x418ced8d},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00800000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8319db1a},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00400000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x0633b634},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00200000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x0c676c68},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00100000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x18ced8d1},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00080000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x319db1a3},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00040000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x633b6347},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00020000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc676c68f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00010000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8ced8d1f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000020},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x19db1a3e},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x40000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x33b6347d},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x20000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x676c68fa},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x10000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xced8d1f4},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x08000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9db1a3e9},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x04000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3b6347d2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x02000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x76c68fa5},
+ {0x57, 4, 0, 0, 0x01000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xed8d1f4a},
+ {0x61, 4, 6, 20, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000080},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xdb1a3e94},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000040},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb6347d28},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000020},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6c68fa51},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000010},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd8d1f4a3},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000008},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb1a3e946},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000004},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6347d28d},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000002},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc68fa51a},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000001},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8d1f4a35},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00008000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1a3e946b},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00004000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x347d28d7},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00002000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x68fa51ae},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00001000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd1f4a35c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000800},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa3e946b9},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000400},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x47d28d73},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000200},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8fa51ae7},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000100},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1f4a35cf},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00800000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3e946b9e},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00400000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7d28d73c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00200000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfa51ae78},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00100000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf4a35cf1},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00080000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe946b9e3},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00040000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd28d73c7},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00020000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa51ae78e},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00010000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x4a35cf1c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000020},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x946b9e38},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x40000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x28d73c71},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x20000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x51ae78e3},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x10000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa35cf1c6},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x08000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x46b9e38d},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x04000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8d73c71b},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x02000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1ae78e36},
+ {0x57, 4, 0, 0, 0x01000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x35cf1c6c},
+ {0x61, 4, 6, 24, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000080},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6b9e38d9},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000040},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd73c71b2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000020},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xae78e364},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000010},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x5cf1c6c9},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000008},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb9e38d92},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000004},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x73c71b25},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000002},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe78e364b},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000001},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xcf1c6c96},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00008000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9e38d92c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00004000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3c71b259},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00002000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x78e364b2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00001000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf1c6c964},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000800},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe38d92c9},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000400},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc71b2593},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000200},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8e364b27},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000100},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1c6c964e},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00800000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x38d92c9c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00400000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x71b25938},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00200000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe364b270},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00100000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc6c964e0},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00080000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8d92c9c0},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00040000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1b259380},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00020000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x364b2700},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00010000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6c964e01},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000020},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd92c9c03},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x40000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb2593807},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x20000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x64b2700f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x10000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc964e01e},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x08000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x92c9c03d},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x04000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x2593807a},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x02000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x4b2700f4},
+ {0x57, 4, 0, 0, 0x01000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x964e01e8},
+ {0x61, 4, 6, 28, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000080},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x2c9c03d1},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000040},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x593807a3},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000020},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb2700f46},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000010},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x64e01e8d},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000008},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc9c03d1a},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000004},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x93807a35},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000002},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x2700f46b},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000001},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x4e01e8d6},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00008000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9c03d1ad},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00004000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3807a35b},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00002000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x700f46b6},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00001000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe01e8d6c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000800},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc03d1ad9},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000400},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x807a35b3},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000200},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x00f46b66},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000100},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x01e8d6cc},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00800000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x03d1ad99},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00400000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x07a35b32},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00200000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x0f46b665},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00100000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1e8d6cca},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00080000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3d1ad994},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00040000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7a35b328},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00020000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf46b6651},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00010000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe8d6cca2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000020},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd1ad9944},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x40000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa35b3289},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x20000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x46b66512},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x10000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8d6cca25},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x08000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1ad9944a},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x04000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x35b32894},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x02000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6b665129},
+ {0x57, 4, 0, 0, 0x01000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd6cca253},
+ {0x61, 4, 6, 32, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000080},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xad9944a7},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000040},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x5b32894f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000020},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb665129f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000010},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6cca253e},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000008},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd9944a7d},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000004},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb32894fb},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000002},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x665129f6},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000001},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xcca253ec},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00008000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9944a7d9},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00004000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x32894fb2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00002000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x65129f65},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00001000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xca253eca},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000800},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x944a7d95},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000400},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x2894fb2a},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000200},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x5129f655},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000100},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa253ecab},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00800000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x44a7d956},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00400000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x894fb2ac},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00200000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x129f6558},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00100000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x253ecab1},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00080000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x4a7d9563},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00040000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x94fb2ac7},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00020000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x29f6558f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00010000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x53ecab1e},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000020},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xa7d9563d},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x40000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x4fb2ac7a},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x20000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9f6558f5},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x10000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3ecab1ea},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x08000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7d9563d5},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x04000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfb2ac7ab},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x02000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf6558f56},
+ {0x57, 4, 0, 0, 0x01000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xecab1eac},
+ {0x61, 4, 6, 36, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000080},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd9563d59},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000040},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb2ac7ab2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000020},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6558f564},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000010},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xcab1eac8},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000008},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9563d590},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000004},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x2ac7ab20},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000002},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x558f5641},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000001},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xab1eac83},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00008000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x563d5906},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00004000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xac7ab20c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00002000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x58f56418},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00001000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb1eac831},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000800},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x63d59063},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000400},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc7ab20c7},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000200},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8f56418f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00000100},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1eac831e},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00800000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3d59063c},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00400000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7ab20c78},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00200000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf56418f0},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00100000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xeac831e1},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00080000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xd59063c2},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00040000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xab20c784},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00020000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x56418f09},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x00010000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xac831e12},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000020},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x59063c25},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x40000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xb20c784b},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x20000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x6418f097},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x10000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc831e12f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x08000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9063c25f},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x04000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x20c784be},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x57, 5, 0, 0, 0x02000000},
+ {0x1d, 5, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x418f097c},
+ {0x57, 4, 0, 0, 0x01000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x831e12f9},
+ {0x71, 4, 6, 40, 0x00000000},
+ {0x67, 4, 0, 0, 0x00000008},
+ {0x71, 5, 6, 41, 0x00000000},
+ {0x4f, 4, 5, 0, 0x00000000},
+ {0xbf, 5, 4, 0, 0x00000000},
+ {0x67, 5, 0, 0, 0x00000030},
+ {0xc7, 5, 0, 0, 0x00000020},
+ {0x6d, 5, 3, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x063c25f3},
+ {0x67, 4, 0, 0, 0x00000010},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x40000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x0c784be7},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x20000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x18f097cf},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x10000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x31e12f9f},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x08000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x63c25f3f},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x04000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc784be7f},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x02000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x8f097cff},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x01000000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x1e12f9fe},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00800000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3c25f3fc},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00400000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x784be7f8},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00200000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf097cff0},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00100000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe12f9fe0},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00080000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xc25f3fc1},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00040000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x84be7f83},
+ {0xbf, 3, 4, 0, 0x00000000},
+ {0x57, 3, 0, 0, 0x00020000},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x097cff07},
+ {0x57, 4, 0, 0, 0x00010000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x12f9fe0f},
+ {0x71, 3, 6, 42, 0x00000000},
+ {0x67, 3, 0, 0, 0x00000008},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00008000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x25f3fc1f},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00004000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x4be7f83f},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00002000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x97cff07f},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00001000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x2f9fe0fe},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000800},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x5f3fc1fd},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000400},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xbe7f83fb},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000200},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7cff07f7},
+ {0x57, 3, 0, 0, 0x00000100},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf9fe0fee},
+ {0x71, 3, 6, 43, 0x00000000},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000080},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xf3fc1fdc},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000040},
+ {0x79, 5, 10, -56, 0x00000000},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xe7f83fb8},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000020},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xcff07f70},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000010},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x9fe0fee1},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000008},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x3fc1fdc2},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000004},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0x7f83fb85},
+ {0xbf, 4, 3, 0, 0x00000000},
+ {0x57, 4, 0, 0, 0x00000002},
+ {0x1d, 4, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xff07f70a},
+ {0x57, 3, 0, 0, 0x00000001},
+ {0x1d, 3, 2, 1, 0x00000000},
+ {0xa7, 1, 0, 0, 0xfe0fee15},
+ {0x71, 2, 0, 201, 0x00000000},
+ {0x67, 2, 0, 0, 0x00000008},
+ {0x71, 3, 0, 200, 0x00000000},
+ {0x4f, 2, 3, 0, 0x00000000},
+ {0x71, 3, 0, 203, 0x00000000},
+ {0x67, 3, 0, 0, 0x00000008},
+ {0x71, 4, 0, 202, 0x00000000},
+ {0x4f, 3, 4, 0, 0x00000000},
+ {0x67, 3, 0, 0, 0x00000010},
+ {0x4f, 3, 2, 0, 0x00000000},
+ {0x67, 1, 0, 0, 0x00000020},
+ {0x77, 1, 0, 0, 0x00000020},
+ {0xbf, 2, 1, 0, 0x00000000},
+ {0x3f, 2, 3, 0, 0x00000000},
+ {0x2f, 2, 3, 0, 0x00000000},
+ {0x1f, 1, 2, 0, 0x00000000},
+ {0x57, 1, 0, 0, 0x0000000f},
+ {0x67, 1, 0, 0, 0x00000002},
+ {0x0f, 0, 1, 0, 0x00000000},
+ {0x71, 1, 0, 137, 0x00000000},
+ {0x67, 1, 0, 0, 0x00000008},
+ {0x71, 2, 0, 136, 0x00000000},
+ {0x4f, 1, 2, 0, 0x00000000},
+ {0x71, 2, 0, 138, 0x00000000},
+ {0x71, 3, 0, 139, 0x00000000},
+ {0x67, 3, 0, 0, 0x00000008},
+ {0x4f, 3, 2, 0, 0x00000000},
+ {0x67, 3, 0, 0, 0x00000010},
+ {0x4f, 3, 1, 0, 0x00000000},
+ {0x07, 3, 0, 0, 0x7cafe800},
+ {0x63, 5, 3, 52, 0x00000000},
+ {0xb7, 7, 0, 0, 0x00000001},
+ {0xbf, 0, 7, 0, 0x00000000},
+ {0x95, 0, 0, 0, 0x00000000},
+};
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_bpf_program.c b/src/spdk/dpdk/drivers/net/tap/tap_bpf_program.c
new file mode 100644
index 00000000..1cb73822
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_bpf_program.c
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/if_tunnel.h>
+#include <linux/filter.h>
+#include <linux/bpf.h>
+
+#include "tap_rss.h"
+
+/** Create IPv4 address */
+#define IPv4(a, b, c, d) ((__u32)(((a) & 0xff) << 24) | \
+ (((b) & 0xff) << 16) | \
+ (((c) & 0xff) << 8) | \
+ ((d) & 0xff))
+
+#define PORT(a, b) ((__u16)(((a) & 0xff) << 8) | \
+ ((b) & 0xff))
+
+/*
+ * The queue number is offset by a unique QUEUE_OFFSET, to distinguish
+ * packets that have gone through this rule (skb->cb[1] != 0) from others.
+ */
+#define QUEUE_OFFSET 0x7cafe800
+#define PIN_GLOBAL_NS 2
+
+#define KEY_IDX 0
+#define BPF_MAP_ID_KEY 1
+
+struct vlan_hdr {
+ __be16 proto;
+ __be16 tci;
+};
+
+struct bpf_elf_map __attribute__((section("maps"), used))
+map_keys = {
+ .type = BPF_MAP_TYPE_HASH,
+ .id = BPF_MAP_ID_KEY,
+ .size_key = sizeof(__u32),
+ .size_value = sizeof(struct rss_key),
+ .max_elem = 256,
+ .pinning = PIN_GLOBAL_NS,
+};
+
+__section("cls_q") int
+match_q(struct __sk_buff *skb)
+{
+ __u32 queue = skb->cb[1];
+ volatile __u32 q = 0xdeadbeef;
+ __u32 match_queue = QUEUE_OFFSET + q;
+
+ /* printt("match_q$i() queue = %d\n", queue); */
+
+ if (queue != match_queue)
+ return TC_ACT_OK;
+
+ /* queue match */
+ skb->cb[1] = 0;
+ return TC_ACT_UNSPEC;
+}
+
+
+struct ipv4_l3_l4_tuple {
+ __u32 src_addr;
+ __u32 dst_addr;
+ __u16 dport;
+ __u16 sport;
+} __attribute__((packed));
+
+struct ipv6_l3_l4_tuple {
+ __u8 src_addr[16];
+ __u8 dst_addr[16];
+ __u16 dport;
+ __u16 sport;
+} __attribute__((packed));
+
+static const __u8 def_rss_key[TAP_RSS_HASH_KEY_SIZE] = {
+ 0xd1, 0x81, 0xc6, 0x2c,
+ 0xf7, 0xf4, 0xdb, 0x5b,
+ 0x19, 0x83, 0xa2, 0xfc,
+ 0x94, 0x3e, 0x1a, 0xdb,
+ 0xd9, 0x38, 0x9e, 0x6b,
+ 0xd1, 0x03, 0x9c, 0x2c,
+ 0xa7, 0x44, 0x99, 0xad,
+ 0x59, 0x3d, 0x56, 0xd9,
+ 0xf3, 0x25, 0x3c, 0x06,
+ 0x2a, 0xdc, 0x1f, 0xfc,
+};
+
+static __u32 __attribute__((always_inline))
+rte_softrss_be(const __u32 *input_tuple, const uint8_t *rss_key,
+ __u8 input_len)
+{
+ __u32 i, j, hash = 0;
+#pragma unroll
+ for (j = 0; j < input_len; j++) {
+#pragma unroll
+ for (i = 0; i < 32; i++) {
+ if (input_tuple[j] & (1 << (31 - i))) {
+ hash ^= ((const __u32 *)def_rss_key)[j] << i |
+ (__u32)((uint64_t)
+ (((const __u32 *)def_rss_key)[j + 1])
+ >> (32 - i));
+ }
+ }
+ }
+ return hash;
+}
+
+static int __attribute__((always_inline))
+rss_l3_l4(struct __sk_buff *skb)
+{
+ void *data_end = (void *)(long)skb->data_end;
+ void *data = (void *)(long)skb->data;
+ __u16 proto = (__u16)skb->protocol;
+ __u32 key_idx = 0xdeadbeef;
+ __u32 hash;
+ struct rss_key *rsskey;
+ __u64 off = ETH_HLEN;
+ int j;
+ __u8 *key = 0;
+ __u32 len;
+ __u32 queue = 0;
+
+ rsskey = map_lookup_elem(&map_keys, &key_idx);
+ if (!rsskey) {
+ printt("hash(): rss key is not configured\n");
+ return TC_ACT_OK;
+ }
+ key = (__u8 *)rsskey->key;
+
+ /* Get correct proto for 802.1ad */
+ if (skb->vlan_present && skb->vlan_proto == htons(ETH_P_8021AD)) {
+ if (data + ETH_ALEN * 2 + sizeof(struct vlan_hdr) +
+ sizeof(proto) > data_end)
+ return TC_ACT_OK;
+ proto = *(__u16 *)(data + ETH_ALEN * 2 +
+ sizeof(struct vlan_hdr));
+ off += sizeof(struct vlan_hdr);
+ }
+
+ if (proto == htons(ETH_P_IP)) {
+ if (data + off + sizeof(struct iphdr) + sizeof(__u32)
+ > data_end)
+ return TC_ACT_OK;
+
+ __u8 *src_dst_addr = data + off + offsetof(struct iphdr, saddr);
+ __u8 *src_dst_port = data + off + sizeof(struct iphdr);
+ struct ipv4_l3_l4_tuple v4_tuple = {
+ .src_addr = IPv4(*(src_dst_addr + 0),
+ *(src_dst_addr + 1),
+ *(src_dst_addr + 2),
+ *(src_dst_addr + 3)),
+ .dst_addr = IPv4(*(src_dst_addr + 4),
+ *(src_dst_addr + 5),
+ *(src_dst_addr + 6),
+ *(src_dst_addr + 7)),
+ .sport = PORT(*(src_dst_port + 0),
+ *(src_dst_port + 1)),
+ .dport = PORT(*(src_dst_port + 2),
+ *(src_dst_port + 3)),
+ };
+ __u8 input_len = sizeof(v4_tuple) / sizeof(__u32);
+ if (rsskey->hash_fields & (1 << HASH_FIELD_IPV4_L3))
+ input_len--;
+ hash = rte_softrss_be((__u32 *)&v4_tuple, key, 3);
+ } else if (proto == htons(ETH_P_IPV6)) {
+ if (data + off + sizeof(struct ipv6hdr) +
+ sizeof(__u32) > data_end)
+ return TC_ACT_OK;
+ __u8 *src_dst_addr = data + off +
+ offsetof(struct ipv6hdr, saddr);
+ __u8 *src_dst_port = data + off +
+ sizeof(struct ipv6hdr);
+ struct ipv6_l3_l4_tuple v6_tuple;
+ for (j = 0; j < 4; j++)
+ *((uint32_t *)&v6_tuple.src_addr + j) =
+ __builtin_bswap32(*((uint32_t *)
+ src_dst_addr + j));
+ for (j = 0; j < 4; j++)
+ *((uint32_t *)&v6_tuple.dst_addr + j) =
+ __builtin_bswap32(*((uint32_t *)
+ src_dst_addr + 4 + j));
+ v6_tuple.sport = PORT(*(src_dst_port + 0),
+ *(src_dst_port + 1));
+ v6_tuple.dport = PORT(*(src_dst_port + 2),
+ *(src_dst_port + 3));
+
+ __u8 input_len = sizeof(v6_tuple) / sizeof(__u32);
+ if (rsskey->hash_fields & (1 << HASH_FIELD_IPV6_L3))
+ input_len--;
+ hash = rte_softrss_be((__u32 *)&v6_tuple, key, 9);
+ } else {
+ return TC_ACT_PIPE;
+ }
+
+ queue = rsskey->queues[(hash % rsskey->nb_queues) &
+ (TAP_MAX_QUEUES - 1)];
+ skb->cb[1] = QUEUE_OFFSET + queue;
+ /* printt(">>>>> rss_l3_l4 hash=0x%x queue=%u\n", hash, queue); */
+
+ return TC_ACT_RECLASSIFY;
+}
+
+#define RSS(L) \
+ __section(#L) int \
+ L ## _hash(struct __sk_buff *skb) \
+ { \
+ return rss_ ## L (skb); \
+ }
+
+RSS(l3_l4)
+
+BPF_LICENSE("Dual BSD/GPL");
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_flow.c b/src/spdk/dpdk/drivers/net/tap/tap_flow.c
new file mode 100644
index 00000000..0e01af62
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_flow.c
@@ -0,0 +1,2191 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 6WIND S.A.
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/queue.h>
+#include <sys/resource.h>
+
+#include <rte_byteorder.h>
+#include <rte_jhash.h>
+#include <rte_malloc.h>
+#include <rte_eth_tap.h>
+#include <tap_flow.h>
+#include <tap_autoconf.h>
+#include <tap_tcmsgs.h>
+#include <tap_rss.h>
+
+#ifndef HAVE_TC_FLOWER
+/*
+ * For kernels < 4.2, this enum is not defined. Runtime checks will be made to
+ * avoid sending TC messages the kernel cannot understand.
+ */
+enum {
+ TCA_FLOWER_UNSPEC,
+ TCA_FLOWER_CLASSID,
+ TCA_FLOWER_INDEV,
+ TCA_FLOWER_ACT,
+ TCA_FLOWER_KEY_ETH_DST, /* ETH_ALEN */
+ TCA_FLOWER_KEY_ETH_DST_MASK, /* ETH_ALEN */
+ TCA_FLOWER_KEY_ETH_SRC, /* ETH_ALEN */
+ TCA_FLOWER_KEY_ETH_SRC_MASK, /* ETH_ALEN */
+ TCA_FLOWER_KEY_ETH_TYPE, /* be16 */
+ TCA_FLOWER_KEY_IP_PROTO, /* u8 */
+ TCA_FLOWER_KEY_IPV4_SRC, /* be32 */
+ TCA_FLOWER_KEY_IPV4_SRC_MASK, /* be32 */
+ TCA_FLOWER_KEY_IPV4_DST, /* be32 */
+ TCA_FLOWER_KEY_IPV4_DST_MASK, /* be32 */
+ TCA_FLOWER_KEY_IPV6_SRC, /* struct in6_addr */
+ TCA_FLOWER_KEY_IPV6_SRC_MASK, /* struct in6_addr */
+ TCA_FLOWER_KEY_IPV6_DST, /* struct in6_addr */
+ TCA_FLOWER_KEY_IPV6_DST_MASK, /* struct in6_addr */
+ TCA_FLOWER_KEY_TCP_SRC, /* be16 */
+ TCA_FLOWER_KEY_TCP_DST, /* be16 */
+ TCA_FLOWER_KEY_UDP_SRC, /* be16 */
+ TCA_FLOWER_KEY_UDP_DST, /* be16 */
+};
+#endif
+#ifndef HAVE_TC_VLAN_ID
+enum {
+ /* TCA_FLOWER_FLAGS, */
+ TCA_FLOWER_KEY_VLAN_ID = TCA_FLOWER_KEY_UDP_DST + 2, /* be16 */
+ TCA_FLOWER_KEY_VLAN_PRIO, /* u8 */
+ TCA_FLOWER_KEY_VLAN_ETH_TYPE, /* be16 */
+};
+#endif
+/*
+ * For kernels < 4.2 BPF related enums may not be defined.
+ * Runtime checks will be carried out to gracefully report on TC messages that
+ * are rejected by the kernel. Rejection reasons may be due to:
+ * 1. enum is not defined
+ * 2. enum is defined but kernel is not configured to support BPF system calls,
+ * BPF classifications or BPF actions.
+ */
+#ifndef HAVE_TC_BPF
+enum {
+ TCA_BPF_UNSPEC,
+ TCA_BPF_ACT,
+ TCA_BPF_POLICE,
+ TCA_BPF_CLASSID,
+ TCA_BPF_OPS_LEN,
+ TCA_BPF_OPS,
+};
+#endif
+#ifndef HAVE_TC_BPF_FD
+enum {
+ TCA_BPF_FD = TCA_BPF_OPS + 1,
+ TCA_BPF_NAME,
+};
+#endif
+#ifndef HAVE_TC_ACT_BPF
+#define tc_gen \
+ __u32 index; \
+ __u32 capab; \
+ int action; \
+ int refcnt; \
+ int bindcnt
+
+struct tc_act_bpf {
+ tc_gen;
+};
+
+enum {
+ TCA_ACT_BPF_UNSPEC,
+ TCA_ACT_BPF_TM,
+ TCA_ACT_BPF_PARMS,
+ TCA_ACT_BPF_OPS_LEN,
+ TCA_ACT_BPF_OPS,
+};
+
+#endif
+#ifndef HAVE_TC_ACT_BPF_FD
+enum {
+ TCA_ACT_BPF_FD = TCA_ACT_BPF_OPS + 1,
+ TCA_ACT_BPF_NAME,
+};
+#endif
+
+/* RSS key management */
+enum bpf_rss_key_e {
+ KEY_CMD_GET = 1,
+ KEY_CMD_RELEASE,
+ KEY_CMD_INIT,
+ KEY_CMD_DEINIT,
+};
+
+enum key_status_e {
+ KEY_STAT_UNSPEC,
+ KEY_STAT_USED,
+ KEY_STAT_AVAILABLE,
+};
+
+#define ISOLATE_HANDLE 1
+#define REMOTE_PROMISCUOUS_HANDLE 2
+
+struct rte_flow {
+ LIST_ENTRY(rte_flow) next; /* Pointer to the next rte_flow structure */
+ struct rte_flow *remote_flow; /* associated remote flow */
+ int bpf_fd[SEC_MAX]; /* list of bfs fds per ELF section */
+ uint32_t key_idx; /* RSS rule key index into BPF map */
+ struct nlmsg msg;
+};
+
+struct convert_data {
+ uint16_t eth_type;
+ uint16_t ip_proto;
+ uint8_t vlan;
+ struct rte_flow *flow;
+};
+
+struct remote_rule {
+ struct rte_flow_attr attr;
+ struct rte_flow_item items[2];
+ struct rte_flow_action actions[2];
+ int mirred;
+};
+
+struct action_data {
+ char id[16];
+
+ union {
+ struct tc_gact gact;
+ struct tc_mirred mirred;
+ struct skbedit {
+ struct tc_skbedit skbedit;
+ uint16_t queue;
+ } skbedit;
+ struct bpf {
+ struct tc_act_bpf bpf;
+ int bpf_fd;
+ const char *annotation;
+ } bpf;
+ };
+};
+
+static int tap_flow_create_eth(const struct rte_flow_item *item, void *data);
+static int tap_flow_create_vlan(const struct rte_flow_item *item, void *data);
+static int tap_flow_create_ipv4(const struct rte_flow_item *item, void *data);
+static int tap_flow_create_ipv6(const struct rte_flow_item *item, void *data);
+static int tap_flow_create_udp(const struct rte_flow_item *item, void *data);
+static int tap_flow_create_tcp(const struct rte_flow_item *item, void *data);
+static int
+tap_flow_validate(struct rte_eth_dev *dev,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item items[],
+ const struct rte_flow_action actions[],
+ struct rte_flow_error *error);
+
+static struct rte_flow *
+tap_flow_create(struct rte_eth_dev *dev,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item items[],
+ const struct rte_flow_action actions[],
+ struct rte_flow_error *error);
+
+static void
+tap_flow_free(struct pmd_internals *pmd,
+ struct rte_flow *flow);
+
+static int
+tap_flow_destroy(struct rte_eth_dev *dev,
+ struct rte_flow *flow,
+ struct rte_flow_error *error);
+
+static int
+tap_flow_isolate(struct rte_eth_dev *dev,
+ int set,
+ struct rte_flow_error *error);
+
+static int bpf_rss_key(enum bpf_rss_key_e cmd, __u32 *key_idx);
+static int rss_enable(struct pmd_internals *pmd,
+ const struct rte_flow_attr *attr,
+ struct rte_flow_error *error);
+static int rss_add_actions(struct rte_flow *flow, struct pmd_internals *pmd,
+ const struct rte_flow_action_rss *rss,
+ struct rte_flow_error *error);
+
+static const struct rte_flow_ops tap_flow_ops = {
+ .validate = tap_flow_validate,
+ .create = tap_flow_create,
+ .destroy = tap_flow_destroy,
+ .flush = tap_flow_flush,
+ .isolate = tap_flow_isolate,
+};
+
+/* Static initializer for items. */
+#define ITEMS(...) \
+ (const enum rte_flow_item_type []){ \
+ __VA_ARGS__, RTE_FLOW_ITEM_TYPE_END, \
+ }
+
+/* Structure to generate a simple graph of layers supported by the NIC. */
+struct tap_flow_items {
+ /* Bit-mask corresponding to what is supported for this item. */
+ const void *mask;
+ const unsigned int mask_sz; /* Bit-mask size in bytes. */
+ /*
+ * Bit-mask corresponding to the default mask, if none is provided
+ * along with the item.
+ */
+ const void *default_mask;
+ /**
+ * Conversion function from rte_flow to netlink attributes.
+ *
+ * @param item
+ * rte_flow item to convert.
+ * @param data
+ * Internal structure to store the conversion.
+ *
+ * @return
+ * 0 on success, negative value otherwise.
+ */
+ int (*convert)(const struct rte_flow_item *item, void *data);
+ /** List of possible following items. */
+ const enum rte_flow_item_type *const items;
+};
+
+/* Graph of supported items and associated actions. */
+static const struct tap_flow_items tap_flow_items[] = {
+ [RTE_FLOW_ITEM_TYPE_END] = {
+ .items = ITEMS(RTE_FLOW_ITEM_TYPE_ETH),
+ },
+ [RTE_FLOW_ITEM_TYPE_ETH] = {
+ .items = ITEMS(
+ RTE_FLOW_ITEM_TYPE_VLAN,
+ RTE_FLOW_ITEM_TYPE_IPV4,
+ RTE_FLOW_ITEM_TYPE_IPV6),
+ .mask = &(const struct rte_flow_item_eth){
+ .dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+ .src.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+ .type = -1,
+ },
+ .mask_sz = sizeof(struct rte_flow_item_eth),
+ .default_mask = &rte_flow_item_eth_mask,
+ .convert = tap_flow_create_eth,
+ },
+ [RTE_FLOW_ITEM_TYPE_VLAN] = {
+ .items = ITEMS(RTE_FLOW_ITEM_TYPE_IPV4,
+ RTE_FLOW_ITEM_TYPE_IPV6),
+ .mask = &(const struct rte_flow_item_vlan){
+ /* DEI matching is not supported */
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+ .tci = 0xffef,
+#else
+ .tci = 0xefff,
+#endif
+ .inner_type = -1,
+ },
+ .mask_sz = sizeof(struct rte_flow_item_vlan),
+ .default_mask = &rte_flow_item_vlan_mask,
+ .convert = tap_flow_create_vlan,
+ },
+ [RTE_FLOW_ITEM_TYPE_IPV4] = {
+ .items = ITEMS(RTE_FLOW_ITEM_TYPE_UDP,
+ RTE_FLOW_ITEM_TYPE_TCP),
+ .mask = &(const struct rte_flow_item_ipv4){
+ .hdr = {
+ .src_addr = -1,
+ .dst_addr = -1,
+ .next_proto_id = -1,
+ },
+ },
+ .mask_sz = sizeof(struct rte_flow_item_ipv4),
+ .default_mask = &rte_flow_item_ipv4_mask,
+ .convert = tap_flow_create_ipv4,
+ },
+ [RTE_FLOW_ITEM_TYPE_IPV6] = {
+ .items = ITEMS(RTE_FLOW_ITEM_TYPE_UDP,
+ RTE_FLOW_ITEM_TYPE_TCP),
+ .mask = &(const struct rte_flow_item_ipv6){
+ .hdr = {
+ .src_addr = {
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff",
+ },
+ .dst_addr = {
+ "\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff",
+ },
+ .proto = -1,
+ },
+ },
+ .mask_sz = sizeof(struct rte_flow_item_ipv6),
+ .default_mask = &rte_flow_item_ipv6_mask,
+ .convert = tap_flow_create_ipv6,
+ },
+ [RTE_FLOW_ITEM_TYPE_UDP] = {
+ .mask = &(const struct rte_flow_item_udp){
+ .hdr = {
+ .src_port = -1,
+ .dst_port = -1,
+ },
+ },
+ .mask_sz = sizeof(struct rte_flow_item_udp),
+ .default_mask = &rte_flow_item_udp_mask,
+ .convert = tap_flow_create_udp,
+ },
+ [RTE_FLOW_ITEM_TYPE_TCP] = {
+ .mask = &(const struct rte_flow_item_tcp){
+ .hdr = {
+ .src_port = -1,
+ .dst_port = -1,
+ },
+ },
+ .mask_sz = sizeof(struct rte_flow_item_tcp),
+ .default_mask = &rte_flow_item_tcp_mask,
+ .convert = tap_flow_create_tcp,
+ },
+};
+
+/*
+ * TC rules, by growing priority
+ *
+ * Remote netdevice Tap netdevice
+ * +-------------+-------------+ +-------------+-------------+
+ * | Ingress | Egress | | Ingress | Egress |
+ * |-------------|-------------| |-------------|-------------|
+ * | | \ / | | | REMOTE TX | prio 1
+ * | | \ / | | | \ / | prio 2
+ * | EXPLICIT | \ / | | EXPLICIT | \ / | .
+ * | | \ / | | | \ / | .
+ * | RULES | X | | RULES | X | .
+ * | . | / \ | | . | / \ | .
+ * | . | / \ | | . | / \ | .
+ * | . | / \ | | . | / \ | .
+ * | . | / \ | | . | / \ | .
+ *
+ * .... .... .... ....
+ *
+ * | . | \ / | | . | \ / | .
+ * | . | \ / | | . | \ / | .
+ * | | \ / | | | \ / |
+ * | LOCAL_MAC | \ / | | \ / | \ / | last prio - 5
+ * | PROMISC | X | | \ / | X | last prio - 4
+ * | ALLMULTI | / \ | | X | / \ | last prio - 3
+ * | BROADCAST | / \ | | / \ | / \ | last prio - 2
+ * | BROADCASTV6 | / \ | | / \ | / \ | last prio - 1
+ * | xx | / \ | | ISOLATE | / \ | last prio
+ * +-------------+-------------+ +-------------+-------------+
+ *
+ * The implicit flow rules are stored in a list in with mandatorily the last two
+ * being the ISOLATE and REMOTE_TX rules. e.g.:
+ *
+ * LOCAL_MAC -> BROADCAST -> BROADCASTV6 -> REMOTE_TX -> ISOLATE -> NULL
+ *
+ * That enables tap_flow_isolate() to remove implicit rules by popping the list
+ * head and remove it as long as it applies on the remote netdevice. The
+ * implicit rule for TX redirection is not removed, as isolate concerns only
+ * incoming traffic.
+ */
+
+static struct remote_rule implicit_rte_flows[TAP_REMOTE_MAX_IDX] = {
+ [TAP_REMOTE_LOCAL_MAC] = {
+ .attr = {
+ .group = MAX_GROUP,
+ .priority = PRIORITY_MASK - TAP_REMOTE_LOCAL_MAC,
+ .ingress = 1,
+ },
+ .items[0] = {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .mask = &(const struct rte_flow_item_eth){
+ .dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+ },
+ },
+ .items[1] = {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ .mirred = TCA_EGRESS_REDIR,
+ },
+ [TAP_REMOTE_BROADCAST] = {
+ .attr = {
+ .group = MAX_GROUP,
+ .priority = PRIORITY_MASK - TAP_REMOTE_BROADCAST,
+ .ingress = 1,
+ },
+ .items[0] = {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .mask = &(const struct rte_flow_item_eth){
+ .dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+ },
+ .spec = &(const struct rte_flow_item_eth){
+ .dst.addr_bytes = "\xff\xff\xff\xff\xff\xff",
+ },
+ },
+ .items[1] = {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ .mirred = TCA_EGRESS_MIRROR,
+ },
+ [TAP_REMOTE_BROADCASTV6] = {
+ .attr = {
+ .group = MAX_GROUP,
+ .priority = PRIORITY_MASK - TAP_REMOTE_BROADCASTV6,
+ .ingress = 1,
+ },
+ .items[0] = {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .mask = &(const struct rte_flow_item_eth){
+ .dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+ },
+ .spec = &(const struct rte_flow_item_eth){
+ .dst.addr_bytes = "\x33\x33\x00\x00\x00\x00",
+ },
+ },
+ .items[1] = {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ .mirred = TCA_EGRESS_MIRROR,
+ },
+ [TAP_REMOTE_PROMISC] = {
+ .attr = {
+ .group = MAX_GROUP,
+ .priority = PRIORITY_MASK - TAP_REMOTE_PROMISC,
+ .ingress = 1,
+ },
+ .items[0] = {
+ .type = RTE_FLOW_ITEM_TYPE_VOID,
+ },
+ .items[1] = {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ .mirred = TCA_EGRESS_MIRROR,
+ },
+ [TAP_REMOTE_ALLMULTI] = {
+ .attr = {
+ .group = MAX_GROUP,
+ .priority = PRIORITY_MASK - TAP_REMOTE_ALLMULTI,
+ .ingress = 1,
+ },
+ .items[0] = {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .mask = &(const struct rte_flow_item_eth){
+ .dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+ },
+ .spec = &(const struct rte_flow_item_eth){
+ .dst.addr_bytes = "\x01\x00\x00\x00\x00\x00",
+ },
+ },
+ .items[1] = {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ .mirred = TCA_EGRESS_MIRROR,
+ },
+ [TAP_REMOTE_TX] = {
+ .attr = {
+ .group = 0,
+ .priority = TAP_REMOTE_TX,
+ .egress = 1,
+ },
+ .items[0] = {
+ .type = RTE_FLOW_ITEM_TYPE_VOID,
+ },
+ .items[1] = {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ .mirred = TCA_EGRESS_MIRROR,
+ },
+ [TAP_ISOLATE] = {
+ .attr = {
+ .group = MAX_GROUP,
+ .priority = PRIORITY_MASK - TAP_ISOLATE,
+ .ingress = 1,
+ },
+ .items[0] = {
+ .type = RTE_FLOW_ITEM_TYPE_VOID,
+ },
+ .items[1] = {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ },
+};
+
+/**
+ * Make as much checks as possible on an Ethernet item, and if a flow is
+ * provided, fill it appropriately with Ethernet info.
+ *
+ * @param[in] item
+ * Item specification.
+ * @param[in, out] data
+ * Additional data structure to tell next layers we've been here.
+ *
+ * @return
+ * 0 if checks are alright, -1 otherwise.
+ */
+static int
+tap_flow_create_eth(const struct rte_flow_item *item, void *data)
+{
+ struct convert_data *info = (struct convert_data *)data;
+ const struct rte_flow_item_eth *spec = item->spec;
+ const struct rte_flow_item_eth *mask = item->mask;
+ struct rte_flow *flow = info->flow;
+ struct nlmsg *msg;
+
+ /* use default mask if none provided */
+ if (!mask)
+ mask = tap_flow_items[RTE_FLOW_ITEM_TYPE_ETH].default_mask;
+ /* TC does not support eth_type masking. Only accept if exact match. */
+ if (mask->type && mask->type != 0xffff)
+ return -1;
+ if (!spec)
+ return 0;
+ /* store eth_type for consistency if ipv4/6 pattern item comes next */
+ if (spec->type & mask->type)
+ info->eth_type = spec->type;
+ if (!flow)
+ return 0;
+ msg = &flow->msg;
+ if (!is_zero_ether_addr(&mask->dst)) {
+ tap_nlattr_add(&msg->nh, TCA_FLOWER_KEY_ETH_DST, ETHER_ADDR_LEN,
+ &spec->dst.addr_bytes);
+ tap_nlattr_add(&msg->nh,
+ TCA_FLOWER_KEY_ETH_DST_MASK, ETHER_ADDR_LEN,
+ &mask->dst.addr_bytes);
+ }
+ if (!is_zero_ether_addr(&mask->src)) {
+ tap_nlattr_add(&msg->nh, TCA_FLOWER_KEY_ETH_SRC, ETHER_ADDR_LEN,
+ &spec->src.addr_bytes);
+ tap_nlattr_add(&msg->nh,
+ TCA_FLOWER_KEY_ETH_SRC_MASK, ETHER_ADDR_LEN,
+ &mask->src.addr_bytes);
+ }
+ return 0;
+}
+
+/**
+ * Make as much checks as possible on a VLAN item, and if a flow is provided,
+ * fill it appropriately with VLAN info.
+ *
+ * @param[in] item
+ * Item specification.
+ * @param[in, out] data
+ * Additional data structure to tell next layers we've been here.
+ *
+ * @return
+ * 0 if checks are alright, -1 otherwise.
+ */
+static int
+tap_flow_create_vlan(const struct rte_flow_item *item, void *data)
+{
+ struct convert_data *info = (struct convert_data *)data;
+ const struct rte_flow_item_vlan *spec = item->spec;
+ const struct rte_flow_item_vlan *mask = item->mask;
+ struct rte_flow *flow = info->flow;
+ struct nlmsg *msg;
+
+ /* use default mask if none provided */
+ if (!mask)
+ mask = tap_flow_items[RTE_FLOW_ITEM_TYPE_VLAN].default_mask;
+ /* Outer TPID cannot be matched. */
+ if (info->eth_type)
+ return -1;
+ /* Double-tagging not supported. */
+ if (info->vlan)
+ return -1;
+ info->vlan = 1;
+ if (mask->inner_type) {
+ /* TC does not support partial eth_type masking */
+ if (mask->inner_type != RTE_BE16(0xffff))
+ return -1;
+ info->eth_type = spec->inner_type;
+ }
+ if (!flow)
+ return 0;
+ msg = &flow->msg;
+ msg->t.tcm_info = TC_H_MAKE(msg->t.tcm_info, htons(ETH_P_8021Q));
+#define VLAN_PRIO(tci) ((tci) >> 13)
+#define VLAN_ID(tci) ((tci) & 0xfff)
+ if (!spec)
+ return 0;
+ if (spec->tci) {
+ uint16_t tci = ntohs(spec->tci) & mask->tci;
+ uint16_t prio = VLAN_PRIO(tci);
+ uint8_t vid = VLAN_ID(tci);
+
+ if (prio)
+ tap_nlattr_add8(&msg->nh,
+ TCA_FLOWER_KEY_VLAN_PRIO, prio);
+ if (vid)
+ tap_nlattr_add16(&msg->nh,
+ TCA_FLOWER_KEY_VLAN_ID, vid);
+ }
+ return 0;
+}
+
+/**
+ * Make as much checks as possible on an IPv4 item, and if a flow is provided,
+ * fill it appropriately with IPv4 info.
+ *
+ * @param[in] item
+ * Item specification.
+ * @param[in, out] data
+ * Additional data structure to tell next layers we've been here.
+ *
+ * @return
+ * 0 if checks are alright, -1 otherwise.
+ */
+static int
+tap_flow_create_ipv4(const struct rte_flow_item *item, void *data)
+{
+ struct convert_data *info = (struct convert_data *)data;
+ const struct rte_flow_item_ipv4 *spec = item->spec;
+ const struct rte_flow_item_ipv4 *mask = item->mask;
+ struct rte_flow *flow = info->flow;
+ struct nlmsg *msg;
+
+ /* use default mask if none provided */
+ if (!mask)
+ mask = tap_flow_items[RTE_FLOW_ITEM_TYPE_IPV4].default_mask;
+ /* check that previous eth type is compatible with ipv4 */
+ if (info->eth_type && info->eth_type != htons(ETH_P_IP))
+ return -1;
+ /* store ip_proto for consistency if udp/tcp pattern item comes next */
+ if (spec)
+ info->ip_proto = spec->hdr.next_proto_id;
+ if (!flow)
+ return 0;
+ msg = &flow->msg;
+ if (!info->eth_type)
+ info->eth_type = htons(ETH_P_IP);
+ if (!spec)
+ return 0;
+ if (mask->hdr.dst_addr) {
+ tap_nlattr_add32(&msg->nh, TCA_FLOWER_KEY_IPV4_DST,
+ spec->hdr.dst_addr);
+ tap_nlattr_add32(&msg->nh, TCA_FLOWER_KEY_IPV4_DST_MASK,
+ mask->hdr.dst_addr);
+ }
+ if (mask->hdr.src_addr) {
+ tap_nlattr_add32(&msg->nh, TCA_FLOWER_KEY_IPV4_SRC,
+ spec->hdr.src_addr);
+ tap_nlattr_add32(&msg->nh, TCA_FLOWER_KEY_IPV4_SRC_MASK,
+ mask->hdr.src_addr);
+ }
+ if (spec->hdr.next_proto_id)
+ tap_nlattr_add8(&msg->nh, TCA_FLOWER_KEY_IP_PROTO,
+ spec->hdr.next_proto_id);
+ return 0;
+}
+
+/**
+ * Make as much checks as possible on an IPv6 item, and if a flow is provided,
+ * fill it appropriately with IPv6 info.
+ *
+ * @param[in] item
+ * Item specification.
+ * @param[in, out] data
+ * Additional data structure to tell next layers we've been here.
+ *
+ * @return
+ * 0 if checks are alright, -1 otherwise.
+ */
+static int
+tap_flow_create_ipv6(const struct rte_flow_item *item, void *data)
+{
+ struct convert_data *info = (struct convert_data *)data;
+ const struct rte_flow_item_ipv6 *spec = item->spec;
+ const struct rte_flow_item_ipv6 *mask = item->mask;
+ struct rte_flow *flow = info->flow;
+ uint8_t empty_addr[16] = { 0 };
+ struct nlmsg *msg;
+
+ /* use default mask if none provided */
+ if (!mask)
+ mask = tap_flow_items[RTE_FLOW_ITEM_TYPE_IPV6].default_mask;
+ /* check that previous eth type is compatible with ipv6 */
+ if (info->eth_type && info->eth_type != htons(ETH_P_IPV6))
+ return -1;
+ /* store ip_proto for consistency if udp/tcp pattern item comes next */
+ if (spec)
+ info->ip_proto = spec->hdr.proto;
+ if (!flow)
+ return 0;
+ msg = &flow->msg;
+ if (!info->eth_type)
+ info->eth_type = htons(ETH_P_IPV6);
+ if (!spec)
+ return 0;
+ if (memcmp(mask->hdr.dst_addr, empty_addr, 16)) {
+ tap_nlattr_add(&msg->nh, TCA_FLOWER_KEY_IPV6_DST,
+ sizeof(spec->hdr.dst_addr), &spec->hdr.dst_addr);
+ tap_nlattr_add(&msg->nh, TCA_FLOWER_KEY_IPV6_DST_MASK,
+ sizeof(mask->hdr.dst_addr), &mask->hdr.dst_addr);
+ }
+ if (memcmp(mask->hdr.src_addr, empty_addr, 16)) {
+ tap_nlattr_add(&msg->nh, TCA_FLOWER_KEY_IPV6_SRC,
+ sizeof(spec->hdr.src_addr), &spec->hdr.src_addr);
+ tap_nlattr_add(&msg->nh, TCA_FLOWER_KEY_IPV6_SRC_MASK,
+ sizeof(mask->hdr.src_addr), &mask->hdr.src_addr);
+ }
+ if (spec->hdr.proto)
+ tap_nlattr_add8(&msg->nh,
+ TCA_FLOWER_KEY_IP_PROTO, spec->hdr.proto);
+ return 0;
+}
+
+/**
+ * Make as much checks as possible on a UDP item, and if a flow is provided,
+ * fill it appropriately with UDP info.
+ *
+ * @param[in] item
+ * Item specification.
+ * @param[in, out] data
+ * Additional data structure to tell next layers we've been here.
+ *
+ * @return
+ * 0 if checks are alright, -1 otherwise.
+ */
+static int
+tap_flow_create_udp(const struct rte_flow_item *item, void *data)
+{
+ struct convert_data *info = (struct convert_data *)data;
+ const struct rte_flow_item_udp *spec = item->spec;
+ const struct rte_flow_item_udp *mask = item->mask;
+ struct rte_flow *flow = info->flow;
+ struct nlmsg *msg;
+
+ /* use default mask if none provided */
+ if (!mask)
+ mask = tap_flow_items[RTE_FLOW_ITEM_TYPE_UDP].default_mask;
+ /* check that previous ip_proto is compatible with udp */
+ if (info->ip_proto && info->ip_proto != IPPROTO_UDP)
+ return -1;
+ /* TC does not support UDP port masking. Only accept if exact match. */
+ if ((mask->hdr.src_port && mask->hdr.src_port != 0xffff) ||
+ (mask->hdr.dst_port && mask->hdr.dst_port != 0xffff))
+ return -1;
+ if (!flow)
+ return 0;
+ msg = &flow->msg;
+ tap_nlattr_add8(&msg->nh, TCA_FLOWER_KEY_IP_PROTO, IPPROTO_UDP);
+ if (!spec)
+ return 0;
+ if (mask->hdr.dst_port)
+ tap_nlattr_add16(&msg->nh, TCA_FLOWER_KEY_UDP_DST,
+ spec->hdr.dst_port);
+ if (mask->hdr.src_port)
+ tap_nlattr_add16(&msg->nh, TCA_FLOWER_KEY_UDP_SRC,
+ spec->hdr.src_port);
+ return 0;
+}
+
+/**
+ * Make as much checks as possible on a TCP item, and if a flow is provided,
+ * fill it appropriately with TCP info.
+ *
+ * @param[in] item
+ * Item specification.
+ * @param[in, out] data
+ * Additional data structure to tell next layers we've been here.
+ *
+ * @return
+ * 0 if checks are alright, -1 otherwise.
+ */
+static int
+tap_flow_create_tcp(const struct rte_flow_item *item, void *data)
+{
+ struct convert_data *info = (struct convert_data *)data;
+ const struct rte_flow_item_tcp *spec = item->spec;
+ const struct rte_flow_item_tcp *mask = item->mask;
+ struct rte_flow *flow = info->flow;
+ struct nlmsg *msg;
+
+ /* use default mask if none provided */
+ if (!mask)
+ mask = tap_flow_items[RTE_FLOW_ITEM_TYPE_TCP].default_mask;
+ /* check that previous ip_proto is compatible with tcp */
+ if (info->ip_proto && info->ip_proto != IPPROTO_TCP)
+ return -1;
+ /* TC does not support TCP port masking. Only accept if exact match. */
+ if ((mask->hdr.src_port && mask->hdr.src_port != 0xffff) ||
+ (mask->hdr.dst_port && mask->hdr.dst_port != 0xffff))
+ return -1;
+ if (!flow)
+ return 0;
+ msg = &flow->msg;
+ tap_nlattr_add8(&msg->nh, TCA_FLOWER_KEY_IP_PROTO, IPPROTO_TCP);
+ if (!spec)
+ return 0;
+ if (mask->hdr.dst_port)
+ tap_nlattr_add16(&msg->nh, TCA_FLOWER_KEY_TCP_DST,
+ spec->hdr.dst_port);
+ if (mask->hdr.src_port)
+ tap_nlattr_add16(&msg->nh, TCA_FLOWER_KEY_TCP_SRC,
+ spec->hdr.src_port);
+ return 0;
+}
+
+/**
+ * Check support for a given item.
+ *
+ * @param[in] item
+ * Item specification.
+ * @param size
+ * Bit-Mask size in bytes.
+ * @param[in] supported_mask
+ * Bit-mask covering supported fields to compare with spec, last and mask in
+ * \item.
+ * @param[in] default_mask
+ * Bit-mask default mask if none is provided in \item.
+ *
+ * @return
+ * 0 on success.
+ */
+static int
+tap_flow_item_validate(const struct rte_flow_item *item,
+ unsigned int size,
+ const uint8_t *supported_mask,
+ const uint8_t *default_mask)
+{
+ int ret = 0;
+
+ /* An empty layer is allowed, as long as all fields are NULL */
+ if (!item->spec && (item->mask || item->last))
+ return -1;
+ /* Is the item spec compatible with what the NIC supports? */
+ if (item->spec && !item->mask) {
+ unsigned int i;
+ const uint8_t *spec = item->spec;
+
+ for (i = 0; i < size; ++i)
+ if ((spec[i] | supported_mask[i]) != supported_mask[i])
+ return -1;
+ /* Is the default mask compatible with what the NIC supports? */
+ for (i = 0; i < size; i++)
+ if ((default_mask[i] | supported_mask[i]) !=
+ supported_mask[i])
+ return -1;
+ }
+ /* Is the item last compatible with what the NIC supports? */
+ if (item->last && !item->mask) {
+ unsigned int i;
+ const uint8_t *spec = item->last;
+
+ for (i = 0; i < size; ++i)
+ if ((spec[i] | supported_mask[i]) != supported_mask[i])
+ return -1;
+ }
+ /* Is the item mask compatible with what the NIC supports? */
+ if (item->mask) {
+ unsigned int i;
+ const uint8_t *spec = item->mask;
+
+ for (i = 0; i < size; ++i)
+ if ((spec[i] | supported_mask[i]) != supported_mask[i])
+ return -1;
+ }
+ /**
+ * Once masked, Are item spec and item last equal?
+ * TC does not support range so anything else is invalid.
+ */
+ if (item->spec && item->last) {
+ uint8_t spec[size];
+ uint8_t last[size];
+ const uint8_t *apply = default_mask;
+ unsigned int i;
+
+ if (item->mask)
+ apply = item->mask;
+ for (i = 0; i < size; ++i) {
+ spec[i] = ((const uint8_t *)item->spec)[i] & apply[i];
+ last[i] = ((const uint8_t *)item->last)[i] & apply[i];
+ }
+ ret = memcmp(spec, last, size);
+ }
+ return ret;
+}
+
+/**
+ * Configure the kernel with a TC action and its configured parameters
+ * Handled actions: "gact", "mirred", "skbedit", "bpf"
+ *
+ * @param[in] flow
+ * Pointer to rte flow containing the netlink message
+ *
+ * @param[in, out] act_index
+ * Pointer to action sequence number in the TC command
+ *
+ * @param[in] adata
+ * Pointer to struct holding the action parameters
+ *
+ * @return
+ * -1 on failure, 0 on success
+ */
+static int
+add_action(struct rte_flow *flow, size_t *act_index, struct action_data *adata)
+{
+ struct nlmsg *msg = &flow->msg;
+
+ if (tap_nlattr_nested_start(msg, (*act_index)++) < 0)
+ return -1;
+
+ tap_nlattr_add(&msg->nh, TCA_ACT_KIND,
+ strlen(adata->id) + 1, adata->id);
+ if (tap_nlattr_nested_start(msg, TCA_ACT_OPTIONS) < 0)
+ return -1;
+ if (strcmp("gact", adata->id) == 0) {
+ tap_nlattr_add(&msg->nh, TCA_GACT_PARMS, sizeof(adata->gact),
+ &adata->gact);
+ } else if (strcmp("mirred", adata->id) == 0) {
+ if (adata->mirred.eaction == TCA_EGRESS_MIRROR)
+ adata->mirred.action = TC_ACT_PIPE;
+ else /* REDIRECT */
+ adata->mirred.action = TC_ACT_STOLEN;
+ tap_nlattr_add(&msg->nh, TCA_MIRRED_PARMS,
+ sizeof(adata->mirred),
+ &adata->mirred);
+ } else if (strcmp("skbedit", adata->id) == 0) {
+ tap_nlattr_add(&msg->nh, TCA_SKBEDIT_PARMS,
+ sizeof(adata->skbedit.skbedit),
+ &adata->skbedit.skbedit);
+ tap_nlattr_add16(&msg->nh, TCA_SKBEDIT_QUEUE_MAPPING,
+ adata->skbedit.queue);
+ } else if (strcmp("bpf", adata->id) == 0) {
+ tap_nlattr_add32(&msg->nh, TCA_ACT_BPF_FD, adata->bpf.bpf_fd);
+ tap_nlattr_add(&msg->nh, TCA_ACT_BPF_NAME,
+ strlen(adata->bpf.annotation) + 1,
+ adata->bpf.annotation);
+ tap_nlattr_add(&msg->nh, TCA_ACT_BPF_PARMS,
+ sizeof(adata->bpf.bpf),
+ &adata->bpf.bpf);
+ } else {
+ return -1;
+ }
+ tap_nlattr_nested_finish(msg); /* nested TCA_ACT_OPTIONS */
+ tap_nlattr_nested_finish(msg); /* nested act_index */
+ return 0;
+}
+
+/**
+ * Helper function to send a serie of TC actions to the kernel
+ *
+ * @param[in] flow
+ * Pointer to rte flow containing the netlink message
+ *
+ * @param[in] nb_actions
+ * Number of actions in an array of action structs
+ *
+ * @param[in] data
+ * Pointer to an array of action structs
+ *
+ * @param[in] classifier_actions
+ * The classifier on behave of which the actions are configured
+ *
+ * @return
+ * -1 on failure, 0 on success
+ */
+static int
+add_actions(struct rte_flow *flow, int nb_actions, struct action_data *data,
+ int classifier_action)
+{
+ struct nlmsg *msg = &flow->msg;
+ size_t act_index = 1;
+ int i;
+
+ if (tap_nlattr_nested_start(msg, classifier_action) < 0)
+ return -1;
+ for (i = 0; i < nb_actions; i++)
+ if (add_action(flow, &act_index, data + i) < 0)
+ return -1;
+ tap_nlattr_nested_finish(msg); /* nested TCA_FLOWER_ACT */
+ return 0;
+}
+
+/**
+ * Validate a flow supported by TC.
+ * If flow param is not NULL, then also fill the netlink message inside.
+ *
+ * @param pmd
+ * Pointer to private structure.
+ * @param[in] attr
+ * Flow rule attributes.
+ * @param[in] pattern
+ * Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ * Associated actions (list terminated by the END action).
+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ * @param[in, out] flow
+ * Flow structure to update.
+ * @param[in] mirred
+ * If set to TCA_EGRESS_REDIR, provided actions will be replaced with a
+ * redirection to the tap netdevice, and the TC rule will be configured
+ * on the remote netdevice in pmd.
+ * If set to TCA_EGRESS_MIRROR, provided actions will be replaced with a
+ * mirroring to the tap netdevice, and the TC rule will be configured
+ * on the remote netdevice in pmd. Matching packets will thus be duplicated.
+ * If set to 0, the standard behavior is to be used: set correct actions for
+ * the TC rule, and apply it on the tap netdevice.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+priv_flow_process(struct pmd_internals *pmd,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item items[],
+ const struct rte_flow_action actions[],
+ struct rte_flow_error *error,
+ struct rte_flow *flow,
+ int mirred)
+{
+ const struct tap_flow_items *cur_item = tap_flow_items;
+ struct convert_data data = {
+ .eth_type = 0,
+ .ip_proto = 0,
+ .flow = flow,
+ };
+ int action = 0; /* Only one action authorized for now */
+
+ if (attr->transfer) {
+ rte_flow_error_set(
+ error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER,
+ NULL, "transfer is not supported");
+ return -rte_errno;
+ }
+ if (attr->group > MAX_GROUP) {
+ rte_flow_error_set(
+ error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_GROUP,
+ NULL, "group value too big: cannot exceed 15");
+ return -rte_errno;
+ }
+ if (attr->priority > MAX_PRIORITY) {
+ rte_flow_error_set(
+ error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+ NULL, "priority value too big");
+ return -rte_errno;
+ } else if (flow) {
+ uint16_t group = attr->group << GROUP_SHIFT;
+ uint16_t prio = group | (attr->priority +
+ RSS_PRIORITY_OFFSET + PRIORITY_OFFSET);
+ flow->msg.t.tcm_info = TC_H_MAKE(prio << 16,
+ flow->msg.t.tcm_info);
+ }
+ if (flow) {
+ if (mirred) {
+ /*
+ * If attr->ingress, the rule applies on remote ingress
+ * to match incoming packets
+ * If attr->egress, the rule applies on tap ingress (as
+ * seen from the kernel) to deal with packets going out
+ * from the DPDK app.
+ */
+ flow->msg.t.tcm_parent = TC_H_MAKE(TC_H_INGRESS, 0);
+ } else {
+ /* Standard rule on tap egress (kernel standpoint). */
+ flow->msg.t.tcm_parent =
+ TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
+ }
+ /* use flower filter type */
+ tap_nlattr_add(&flow->msg.nh, TCA_KIND, sizeof("flower"), "flower");
+ if (tap_nlattr_nested_start(&flow->msg, TCA_OPTIONS) < 0)
+ goto exit_item_not_supported;
+ }
+ for (; items->type != RTE_FLOW_ITEM_TYPE_END; ++items) {
+ const struct tap_flow_items *token = NULL;
+ unsigned int i;
+ int err = 0;
+
+ if (items->type == RTE_FLOW_ITEM_TYPE_VOID)
+ continue;
+ for (i = 0;
+ cur_item->items &&
+ cur_item->items[i] != RTE_FLOW_ITEM_TYPE_END;
+ ++i) {
+ if (cur_item->items[i] == items->type) {
+ token = &tap_flow_items[items->type];
+ break;
+ }
+ }
+ if (!token)
+ goto exit_item_not_supported;
+ cur_item = token;
+ err = tap_flow_item_validate(
+ items, cur_item->mask_sz,
+ (const uint8_t *)cur_item->mask,
+ (const uint8_t *)cur_item->default_mask);
+ if (err)
+ goto exit_item_not_supported;
+ if (flow && cur_item->convert) {
+ err = cur_item->convert(items, &data);
+ if (err)
+ goto exit_item_not_supported;
+ }
+ }
+ if (flow) {
+ if (data.vlan) {
+ tap_nlattr_add16(&flow->msg.nh, TCA_FLOWER_KEY_ETH_TYPE,
+ htons(ETH_P_8021Q));
+ tap_nlattr_add16(&flow->msg.nh,
+ TCA_FLOWER_KEY_VLAN_ETH_TYPE,
+ data.eth_type ?
+ data.eth_type : htons(ETH_P_ALL));
+ } else if (data.eth_type) {
+ tap_nlattr_add16(&flow->msg.nh, TCA_FLOWER_KEY_ETH_TYPE,
+ data.eth_type);
+ }
+ }
+ if (mirred && flow) {
+ struct action_data adata = {
+ .id = "mirred",
+ .mirred = {
+ .eaction = mirred,
+ },
+ };
+
+ /*
+ * If attr->egress && mirred, then this is a special
+ * case where the rule must be applied on the tap, to
+ * redirect packets coming from the DPDK App, out
+ * through the remote netdevice.
+ */
+ adata.mirred.ifindex = attr->ingress ? pmd->if_index :
+ pmd->remote_if_index;
+ if (mirred == TCA_EGRESS_MIRROR)
+ adata.mirred.action = TC_ACT_PIPE;
+ else
+ adata.mirred.action = TC_ACT_STOLEN;
+ if (add_actions(flow, 1, &adata, TCA_FLOWER_ACT) < 0)
+ goto exit_action_not_supported;
+ else
+ goto end;
+ }
+actions:
+ for (; actions->type != RTE_FLOW_ACTION_TYPE_END; ++actions) {
+ int err = 0;
+
+ if (actions->type == RTE_FLOW_ACTION_TYPE_VOID) {
+ continue;
+ } else if (actions->type == RTE_FLOW_ACTION_TYPE_DROP) {
+ if (action)
+ goto exit_action_not_supported;
+ action = 1;
+ if (flow) {
+ struct action_data adata = {
+ .id = "gact",
+ .gact = {
+ .action = TC_ACT_SHOT,
+ },
+ };
+
+ err = add_actions(flow, 1, &adata,
+ TCA_FLOWER_ACT);
+ }
+ } else if (actions->type == RTE_FLOW_ACTION_TYPE_PASSTHRU) {
+ if (action)
+ goto exit_action_not_supported;
+ action = 1;
+ if (flow) {
+ struct action_data adata = {
+ .id = "gact",
+ .gact = {
+ /* continue */
+ .action = TC_ACT_UNSPEC,
+ },
+ };
+
+ err = add_actions(flow, 1, &adata,
+ TCA_FLOWER_ACT);
+ }
+ } else if (actions->type == RTE_FLOW_ACTION_TYPE_QUEUE) {
+ const struct rte_flow_action_queue *queue =
+ (const struct rte_flow_action_queue *)
+ actions->conf;
+
+ if (action)
+ goto exit_action_not_supported;
+ action = 1;
+ if (!queue ||
+ (queue->index > pmd->dev->data->nb_rx_queues - 1))
+ goto exit_action_not_supported;
+ if (flow) {
+ struct action_data adata = {
+ .id = "skbedit",
+ .skbedit = {
+ .skbedit = {
+ .action = TC_ACT_PIPE,
+ },
+ .queue = queue->index,
+ },
+ };
+
+ err = add_actions(flow, 1, &adata,
+ TCA_FLOWER_ACT);
+ }
+ } else if (actions->type == RTE_FLOW_ACTION_TYPE_RSS) {
+ const struct rte_flow_action_rss *rss =
+ (const struct rte_flow_action_rss *)
+ actions->conf;
+
+ if (action++)
+ goto exit_action_not_supported;
+
+ if (!pmd->rss_enabled) {
+ err = rss_enable(pmd, attr, error);
+ if (err)
+ goto exit_action_not_supported;
+ }
+ if (flow)
+ err = rss_add_actions(flow, pmd, rss, error);
+ } else {
+ goto exit_action_not_supported;
+ }
+ if (err)
+ goto exit_action_not_supported;
+ }
+ /* When fate is unknown, drop traffic. */
+ if (!action) {
+ static const struct rte_flow_action drop[] = {
+ { .type = RTE_FLOW_ACTION_TYPE_DROP, },
+ { .type = RTE_FLOW_ACTION_TYPE_END, },
+ };
+
+ actions = drop;
+ goto actions;
+ }
+end:
+ if (flow)
+ tap_nlattr_nested_finish(&flow->msg); /* nested TCA_OPTIONS */
+ return 0;
+exit_item_not_supported:
+ rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM,
+ items, "item not supported");
+ return -rte_errno;
+exit_action_not_supported:
+ rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,
+ actions, "action not supported");
+ return -rte_errno;
+}
+
+
+
+/**
+ * Validate a flow.
+ *
+ * @see rte_flow_validate()
+ * @see rte_flow_ops
+ */
+static int
+tap_flow_validate(struct rte_eth_dev *dev,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item items[],
+ const struct rte_flow_action actions[],
+ struct rte_flow_error *error)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+
+ return priv_flow_process(pmd, attr, items, actions, error, NULL, 0);
+}
+
+/**
+ * Set a unique handle in a flow.
+ *
+ * The kernel supports TC rules with equal priority, as long as they use the
+ * same matching fields (e.g.: dst mac and ipv4) with different values (and
+ * full mask to ensure no collision is possible).
+ * In those rules, the handle (uint32_t) is the part that would identify
+ * specifically each rule.
+ *
+ * On 32-bit architectures, the handle can simply be the flow's pointer address.
+ * On 64-bit architectures, we rely on jhash(flow) to find a (sufficiently)
+ * unique handle.
+ *
+ * @param[in, out] flow
+ * The flow that needs its handle set.
+ */
+static void
+tap_flow_set_handle(struct rte_flow *flow)
+{
+ uint32_t handle = 0;
+
+ if (sizeof(flow) > 4)
+ handle = rte_jhash(&flow, sizeof(flow), 1);
+ else
+ handle = (uintptr_t)flow;
+ /* must be at least 1 to avoid letting the kernel choose one for us */
+ if (!handle)
+ handle = 1;
+ flow->msg.t.tcm_handle = handle;
+}
+
+/**
+ * Free the flow opened file descriptors and allocated memory
+ *
+ * @param[in] flow
+ * Pointer to the flow to free
+ *
+ */
+static void
+tap_flow_free(struct pmd_internals *pmd, struct rte_flow *flow)
+{
+ int i;
+
+ if (!flow)
+ return;
+
+ if (pmd->rss_enabled) {
+ /* Close flow BPF file descriptors */
+ for (i = 0; i < SEC_MAX; i++)
+ if (flow->bpf_fd[i] != 0) {
+ close(flow->bpf_fd[i]);
+ flow->bpf_fd[i] = 0;
+ }
+
+ /* Release the map key for this RSS rule */
+ bpf_rss_key(KEY_CMD_RELEASE, &flow->key_idx);
+ flow->key_idx = 0;
+ }
+
+ /* Free flow allocated memory */
+ rte_free(flow);
+}
+
+/**
+ * Create a flow.
+ *
+ * @see rte_flow_create()
+ * @see rte_flow_ops
+ */
+static struct rte_flow *
+tap_flow_create(struct rte_eth_dev *dev,
+ const struct rte_flow_attr *attr,
+ const struct rte_flow_item items[],
+ const struct rte_flow_action actions[],
+ struct rte_flow_error *error)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct rte_flow *remote_flow = NULL;
+ struct rte_flow *flow = NULL;
+ struct nlmsg *msg = NULL;
+ int err;
+
+ if (!pmd->if_index) {
+ rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL,
+ "can't create rule, ifindex not found");
+ goto fail;
+ }
+ /*
+ * No rules configured through standard rte_flow should be set on the
+ * priorities used by implicit rules.
+ */
+ if ((attr->group == MAX_GROUP) &&
+ attr->priority > (MAX_PRIORITY - TAP_REMOTE_MAX_IDX)) {
+ rte_flow_error_set(
+ error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+ NULL, "priority value too big");
+ goto fail;
+ }
+ flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+ if (!flow) {
+ rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL, "cannot allocate memory for rte_flow");
+ goto fail;
+ }
+ msg = &flow->msg;
+ tc_init_msg(msg, pmd->if_index, RTM_NEWTFILTER,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+ msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+ tap_flow_set_handle(flow);
+ if (priv_flow_process(pmd, attr, items, actions, error, flow, 0))
+ goto fail;
+ err = tap_nl_send(pmd->nlsk_fd, &msg->nh);
+ if (err < 0) {
+ rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL, "couldn't send request to kernel");
+ goto fail;
+ }
+ err = tap_nl_recv_ack(pmd->nlsk_fd);
+ if (err < 0) {
+ TAP_LOG(ERR,
+ "Kernel refused TC filter rule creation (%d): %s",
+ errno, strerror(errno));
+ rte_flow_error_set(error, EEXIST, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL,
+ "overlapping rules or Kernel too old for flower support");
+ goto fail;
+ }
+ LIST_INSERT_HEAD(&pmd->flows, flow, next);
+ /**
+ * If a remote device is configured, a TC rule with identical items for
+ * matching must be set on that device, with a single action: redirect
+ * to the local pmd->if_index.
+ */
+ if (pmd->remote_if_index) {
+ remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+ if (!remote_flow) {
+ rte_flow_error_set(
+ error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+ "cannot allocate memory for rte_flow");
+ goto fail;
+ }
+ msg = &remote_flow->msg;
+ /* set the rule if_index for the remote netdevice */
+ tc_init_msg(
+ msg, pmd->remote_if_index, RTM_NEWTFILTER,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+ msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+ tap_flow_set_handle(remote_flow);
+ if (priv_flow_process(pmd, attr, items, NULL,
+ error, remote_flow, TCA_EGRESS_REDIR)) {
+ rte_flow_error_set(
+ error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL, "rte flow rule validation failed");
+ goto fail;
+ }
+ err = tap_nl_send(pmd->nlsk_fd, &msg->nh);
+ if (err < 0) {
+ rte_flow_error_set(
+ error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL, "Failure sending nl request");
+ goto fail;
+ }
+ err = tap_nl_recv_ack(pmd->nlsk_fd);
+ if (err < 0) {
+ TAP_LOG(ERR,
+ "Kernel refused TC filter rule creation (%d): %s",
+ errno, strerror(errno));
+ rte_flow_error_set(
+ error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL,
+ "overlapping rules or Kernel too old for flower support");
+ goto fail;
+ }
+ flow->remote_flow = remote_flow;
+ }
+ return flow;
+fail:
+ if (remote_flow)
+ rte_free(remote_flow);
+ if (flow)
+ tap_flow_free(pmd, flow);
+ return NULL;
+}
+
+/**
+ * Destroy a flow using pointer to pmd_internal.
+ *
+ * @param[in, out] pmd
+ * Pointer to private structure.
+ * @param[in] flow
+ * Pointer to the flow to destroy.
+ * @param[in, out] error
+ * Pointer to the flow error handler
+ *
+ * @return 0 if the flow could be destroyed, -1 otherwise.
+ */
+static int
+tap_flow_destroy_pmd(struct pmd_internals *pmd,
+ struct rte_flow *flow,
+ struct rte_flow_error *error)
+{
+ struct rte_flow *remote_flow = flow->remote_flow;
+ int ret = 0;
+
+ LIST_REMOVE(flow, next);
+ flow->msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ flow->msg.nh.nlmsg_type = RTM_DELTFILTER;
+
+ ret = tap_nl_send(pmd->nlsk_fd, &flow->msg.nh);
+ if (ret < 0) {
+ rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL, "couldn't send request to kernel");
+ goto end;
+ }
+ ret = tap_nl_recv_ack(pmd->nlsk_fd);
+ /* If errno is ENOENT, the rule is already no longer in the kernel. */
+ if (ret < 0 && errno == ENOENT)
+ ret = 0;
+ if (ret < 0) {
+ TAP_LOG(ERR,
+ "Kernel refused TC filter rule deletion (%d): %s",
+ errno, strerror(errno));
+ rte_flow_error_set(
+ error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+ "couldn't receive kernel ack to our request");
+ goto end;
+ }
+
+ if (remote_flow) {
+ remote_flow->msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ remote_flow->msg.nh.nlmsg_type = RTM_DELTFILTER;
+
+ ret = tap_nl_send(pmd->nlsk_fd, &remote_flow->msg.nh);
+ if (ret < 0) {
+ rte_flow_error_set(
+ error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL, "Failure sending nl request");
+ goto end;
+ }
+ ret = tap_nl_recv_ack(pmd->nlsk_fd);
+ if (ret < 0 && errno == ENOENT)
+ ret = 0;
+ if (ret < 0) {
+ TAP_LOG(ERR,
+ "Kernel refused TC filter rule deletion (%d): %s",
+ errno, strerror(errno));
+ rte_flow_error_set(
+ error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL, "Failure trying to receive nl ack");
+ goto end;
+ }
+ }
+end:
+ if (remote_flow)
+ rte_free(remote_flow);
+ tap_flow_free(pmd, flow);
+ return ret;
+}
+
+/**
+ * Destroy a flow.
+ *
+ * @see rte_flow_destroy()
+ * @see rte_flow_ops
+ */
+static int
+tap_flow_destroy(struct rte_eth_dev *dev,
+ struct rte_flow *flow,
+ struct rte_flow_error *error)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+
+ return tap_flow_destroy_pmd(pmd, flow, error);
+}
+
+/**
+ * Enable/disable flow isolation.
+ *
+ * @see rte_flow_isolate()
+ * @see rte_flow_ops
+ */
+static int
+tap_flow_isolate(struct rte_eth_dev *dev,
+ int set,
+ struct rte_flow_error *error __rte_unused)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+
+ /* normalize 'set' variable to contain 0 or 1 values */
+ if (set)
+ set = 1;
+ /* if already in the right isolation mode - nothing to do */
+ if ((set ^ pmd->flow_isolate) == 0)
+ return 0;
+ /* mark the isolation mode for tap_flow_implicit_create() */
+ pmd->flow_isolate = set;
+ /*
+ * If netdevice is there, setup appropriate flow rules immediately.
+ * Otherwise it will be set when bringing up the netdevice (tun_alloc).
+ */
+ if (!pmd->rxq[0].fd)
+ return 0;
+ if (set) {
+ struct rte_flow *remote_flow;
+
+ while (1) {
+ remote_flow = LIST_FIRST(&pmd->implicit_flows);
+ if (!remote_flow)
+ break;
+ /*
+ * Remove all implicit rules on the remote.
+ * Keep the local rule to redirect packets on TX.
+ * Keep also the last implicit local rule: ISOLATE.
+ */
+ if (remote_flow->msg.t.tcm_ifindex == pmd->if_index)
+ break;
+ if (tap_flow_destroy_pmd(pmd, remote_flow, NULL) < 0)
+ goto error;
+ }
+ /* Switch the TC rule according to pmd->flow_isolate */
+ if (tap_flow_implicit_create(pmd, TAP_ISOLATE) == -1)
+ goto error;
+ } else {
+ /* Switch the TC rule according to pmd->flow_isolate */
+ if (tap_flow_implicit_create(pmd, TAP_ISOLATE) == -1)
+ goto error;
+ if (!pmd->remote_if_index)
+ return 0;
+ if (tap_flow_implicit_create(pmd, TAP_REMOTE_TX) < 0)
+ goto error;
+ if (tap_flow_implicit_create(pmd, TAP_REMOTE_LOCAL_MAC) < 0)
+ goto error;
+ if (tap_flow_implicit_create(pmd, TAP_REMOTE_BROADCAST) < 0)
+ goto error;
+ if (tap_flow_implicit_create(pmd, TAP_REMOTE_BROADCASTV6) < 0)
+ goto error;
+ if (dev->data->promiscuous &&
+ tap_flow_implicit_create(pmd, TAP_REMOTE_PROMISC) < 0)
+ goto error;
+ if (dev->data->all_multicast &&
+ tap_flow_implicit_create(pmd, TAP_REMOTE_ALLMULTI) < 0)
+ goto error;
+ }
+ return 0;
+error:
+ pmd->flow_isolate = 0;
+ return rte_flow_error_set(
+ error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "TC rule creation failed");
+}
+
+/**
+ * Destroy all flows.
+ *
+ * @see rte_flow_flush()
+ * @see rte_flow_ops
+ */
+int
+tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct rte_flow *flow;
+
+ while (!LIST_EMPTY(&pmd->flows)) {
+ flow = LIST_FIRST(&pmd->flows);
+ if (tap_flow_destroy(dev, flow, error) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Add an implicit flow rule on the remote device to make sure traffic gets to
+ * the tap netdevice from there.
+ *
+ * @param pmd
+ * Pointer to private structure.
+ * @param[in] idx
+ * The idx in the implicit_rte_flows array specifying which rule to apply.
+ *
+ * @return -1 if the rule couldn't be applied, 0 otherwise.
+ */
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+ enum implicit_rule_index idx)
+{
+ uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE;
+ struct rte_flow_action *actions = implicit_rte_flows[idx].actions;
+ struct rte_flow_action isolate_actions[2] = {
+ [1] = {
+ .type = RTE_FLOW_ACTION_TYPE_END,
+ },
+ };
+ struct rte_flow_item *items = implicit_rte_flows[idx].items;
+ struct rte_flow_attr *attr = &implicit_rte_flows[idx].attr;
+ struct rte_flow_item_eth eth_local = { .type = 0 };
+ uint16_t if_index = pmd->remote_if_index;
+ struct rte_flow *remote_flow = NULL;
+ struct nlmsg *msg = NULL;
+ int err = 0;
+ struct rte_flow_item items_local[2] = {
+ [0] = {
+ .type = items[0].type,
+ .spec = &eth_local,
+ .mask = items[0].mask,
+ },
+ [1] = {
+ .type = items[1].type,
+ }
+ };
+
+ remote_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+ if (!remote_flow) {
+ TAP_LOG(ERR, "Cannot allocate memory for rte_flow");
+ goto fail;
+ }
+ msg = &remote_flow->msg;
+ if (idx == TAP_REMOTE_TX) {
+ if_index = pmd->if_index;
+ } else if (idx == TAP_ISOLATE) {
+ if_index = pmd->if_index;
+ /* Don't be exclusive for this rule, it can be changed later. */
+ flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE;
+ isolate_actions[0].type = pmd->flow_isolate ?
+ RTE_FLOW_ACTION_TYPE_DROP :
+ RTE_FLOW_ACTION_TYPE_PASSTHRU;
+ actions = isolate_actions;
+ } else if (idx == TAP_REMOTE_LOCAL_MAC) {
+ /*
+ * eth addr couldn't be set in implicit_rte_flows[] as it is not
+ * known at compile time.
+ */
+ memcpy(&eth_local.dst, &pmd->eth_addr, sizeof(pmd->eth_addr));
+ items = items_local;
+ }
+ tc_init_msg(msg, if_index, RTM_NEWTFILTER, flags);
+ msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+ /*
+ * The ISOLATE rule is always present and must have a static handle, as
+ * the action is changed whether the feature is enabled (DROP) or
+ * disabled (PASSTHRU).
+ * There is just one REMOTE_PROMISCUOUS rule in all cases. It should
+ * have a static handle such that adding it twice will fail with EEXIST
+ * with any kernel version. Remark: old kernels may falsely accept the
+ * same REMOTE_PROMISCUOUS rules if they had different handles.
+ */
+ if (idx == TAP_ISOLATE)
+ remote_flow->msg.t.tcm_handle = ISOLATE_HANDLE;
+ else if (idx == TAP_REMOTE_PROMISC)
+ remote_flow->msg.t.tcm_handle = REMOTE_PROMISCUOUS_HANDLE;
+ else
+ tap_flow_set_handle(remote_flow);
+ if (priv_flow_process(pmd, attr, items, actions, NULL,
+ remote_flow, implicit_rte_flows[idx].mirred)) {
+ TAP_LOG(ERR, "rte flow rule validation failed");
+ goto fail;
+ }
+ err = tap_nl_send(pmd->nlsk_fd, &msg->nh);
+ if (err < 0) {
+ TAP_LOG(ERR, "Failure sending nl request");
+ goto fail;
+ }
+ err = tap_nl_recv_ack(pmd->nlsk_fd);
+ if (err < 0) {
+ /* Silently ignore re-entering existing rule */
+ if (errno == EEXIST)
+ goto success;
+ TAP_LOG(ERR,
+ "Kernel refused TC filter rule creation (%d): %s",
+ errno, strerror(errno));
+ goto fail;
+ }
+ LIST_INSERT_HEAD(&pmd->implicit_flows, remote_flow, next);
+success:
+ return 0;
+fail:
+ if (remote_flow)
+ rte_free(remote_flow);
+ return -1;
+}
+
+/**
+ * Remove specific implicit flow rule on the remote device.
+ *
+ * @param[in, out] pmd
+ * Pointer to private structure.
+ * @param[in] idx
+ * The idx in the implicit_rte_flows array specifying which rule to remove.
+ *
+ * @return -1 if one of the implicit rules couldn't be created, 0 otherwise.
+ */
+int tap_flow_implicit_destroy(struct pmd_internals *pmd,
+ enum implicit_rule_index idx)
+{
+ struct rte_flow *remote_flow;
+ int cur_prio = -1;
+ int idx_prio = implicit_rte_flows[idx].attr.priority + PRIORITY_OFFSET;
+
+ for (remote_flow = LIST_FIRST(&pmd->implicit_flows);
+ remote_flow;
+ remote_flow = LIST_NEXT(remote_flow, next)) {
+ cur_prio = (remote_flow->msg.t.tcm_info >> 16) & PRIORITY_MASK;
+ if (cur_prio != idx_prio)
+ continue;
+ return tap_flow_destroy_pmd(pmd, remote_flow, NULL);
+ }
+ return 0;
+}
+
+/**
+ * Destroy all implicit flows.
+ *
+ * @see rte_flow_flush()
+ */
+int
+tap_flow_implicit_flush(struct pmd_internals *pmd, struct rte_flow_error *error)
+{
+ struct rte_flow *remote_flow;
+
+ while (!LIST_EMPTY(&pmd->implicit_flows)) {
+ remote_flow = LIST_FIRST(&pmd->implicit_flows);
+ if (tap_flow_destroy_pmd(pmd, remote_flow, error) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+#define MAX_RSS_KEYS 256
+#define KEY_IDX_OFFSET (3 * MAX_RSS_KEYS)
+#define SEC_NAME_CLS_Q "cls_q"
+
+const char *sec_name[SEC_MAX] = {
+ [SEC_L3_L4] = "l3_l4",
+};
+
+/**
+ * Enable RSS on tap: create TC rules for queuing.
+ *
+ * @param[in, out] pmd
+ * Pointer to private structure.
+ *
+ * @param[in] attr
+ * Pointer to rte_flow to get flow group
+ *
+ * @param[out] error
+ * Pointer to error reporting if not NULL.
+ *
+ * @return 0 on success, negative value on failure.
+ */
+static int rss_enable(struct pmd_internals *pmd,
+ const struct rte_flow_attr *attr,
+ struct rte_flow_error *error)
+{
+ struct rte_flow *rss_flow = NULL;
+ struct nlmsg *msg = NULL;
+ /* 4096 is the maximum number of instructions for a BPF program */
+ char annotation[64];
+ int i;
+ int err = 0;
+
+ /* unlimit locked memory */
+ struct rlimit memlock_limit = {
+ .rlim_cur = RLIM_INFINITY,
+ .rlim_max = RLIM_INFINITY,
+ };
+ setrlimit(RLIMIT_MEMLOCK, &memlock_limit);
+
+ /* Get a new map key for a new RSS rule */
+ err = bpf_rss_key(KEY_CMD_INIT, NULL);
+ if (err < 0) {
+ rte_flow_error_set(
+ error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+ "Failed to initialize BPF RSS keys");
+
+ return -1;
+ }
+
+ /*
+ * Create BPF RSS MAP
+ */
+ pmd->map_fd = tap_flow_bpf_rss_map_create(sizeof(__u32), /* key size */
+ sizeof(struct rss_key),
+ MAX_RSS_KEYS);
+ if (pmd->map_fd < 0) {
+ TAP_LOG(ERR,
+ "Failed to create BPF map (%d): %s",
+ errno, strerror(errno));
+ rte_flow_error_set(
+ error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+ "Kernel too old or not configured "
+ "to support BPF maps");
+
+ return -ENOTSUP;
+ }
+
+ /*
+ * Add a rule per queue to match reclassified packets and direct them to
+ * the correct queue.
+ */
+ for (i = 0; i < pmd->dev->data->nb_rx_queues; i++) {
+ pmd->bpf_fd[i] = tap_flow_bpf_cls_q(i);
+ if (pmd->bpf_fd[i] < 0) {
+ TAP_LOG(ERR,
+ "Failed to load BPF section %s for queue %d",
+ SEC_NAME_CLS_Q, i);
+ rte_flow_error_set(
+ error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE,
+ NULL,
+ "Kernel too old or not configured "
+ "to support BPF programs loading");
+
+ return -ENOTSUP;
+ }
+
+ rss_flow = rte_malloc(__func__, sizeof(struct rte_flow), 0);
+ if (!rss_flow) {
+ TAP_LOG(ERR,
+ "Cannot allocate memory for rte_flow");
+ return -1;
+ }
+ msg = &rss_flow->msg;
+ tc_init_msg(msg, pmd->if_index, RTM_NEWTFILTER, NLM_F_REQUEST |
+ NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+ msg->t.tcm_info = TC_H_MAKE(0, htons(ETH_P_ALL));
+ tap_flow_set_handle(rss_flow);
+ uint16_t group = attr->group << GROUP_SHIFT;
+ uint16_t prio = group | (i + PRIORITY_OFFSET);
+ msg->t.tcm_info = TC_H_MAKE(prio << 16, msg->t.tcm_info);
+ msg->t.tcm_parent = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
+
+ tap_nlattr_add(&msg->nh, TCA_KIND, sizeof("bpf"), "bpf");
+ if (tap_nlattr_nested_start(msg, TCA_OPTIONS) < 0)
+ return -1;
+ tap_nlattr_add32(&msg->nh, TCA_BPF_FD, pmd->bpf_fd[i]);
+ snprintf(annotation, sizeof(annotation), "[%s%d]",
+ SEC_NAME_CLS_Q, i);
+ tap_nlattr_add(&msg->nh, TCA_BPF_NAME, strlen(annotation) + 1,
+ annotation);
+ /* Actions */
+ {
+ struct action_data adata = {
+ .id = "skbedit",
+ .skbedit = {
+ .skbedit = {
+ .action = TC_ACT_PIPE,
+ },
+ .queue = i,
+ },
+ };
+ if (add_actions(rss_flow, 1, &adata, TCA_BPF_ACT) < 0)
+ return -1;
+ }
+ tap_nlattr_nested_finish(msg); /* nested TCA_OPTIONS */
+
+ /* Netlink message is now ready to be sent */
+ if (tap_nl_send(pmd->nlsk_fd, &msg->nh) < 0)
+ return -1;
+ err = tap_nl_recv_ack(pmd->nlsk_fd);
+ if (err < 0) {
+ TAP_LOG(ERR,
+ "Kernel refused TC filter rule creation (%d): %s",
+ errno, strerror(errno));
+ return err;
+ }
+ LIST_INSERT_HEAD(&pmd->rss_flows, rss_flow, next);
+ }
+
+ pmd->rss_enabled = 1;
+ return err;
+}
+
+/**
+ * Manage bpf RSS keys repository with operations: init, get, release
+ *
+ * @param[in] cmd
+ * Command on RSS keys: init, get, release
+ *
+ * @param[in, out] key_idx
+ * Pointer to RSS Key index (out for get command, in for release command)
+ *
+ * @return -1 if couldn't get, release or init the RSS keys, 0 otherwise.
+ */
+static int bpf_rss_key(enum bpf_rss_key_e cmd, __u32 *key_idx)
+{
+ __u32 i;
+ int err = 0;
+ static __u32 num_used_keys;
+ static __u32 rss_keys[MAX_RSS_KEYS] = {KEY_STAT_UNSPEC};
+ static __u32 rss_keys_initialized;
+ __u32 key;
+
+ switch (cmd) {
+ case KEY_CMD_GET:
+ if (!rss_keys_initialized) {
+ err = -1;
+ break;
+ }
+
+ if (num_used_keys == RTE_DIM(rss_keys)) {
+ err = -1;
+ break;
+ }
+
+ *key_idx = num_used_keys % RTE_DIM(rss_keys);
+ while (rss_keys[*key_idx] == KEY_STAT_USED)
+ *key_idx = (*key_idx + 1) % RTE_DIM(rss_keys);
+
+ rss_keys[*key_idx] = KEY_STAT_USED;
+
+ /*
+ * Add an offset to key_idx in order to handle a case of
+ * RSS and non RSS flows mixture.
+ * If a non RSS flow is destroyed it has an eBPF map
+ * index 0 (initialized on flow creation) and might
+ * unintentionally remove RSS entry 0 from eBPF map.
+ * To avoid this issue, add an offset to the real index
+ * during a KEY_CMD_GET operation and subtract this offset
+ * during a KEY_CMD_RELEASE operation in order to restore
+ * the real index.
+ */
+ *key_idx += KEY_IDX_OFFSET;
+ num_used_keys++;
+ break;
+
+ case KEY_CMD_RELEASE:
+ if (!rss_keys_initialized)
+ break;
+
+ /*
+ * Subtract offest to restore real key index
+ * If a non RSS flow is falsely trying to release map
+ * entry 0 - the offset subtraction will calculate the real
+ * map index as an out-of-range value and the release operation
+ * will be silently ignored.
+ */
+ key = *key_idx - KEY_IDX_OFFSET;
+ if (key >= RTE_DIM(rss_keys))
+ break;
+
+ if (rss_keys[key] == KEY_STAT_USED) {
+ rss_keys[key] = KEY_STAT_AVAILABLE;
+ num_used_keys--;
+ }
+ break;
+
+ case KEY_CMD_INIT:
+ for (i = 0; i < RTE_DIM(rss_keys); i++)
+ rss_keys[i] = KEY_STAT_AVAILABLE;
+
+ rss_keys_initialized = 1;
+ num_used_keys = 0;
+ break;
+
+ case KEY_CMD_DEINIT:
+ for (i = 0; i < RTE_DIM(rss_keys); i++)
+ rss_keys[i] = KEY_STAT_UNSPEC;
+
+ rss_keys_initialized = 0;
+ num_used_keys = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * Add RSS hash calculations and queue selection
+ *
+ * @param[in, out] pmd
+ * Pointer to internal structure. Used to set/get RSS map fd
+ *
+ * @param[in] rss
+ * Pointer to RSS flow actions
+ *
+ * @param[out] error
+ * Pointer to error reporting if not NULL.
+ *
+ * @return 0 on success, negative value on failure
+ */
+static int rss_add_actions(struct rte_flow *flow, struct pmd_internals *pmd,
+ const struct rte_flow_action_rss *rss,
+ struct rte_flow_error *error)
+{
+ /* 4096 is the maximum number of instructions for a BPF program */
+ unsigned int i;
+ int err;
+ struct rss_key rss_entry = { .hash_fields = 0,
+ .key_size = 0 };
+
+ /* Check supported RSS features */
+ if (rss->func != RTE_ETH_HASH_FUNCTION_DEFAULT)
+ return rte_flow_error_set
+ (error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "non-default RSS hash functions are not supported");
+ if (rss->level)
+ return rte_flow_error_set
+ (error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "a nonzero RSS encapsulation level is not supported");
+
+ /* Get a new map key for a new RSS rule */
+ err = bpf_rss_key(KEY_CMD_GET, &flow->key_idx);
+ if (err < 0) {
+ rte_flow_error_set(
+ error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+ "Failed to get BPF RSS key");
+
+ return -1;
+ }
+
+ /* Update RSS map entry with queues */
+ rss_entry.nb_queues = rss->queue_num;
+ for (i = 0; i < rss->queue_num; i++)
+ rss_entry.queues[i] = rss->queue[i];
+ rss_entry.hash_fields =
+ (1 << HASH_FIELD_IPV4_L3_L4) | (1 << HASH_FIELD_IPV6_L3_L4);
+
+ /* Add this RSS entry to map */
+ err = tap_flow_bpf_update_rss_elem(pmd->map_fd,
+ &flow->key_idx, &rss_entry);
+
+ if (err) {
+ TAP_LOG(ERR,
+ "Failed to update BPF map entry #%u (%d): %s",
+ flow->key_idx, errno, strerror(errno));
+ rte_flow_error_set(
+ error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+ "Kernel too old or not configured "
+ "to support BPF maps updates");
+
+ return -ENOTSUP;
+ }
+
+
+ /*
+ * Load bpf rules to calculate hash for this key_idx
+ */
+
+ flow->bpf_fd[SEC_L3_L4] =
+ tap_flow_bpf_calc_l3_l4_hash(flow->key_idx, pmd->map_fd);
+ if (flow->bpf_fd[SEC_L3_L4] < 0) {
+ TAP_LOG(ERR,
+ "Failed to load BPF section %s (%d): %s",
+ sec_name[SEC_L3_L4], errno, strerror(errno));
+ rte_flow_error_set(
+ error, ENOTSUP, RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+ "Kernel too old or not configured "
+ "to support BPF program loading");
+
+ return -ENOTSUP;
+ }
+
+ /* Actions */
+ {
+ struct action_data adata[] = {
+ {
+ .id = "bpf",
+ .bpf = {
+ .bpf_fd = flow->bpf_fd[SEC_L3_L4],
+ .annotation = sec_name[SEC_L3_L4],
+ .bpf = {
+ .action = TC_ACT_PIPE,
+ },
+ },
+ },
+ };
+
+ if (add_actions(flow, RTE_DIM(adata), adata,
+ TCA_FLOWER_ACT) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Manage filter operations.
+ *
+ * @param dev
+ * Pointer to Ethernet device structure.
+ * @param filter_type
+ * Filter type.
+ * @param filter_op
+ * Operation to perform.
+ * @param arg
+ * Pointer to operation-specific structure.
+ *
+ * @return
+ * 0 on success, negative errno value on failure.
+ */
+int
+tap_dev_filter_ctrl(struct rte_eth_dev *dev,
+ enum rte_filter_type filter_type,
+ enum rte_filter_op filter_op,
+ void *arg)
+{
+ switch (filter_type) {
+ case RTE_ETH_FILTER_GENERIC:
+ if (filter_op != RTE_ETH_FILTER_GET)
+ return -EINVAL;
+ *(const void **)arg = &tap_flow_ops;
+ return 0;
+ default:
+ TAP_LOG(ERR, "%p: filter type (%d) not supported",
+ dev, filter_type);
+ }
+ return -EINVAL;
+}
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_flow.h b/src/spdk/dpdk/drivers/net/tap/tap_flow.h
new file mode 100644
index 00000000..ac60a9ae
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_flow.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 6WIND S.A.
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#ifndef _TAP_FLOW_H_
+#define _TAP_FLOW_H_
+
+#include <rte_flow.h>
+#include <rte_flow_driver.h>
+#include <rte_eth_tap.h>
+#include <tap_autoconf.h>
+
+/**
+ * In TC, priority 0 means we require the kernel to allocate one for us.
+ * In rte_flow, however, we want the priority 0 to be the most important one.
+ * Use an offset to have the most important priority being 1 in TC.
+ */
+#define PRIORITY_OFFSET 1
+#define PRIORITY_MASK (0xfff)
+#define MAX_PRIORITY (PRIORITY_MASK - PRIORITY_OFFSET)
+#define GROUP_MASK (0xf)
+#define GROUP_SHIFT 12
+#define MAX_GROUP GROUP_MASK
+#define RSS_PRIORITY_OFFSET RTE_PMD_TAP_MAX_QUEUES
+
+/**
+ * These index are actually in reversed order: their priority is processed
+ * by subtracting their value to the lowest priority (PRIORITY_MASK).
+ * Thus the first one will have the lowest priority in the end
+ * (but biggest value).
+ */
+enum implicit_rule_index {
+ TAP_REMOTE_TX,
+ TAP_ISOLATE,
+ TAP_REMOTE_BROADCASTV6,
+ TAP_REMOTE_BROADCAST,
+ TAP_REMOTE_ALLMULTI,
+ TAP_REMOTE_PROMISC,
+ TAP_REMOTE_LOCAL_MAC,
+ TAP_REMOTE_MAX_IDX,
+};
+
+enum bpf_fd_idx {
+ SEC_L3_L4,
+ SEC_MAX,
+};
+
+int tap_dev_filter_ctrl(struct rte_eth_dev *dev,
+ enum rte_filter_type filter_type,
+ enum rte_filter_op filter_op,
+ void *arg);
+int tap_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error);
+
+int tap_flow_implicit_create(struct pmd_internals *pmd,
+ enum implicit_rule_index idx);
+int tap_flow_implicit_destroy(struct pmd_internals *pmd,
+ enum implicit_rule_index idx);
+int tap_flow_implicit_flush(struct pmd_internals *pmd,
+ struct rte_flow_error *error);
+
+int tap_flow_bpf_cls_q(__u32 queue_idx);
+int tap_flow_bpf_calc_l3_l4_hash(__u32 key_idx, int map_fd);
+int tap_flow_bpf_rss_map_create(unsigned int key_size, unsigned int value_size,
+ unsigned int max_entries);
+int tap_flow_bpf_update_rss_elem(int fd, void *key, void *value);
+
+#endif /* _TAP_FLOW_H_ */
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_intr.c b/src/spdk/dpdk/drivers/net/tap/tap_intr.c
new file mode 100644
index 00000000..fc590181
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_intr.c
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2018 Mellanox Technologies, Ltd
+ */
+
+/**
+ * @file
+ * Interrupts handling for tap driver.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rte_eth_tap.h>
+#include <rte_errno.h>
+#include <rte_interrupts.h>
+
+
+/**
+ * Unregister Rx interrupts free the queue interrupt vector.
+ *
+ * @param dev
+ * Pointer to the tap rte_eth_dev structure.
+ */
+static void
+tap_rx_intr_vec_uninstall(struct rte_eth_dev *dev)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ struct rte_intr_handle *intr_handle = &pmd->intr_handle;
+
+ rte_intr_free_epoll_fd(intr_handle);
+ free(intr_handle->intr_vec);
+ intr_handle->intr_vec = NULL;
+ intr_handle->nb_efd = 0;
+}
+
+/**
+ * Allocate Rx queue interrupt vector and register Rx interrupts.
+ *
+ * @param dev
+ * Pointer to the tap rte_eth_dev device structure.
+ *
+ * @return
+ * 0 on success, negative errno value otherwise and rte_errno is set.
+ */
+static int
+tap_rx_intr_vec_install(struct rte_eth_dev *dev)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+ unsigned int rxqs_n = pmd->dev->data->nb_rx_queues;
+ struct rte_intr_handle *intr_handle = &pmd->intr_handle;
+ unsigned int n = RTE_MIN(rxqs_n, (uint32_t)RTE_MAX_RXTX_INTR_VEC_ID);
+ unsigned int i;
+ unsigned int count = 0;
+
+ if (!dev->data->dev_conf.intr_conf.rxq)
+ return 0;
+ intr_handle->intr_vec = malloc(sizeof(intr_handle->intr_vec[rxqs_n]));
+ if (intr_handle->intr_vec == NULL) {
+ rte_errno = ENOMEM;
+ TAP_LOG(ERR,
+ "failed to allocate memory for interrupt vector,"
+ " Rx interrupts will not be supported");
+ return -rte_errno;
+ }
+ for (i = 0; i < n; i++) {
+ struct rx_queue *rxq = pmd->dev->data->rx_queues[i];
+
+ /* Skip queues that cannot request interrupts. */
+ if (!rxq || rxq->fd <= 0) {
+ /* Use invalid intr_vec[] index to disable entry. */
+ intr_handle->intr_vec[i] =
+ RTE_INTR_VEC_RXTX_OFFSET +
+ RTE_MAX_RXTX_INTR_VEC_ID;
+ continue;
+ }
+ intr_handle->intr_vec[i] = RTE_INTR_VEC_RXTX_OFFSET + count;
+ intr_handle->efds[count] = rxq->fd;
+ count++;
+ }
+ if (!count)
+ tap_rx_intr_vec_uninstall(dev);
+ else
+ intr_handle->nb_efd = count;
+ return 0;
+}
+
+/**
+ * Register or unregister the Rx interrupts.
+ *
+ * @param dev
+ * Pointer to the tap rte_eth_dev device structure.
+ * @param set
+ * should the operation be register or unregister the interrupts.
+ *
+ * @return
+ * 0 on success, negative errno value otherwise and rte_errno is set.
+ */
+int
+tap_rx_intr_vec_set(struct rte_eth_dev *dev, int set)
+{
+ tap_rx_intr_vec_uninstall(dev);
+ if (set)
+ return tap_rx_intr_vec_install(dev);
+ return 0;
+}
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_log.h b/src/spdk/dpdk/drivers/net/tap/tap_log.h
new file mode 100644
index 00000000..fa06843a
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_log.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 6WIND S.A.
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+extern int tap_logtype;
+
+#define TAP_LOG(level, fmt, args...) \
+ rte_log(RTE_LOG_ ## level, tap_logtype, "%s(): " fmt "\n", \
+ __func__, ## args)
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_netlink.c b/src/spdk/dpdk/drivers/net/tap/tap_netlink.c
new file mode 100644
index 00000000..6cb51009
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_netlink.c
@@ -0,0 +1,340 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 6WIND S.A.
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/netlink.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <rte_malloc.h>
+#include <tap_netlink.h>
+#include <rte_random.h>
+#include "tap_log.h"
+
+/* Must be quite large to support dumping a huge list of QDISC or filters. */
+#define BUF_SIZE (32 * 1024) /* Size of the buffer to receive kernel messages */
+#define SNDBUF_SIZE 32768 /* Send buffer size for the netlink socket */
+#define RCVBUF_SIZE 32768 /* Receive buffer size for the netlink socket */
+
+struct nested_tail {
+ struct rtattr *tail;
+ struct nested_tail *prev;
+};
+
+/**
+ * Initialize a netlink socket for communicating with the kernel.
+ *
+ * @param nl_groups
+ * Set it to a netlink group value (e.g. RTMGRP_LINK) to receive messages for
+ * specific netlink multicast groups. Otherwise, no subscription will be made.
+ *
+ * @return
+ * netlink socket file descriptor on success, -1 otherwise.
+ */
+int
+tap_nl_init(uint32_t nl_groups)
+{
+ int fd, sndbuf_size = SNDBUF_SIZE, rcvbuf_size = RCVBUF_SIZE;
+ struct sockaddr_nl local = {
+ .nl_family = AF_NETLINK,
+ .nl_groups = nl_groups,
+ };
+
+ fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
+ if (fd < 0) {
+ TAP_LOG(ERR, "Unable to create a netlink socket");
+ return -1;
+ }
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(int))) {
+ TAP_LOG(ERR, "Unable to set socket buffer send size");
+ return -1;
+ }
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(int))) {
+ TAP_LOG(ERR, "Unable to set socket buffer receive size");
+ return -1;
+ }
+ if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {
+ TAP_LOG(ERR, "Unable to bind to the netlink socket");
+ return -1;
+ }
+ return fd;
+}
+
+/**
+ * Clean up a netlink socket once all communicating with the kernel is finished.
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ *
+ * @return
+ * 0 on success, -1 otherwise.
+ */
+int
+tap_nl_final(int nlsk_fd)
+{
+ if (close(nlsk_fd)) {
+ TAP_LOG(ERR, "Failed to close netlink socket: %s (%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Send a message to the kernel on the netlink socket.
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ * @param[in] nh
+ * The netlink message send to the kernel.
+ *
+ * @return
+ * the number of sent bytes on success, -1 otherwise.
+ */
+int
+tap_nl_send(int nlsk_fd, struct nlmsghdr *nh)
+{
+ /* man 7 netlink EXAMPLE */
+ struct sockaddr_nl sa = {
+ .nl_family = AF_NETLINK,
+ };
+ struct iovec iov = {
+ .iov_base = nh,
+ .iov_len = nh->nlmsg_len,
+ };
+ struct msghdr msg = {
+ .msg_name = &sa,
+ .msg_namelen = sizeof(sa),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ int send_bytes;
+
+ nh->nlmsg_pid = 0; /* communication with the kernel uses pid 0 */
+ nh->nlmsg_seq = (uint32_t)rte_rand();
+ send_bytes = sendmsg(nlsk_fd, &msg, 0);
+ if (send_bytes < 0) {
+ TAP_LOG(ERR, "Failed to send netlink message: %s (%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+ return send_bytes;
+}
+
+/**
+ * Check that the kernel sends an appropriate ACK in response
+ * to an tap_nl_send().
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ *
+ * @return
+ * 0 on success, -1 otherwise with errno set.
+ */
+int
+tap_nl_recv_ack(int nlsk_fd)
+{
+ return tap_nl_recv(nlsk_fd, NULL, NULL);
+}
+
+/**
+ * Receive a message from the kernel on the netlink socket, following an
+ * tap_nl_send().
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ * @param[in] cb
+ * The callback function to call for each netlink message received.
+ * @param[in, out] arg
+ * Custom arguments for the callback.
+ *
+ * @return
+ * 0 on success, -1 otherwise with errno set.
+ */
+int
+tap_nl_recv(int nlsk_fd, int (*cb)(struct nlmsghdr *, void *arg), void *arg)
+{
+ /* man 7 netlink EXAMPLE */
+ struct sockaddr_nl sa;
+ char buf[BUF_SIZE];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf),
+ };
+ struct msghdr msg = {
+ .msg_name = &sa,
+ .msg_namelen = sizeof(sa),
+ .msg_iov = &iov,
+ /* One message at a time */
+ .msg_iovlen = 1,
+ };
+ int multipart = 0;
+ int ret = 0;
+
+ do {
+ struct nlmsghdr *nh;
+ int recv_bytes = 0;
+
+ recv_bytes = recvmsg(nlsk_fd, &msg, 0);
+ if (recv_bytes < 0)
+ return -1;
+ for (nh = (struct nlmsghdr *)buf;
+ NLMSG_OK(nh, (unsigned int)recv_bytes);
+ nh = NLMSG_NEXT(nh, recv_bytes)) {
+ if (nh->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err_data = NLMSG_DATA(nh);
+
+ if (err_data->error < 0) {
+ errno = -err_data->error;
+ return -1;
+ }
+ /* Ack message. */
+ return 0;
+ }
+ /* Multi-part msgs and their trailing DONE message. */
+ if (nh->nlmsg_flags & NLM_F_MULTI) {
+ if (nh->nlmsg_type == NLMSG_DONE)
+ return 0;
+ multipart = 1;
+ }
+ if (cb)
+ ret = cb(nh, arg);
+ }
+ } while (multipart);
+ return ret;
+}
+
+/**
+ * Append a netlink attribute to a message.
+ *
+ * @param[in, out] nh
+ * The netlink message to parse, received from the kernel.
+ * @param[in] type
+ * The type of attribute to append.
+ * @param[in] data_len
+ * The length of the data to append.
+ * @param[in] data
+ * The data to append.
+ */
+void
+tap_nlattr_add(struct nlmsghdr *nh, unsigned short type,
+ unsigned int data_len, const void *data)
+{
+ /* see man 3 rtnetlink */
+ struct rtattr *rta;
+
+ rta = (struct rtattr *)NLMSG_TAIL(nh);
+ rta->rta_len = RTA_LENGTH(data_len);
+ rta->rta_type = type;
+ memcpy(RTA_DATA(rta), data, data_len);
+ nh->nlmsg_len = NLMSG_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len);
+}
+
+/**
+ * Append a uint8_t netlink attribute to a message.
+ *
+ * @param[in, out] nh
+ * The netlink message to parse, received from the kernel.
+ * @param[in] type
+ * The type of attribute to append.
+ * @param[in] data
+ * The data to append.
+ */
+void
+tap_nlattr_add8(struct nlmsghdr *nh, unsigned short type, uint8_t data)
+{
+ tap_nlattr_add(nh, type, sizeof(uint8_t), &data);
+}
+
+/**
+ * Append a uint16_t netlink attribute to a message.
+ *
+ * @param[in, out] nh
+ * The netlink message to parse, received from the kernel.
+ * @param[in] type
+ * The type of attribute to append.
+ * @param[in] data
+ * The data to append.
+ */
+void
+tap_nlattr_add16(struct nlmsghdr *nh, unsigned short type, uint16_t data)
+{
+ tap_nlattr_add(nh, type, sizeof(uint16_t), &data);
+}
+
+/**
+ * Append a uint16_t netlink attribute to a message.
+ *
+ * @param[in, out] nh
+ * The netlink message to parse, received from the kernel.
+ * @param[in] type
+ * The type of attribute to append.
+ * @param[in] data
+ * The data to append.
+ */
+void
+tap_nlattr_add32(struct nlmsghdr *nh, unsigned short type, uint32_t data)
+{
+ tap_nlattr_add(nh, type, sizeof(uint32_t), &data);
+}
+
+/**
+ * Start a nested netlink attribute.
+ * It must be followed later by a call to tap_nlattr_nested_finish().
+ *
+ * @param[in, out] msg
+ * The netlink message where to edit the nested_tails metadata.
+ * @param[in] type
+ * The nested attribute type to append.
+ *
+ * @return
+ * -1 if adding a nested netlink attribute failed, 0 otherwise.
+ */
+int
+tap_nlattr_nested_start(struct nlmsg *msg, uint16_t type)
+{
+ struct nested_tail *tail;
+
+ tail = rte_zmalloc(NULL, sizeof(struct nested_tail), 0);
+ if (!tail) {
+ TAP_LOG(ERR,
+ "Couldn't allocate memory for nested netlink attribute");
+ return -1;
+ }
+
+ tail->tail = (struct rtattr *)NLMSG_TAIL(&msg->nh);
+
+ tap_nlattr_add(&msg->nh, type, 0, NULL);
+
+ tail->prev = msg->nested_tails;
+
+ msg->nested_tails = tail;
+
+ return 0;
+}
+
+/**
+ * End a nested netlink attribute.
+ * It follows a call to tap_nlattr_nested_start().
+ * In effect, it will modify the nested attribute length to include every bytes
+ * from the nested attribute start, up to here.
+ *
+ * @param[in, out] msg
+ * The netlink message where to edit the nested_tails metadata.
+ */
+void
+tap_nlattr_nested_finish(struct nlmsg *msg)
+{
+ struct nested_tail *tail = msg->nested_tails;
+
+ tail->tail->rta_len = (char *)NLMSG_TAIL(&msg->nh) - (char *)tail->tail;
+
+ if (tail->prev)
+ msg->nested_tails = tail->prev;
+
+ rte_free(tail);
+}
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_netlink.h b/src/spdk/dpdk/drivers/net/tap/tap_netlink.h
new file mode 100644
index 00000000..faa73ba1
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_netlink.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 6WIND S.A.
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#ifndef _TAP_NETLINK_H_
+#define _TAP_NETLINK_H_
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <linux/rtnetlink.h>
+#include <linux/netlink.h>
+#include <stdio.h>
+
+#include <rte_log.h>
+
+#define NLMSG_BUF 512
+
+struct nlmsg {
+ struct nlmsghdr nh;
+ struct tcmsg t;
+ char buf[NLMSG_BUF];
+ struct nested_tail *nested_tails;
+};
+
+#define NLMSG_TAIL(nlh) (void *)((char *)(nlh) + NLMSG_ALIGN((nlh)->nlmsg_len))
+
+int tap_nl_init(uint32_t nl_groups);
+int tap_nl_final(int nlsk_fd);
+int tap_nl_send(int nlsk_fd, struct nlmsghdr *nh);
+int tap_nl_recv(int nlsk_fd, int (*callback)(struct nlmsghdr *, void *),
+ void *arg);
+int tap_nl_recv_ack(int nlsk_fd);
+void tap_nlattr_add(struct nlmsghdr *nh, unsigned short type,
+ unsigned int data_len, const void *data);
+void tap_nlattr_add8(struct nlmsghdr *nh, unsigned short type, uint8_t data);
+void tap_nlattr_add16(struct nlmsghdr *nh, unsigned short type, uint16_t data);
+void tap_nlattr_add32(struct nlmsghdr *nh, unsigned short type, uint32_t data);
+int tap_nlattr_nested_start(struct nlmsg *msg, uint16_t type);
+void tap_nlattr_nested_finish(struct nlmsg *msg);
+
+#endif /* _TAP_NETLINK_H_ */
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_rss.h b/src/spdk/dpdk/drivers/net/tap/tap_rss.h
new file mode 100644
index 00000000..17606b2d
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_rss.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#ifndef _TAP_RSS_H_
+#define _TAP_RSS_H_
+
+#ifndef TAP_MAX_QUEUES
+#define TAP_MAX_QUEUES 16
+#endif
+
+/* Fixed RSS hash key size in bytes. */
+#define TAP_RSS_HASH_KEY_SIZE 40
+
+/* Supported RSS */
+#define TAP_RSS_HF_MASK (~(ETH_RSS_IP | ETH_RSS_UDP | ETH_RSS_TCP))
+
+/* hashed fields for RSS */
+enum hash_field {
+ HASH_FIELD_IPV4_L3, /* IPv4 src/dst addr */
+ HASH_FIELD_IPV4_L3_L4, /* IPv4 src/dst addr + L4 src/dst ports */
+ HASH_FIELD_IPV6_L3, /* IPv6 src/dst addr */
+ HASH_FIELD_IPV6_L3_L4, /* IPv6 src/dst addr + L4 src/dst ports */
+ HASH_FIELD_L2_SRC, /* Ethernet src addr */
+ HASH_FIELD_L2_DST, /* Ethernet dst addr */
+ HASH_FIELD_L3_SRC, /* L3 src addr */
+ HASH_FIELD_L3_DST, /* L3 dst addr */
+ HASH_FIELD_L4_SRC, /* TCP/UDP src ports */
+ HASH_FIELD_L4_DST, /* TCP/UDP dst ports */
+};
+
+struct rss_key {
+ __u8 key[128];
+ __u32 hash_fields;
+ __u32 key_size;
+ __u32 queues[TAP_MAX_QUEUES];
+ __u32 nb_queues;
+} __attribute__((packed));
+
+#endif /* _TAP_RSS_H_ */
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_tcmsgs.c b/src/spdk/dpdk/drivers/net/tap/tap_tcmsgs.c
new file mode 100644
index 00000000..3c9d0366
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_tcmsgs.c
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 6WIND S.A.
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#include <inttypes.h>
+#include <linux/netlink.h>
+#include <net/if.h>
+#include <string.h>
+
+#include <rte_log.h>
+#include <tap_tcmsgs.h>
+#include "tap_log.h"
+
+struct qdisc {
+ uint32_t handle;
+ uint32_t parent;
+};
+
+struct list_args {
+ int nlsk_fd;
+ uint16_t ifindex;
+ void *custom_arg;
+};
+
+struct qdisc_custom_arg {
+ uint32_t handle;
+ uint32_t parent;
+ uint8_t exists;
+};
+
+/**
+ * Initialize a netlink message with a TC header.
+ *
+ * @param[in, out] msg
+ * The netlink message to fill.
+ * @param[in] ifindex
+ * The netdevice ifindex where the rule will be applied.
+ * @param[in] type
+ * The type of TC message to create (RTM_NEWTFILTER, RTM_NEWQDISC, etc.).
+ * @param[in] flags
+ * Overrides the default netlink flags for this msg with those specified.
+ */
+void
+tc_init_msg(struct nlmsg *msg, uint16_t ifindex, uint16_t type, uint16_t flags)
+{
+ struct nlmsghdr *n = &msg->nh;
+
+ n->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+ n->nlmsg_type = type;
+ if (flags)
+ n->nlmsg_flags = flags;
+ else
+ n->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ msg->t.tcm_family = AF_UNSPEC;
+ msg->t.tcm_ifindex = ifindex;
+}
+
+/**
+ * Delete a specific QDISC identified by its iface, and it's handle and parent.
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ * @param[in] ifindex
+ * The netdevice ifindex on whom the deletion will happen.
+ * @param[in] qinfo
+ * Additional info to identify the QDISC (handle and parent).
+ *
+ * @return
+ * 0 on success, -1 otherwise with errno set.
+ */
+static int
+qdisc_del(int nlsk_fd, uint16_t ifindex, struct qdisc *qinfo)
+{
+ struct nlmsg msg;
+ int fd = 0;
+
+ tc_init_msg(&msg, ifindex, RTM_DELQDISC, 0);
+ msg.t.tcm_handle = qinfo->handle;
+ msg.t.tcm_parent = qinfo->parent;
+ /* if no netlink socket is provided, create one */
+ if (!nlsk_fd) {
+ fd = tap_nl_init(0);
+ if (fd < 0) {
+ TAP_LOG(ERR,
+ "Could not delete QDISC: null netlink socket");
+ return -1;
+ }
+ } else {
+ fd = nlsk_fd;
+ }
+ if (tap_nl_send(fd, &msg.nh) < 0)
+ goto error;
+ if (tap_nl_recv_ack(fd) < 0)
+ goto error;
+ if (!nlsk_fd)
+ return tap_nl_final(fd);
+ return 0;
+error:
+ if (!nlsk_fd)
+ tap_nl_final(fd);
+ return -1;
+}
+
+/**
+ * Add the multiqueue QDISC with MULTIQ_MAJOR_HANDLE handle.
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ * @param[in] ifindex
+ * The netdevice ifindex where to add the multiqueue QDISC.
+ *
+ * @return
+ * 0 on success, -1 otherwise with errno set.
+ */
+int
+qdisc_add_multiq(int nlsk_fd, uint16_t ifindex)
+{
+ struct tc_multiq_qopt opt;
+ struct nlmsg msg;
+
+ tc_init_msg(&msg, ifindex, RTM_NEWQDISC,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+ msg.t.tcm_handle = TC_H_MAKE(MULTIQ_MAJOR_HANDLE, 0);
+ msg.t.tcm_parent = TC_H_ROOT;
+ tap_nlattr_add(&msg.nh, TCA_KIND, sizeof("multiq"), "multiq");
+ tap_nlattr_add(&msg.nh, TCA_OPTIONS, sizeof(opt), &opt);
+ if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
+ return -1;
+ if (tap_nl_recv_ack(nlsk_fd) < 0)
+ return -1;
+ return 0;
+}
+
+/**
+ * Add the ingress QDISC with default ffff: handle.
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ * @param[in] ifindex
+ * The netdevice ifindex where the QDISC will be added.
+ *
+ * @return
+ * 0 on success, -1 otherwise with errno set.
+ */
+int
+qdisc_add_ingress(int nlsk_fd, uint16_t ifindex)
+{
+ struct nlmsg msg;
+
+ tc_init_msg(&msg, ifindex, RTM_NEWQDISC,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
+ msg.t.tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
+ msg.t.tcm_parent = TC_H_INGRESS;
+ tap_nlattr_add(&msg.nh, TCA_KIND, sizeof("ingress"), "ingress");
+ if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
+ return -1;
+ if (tap_nl_recv_ack(nlsk_fd) < 0)
+ return -1;
+ return 0;
+}
+
+/**
+ * Callback function to delete a QDISC.
+ *
+ * @param[in] nh
+ * The netlink message to parse, received from the kernel.
+ * @param[in] arg
+ * Custom arguments for the callback.
+ *
+ * @return
+ * 0 on success, -1 otherwise with errno set.
+ */
+static int
+qdisc_del_cb(struct nlmsghdr *nh, void *arg)
+{
+ struct tcmsg *t = NLMSG_DATA(nh);
+ struct list_args *args = arg;
+
+ struct qdisc qinfo = {
+ .handle = t->tcm_handle,
+ .parent = t->tcm_parent,
+ };
+
+ /* filter out other ifaces' qdiscs */
+ if (args->ifindex != (unsigned int)t->tcm_ifindex)
+ return 0;
+ /*
+ * Use another nlsk_fd (0) to avoid tampering with the current list
+ * iteration.
+ */
+ return qdisc_del(0, args->ifindex, &qinfo);
+}
+
+/**
+ * Iterate over all QDISC, and call the callback() function for each.
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ * @param[in] ifindex
+ * The netdevice ifindex where to find QDISCs.
+ * @param[in] callback
+ * The function to call for each QDISC.
+ * @param[in, out] arg
+ * The arguments to provide the callback function with.
+ *
+ * @return
+ * 0 on success, -1 otherwise with errno set.
+ */
+static int
+qdisc_iterate(int nlsk_fd, uint16_t ifindex,
+ int (*callback)(struct nlmsghdr *, void *), void *arg)
+{
+ struct nlmsg msg;
+ struct list_args args = {
+ .nlsk_fd = nlsk_fd,
+ .ifindex = ifindex,
+ .custom_arg = arg,
+ };
+
+ tc_init_msg(&msg, ifindex, RTM_GETQDISC, NLM_F_REQUEST | NLM_F_DUMP);
+ if (tap_nl_send(nlsk_fd, &msg.nh) < 0)
+ return -1;
+ if (tap_nl_recv(nlsk_fd, callback, &args) < 0)
+ return -1;
+ return 0;
+}
+
+/**
+ * Delete all QDISCs for a given netdevice.
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ * @param[in] ifindex
+ * The netdevice ifindex where to find QDISCs.
+ *
+ * @return
+ * 0 on success, -1 otherwise with errno set.
+ */
+int
+qdisc_flush(int nlsk_fd, uint16_t ifindex)
+{
+ return qdisc_iterate(nlsk_fd, ifindex, qdisc_del_cb, NULL);
+}
+
+/**
+ * Create the multiqueue QDISC, only if it does not exist already.
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ * @param[in] ifindex
+ * The netdevice ifindex where to add the multiqueue QDISC.
+ *
+ * @return
+ * 0 if the qdisc exists or if has been successfully added.
+ * Return -1 otherwise.
+ */
+int
+qdisc_create_multiq(int nlsk_fd, uint16_t ifindex)
+{
+ int err = 0;
+
+ err = qdisc_add_multiq(nlsk_fd, ifindex);
+ if (err < 0 && errno != -EEXIST) {
+ TAP_LOG(ERR, "Could not add multiq qdisc (%d): %s",
+ errno, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Create the ingress QDISC, only if it does not exist already.
+ *
+ * @param[in] nlsk_fd
+ * The netlink socket file descriptor used for communication.
+ * @param[in] ifindex
+ * The netdevice ifindex where to add the ingress QDISC.
+ *
+ * @return
+ * 0 if the qdisc exists or if has been successfully added.
+ * Return -1 otherwise.
+ */
+int
+qdisc_create_ingress(int nlsk_fd, uint16_t ifindex)
+{
+ int err = 0;
+
+ err = qdisc_add_ingress(nlsk_fd, ifindex);
+ if (err < 0 && errno != -EEXIST) {
+ TAP_LOG(ERR, "Could not add ingress qdisc (%d): %s",
+ errno, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
diff --git a/src/spdk/dpdk/drivers/net/tap/tap_tcmsgs.h b/src/spdk/dpdk/drivers/net/tap/tap_tcmsgs.h
new file mode 100644
index 00000000..8cedea84
--- /dev/null
+++ b/src/spdk/dpdk/drivers/net/tap/tap_tcmsgs.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2017 6WIND S.A.
+ * Copyright 2017 Mellanox Technologies, Ltd
+ */
+
+#ifndef _TAP_TCMSGS_H_
+#define _TAP_TCMSGS_H_
+
+#include <tap_autoconf.h>
+#include <linux/if_ether.h>
+#include <linux/rtnetlink.h>
+#include <linux/pkt_sched.h>
+#include <linux/pkt_cls.h>
+#include <linux/tc_act/tc_mirred.h>
+#include <linux/tc_act/tc_gact.h>
+#include <linux/tc_act/tc_skbedit.h>
+#ifdef HAVE_TC_ACT_BPF
+#include <linux/tc_act/tc_bpf.h>
+#endif
+#include <inttypes.h>
+
+#include <rte_ether.h>
+#include <tap_netlink.h>
+
+#define MULTIQ_MAJOR_HANDLE (1 << 16)
+
+void tc_init_msg(struct nlmsg *msg, uint16_t ifindex, uint16_t type,
+ uint16_t flags);
+int qdisc_list(int nlsk_fd, uint16_t ifindex);
+int qdisc_flush(int nlsk_fd, uint16_t ifindex);
+int qdisc_create_ingress(int nlsk_fd, uint16_t ifindex);
+int qdisc_create_multiq(int nlsk_fd, uint16_t ifindex);
+int qdisc_add_ingress(int nlsk_fd, uint16_t ifindex);
+int qdisc_add_multiq(int nlsk_fd, uint16_t ifindex);
+int filter_list_ingress(int nlsk_fd, uint16_t ifindex);
+
+#endif /* _TAP_TCMSGS_H_ */