selftests: xsk: Put the same buffer only once in the fill ring
[linux-2.6-microblaze.git] / tools / testing / selftests / bpf / xdpxceiver.c
index f53ce26..c5c68b8 100644 (file)
@@ -19,7 +19,7 @@
  * Virtual Ethernet interfaces.
  *
  * For each mode, the following tests are run:
- *    a. nopoll - soft-irq processing
+ *    a. nopoll - soft-irq processing in run-to-completion mode
  *    b. poll - using poll() syscall
  *    c. Socket Teardown
  *       Create a Tx and a Rx socket, Tx from one socket, Rx on another. Destroy
  *       Configure sockets at indexes 0 and 1, run a traffic on queue ids 0,
  *       then remove xsk sockets from queue 0 on both veth interfaces and
  *       finally run a traffic on queues ids 1
+ *    g. unaligned mode
+ *    h. tests for invalid and corner case Tx descriptors so that the correct ones
+ *       are discarded and let through, respectively.
+ *    i. 2K frame size tests
  *
  * Total tests: 12
  *
@@ -112,13 +116,10 @@ static void __exit_with_error(int error, const char *file, const char *func, int
 
 #define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__)
 
-#define print_ksft_result(void)\
-       (ksft_test_result_pass("PASS: %s %s %s%s%s%s\n", configured_mode ? "DRV" : "SKB",\
-                              test_type == TEST_TYPE_POLL ? "POLL" : "NOPOLL",\
-                              test_type == TEST_TYPE_TEARDOWN ? "Socket Teardown" : "",\
-                              test_type == TEST_TYPE_BIDI ? "Bi-directional Sockets" : "",\
-                              test_type == TEST_TYPE_STATS ? "Stats" : "",\
-                              test_type == TEST_TYPE_BPF_RES ? "BPF RES" : ""))
+#define mode_string(test) (test)->ifobj_tx->xdp_flags & XDP_FLAGS_SKB_MODE ? "SKB" : "DRV"
+
+#define print_ksft_result(test)                                                \
+       (ksft_test_result_pass("PASS: %s %s\n", mode_string(test), (test)->name))
 
 static void memset32_htonl(void *dest, u32 val, u32 size)
 {
@@ -235,80 +236,46 @@ static void gen_udp_csum(struct udphdr *udp_hdr, struct iphdr *ip_hdr)
            udp_csum(ip_hdr->saddr, ip_hdr->daddr, UDP_PKT_SIZE, IPPROTO_UDP, (u16 *)udp_hdr);
 }
 
-static void xsk_configure_umem(struct ifobject *data, void *buffer, u64 size, int idx)
+static int xsk_configure_umem(struct xsk_umem_info *umem, void *buffer, u64 size)
 {
        struct xsk_umem_config cfg = {
                .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
                .comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
-               .frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE,
-               .frame_headroom = frame_headroom,
+               .frame_size = umem->frame_size,
+               .frame_headroom = umem->frame_headroom,
                .flags = XSK_UMEM__DEFAULT_FLAGS
        };
-       struct xsk_umem_info *umem;
        int ret;
 
-       umem = calloc(1, sizeof(struct xsk_umem_info));
-       if (!umem)
-               exit_with_error(errno);
+       if (umem->unaligned_mode)
+               cfg.flags |= XDP_UMEM_UNALIGNED_CHUNK_FLAG;
 
        ret = xsk_umem__create(&umem->umem, buffer, size,
                               &umem->fq, &umem->cq, &cfg);
        if (ret)
-               exit_with_error(-ret);
+               return ret;
 
        umem->buffer = buffer;
-
-       data->umem_arr[idx] = umem;
-}
-
-static void xsk_populate_fill_ring(struct xsk_umem_info *umem)
-{
-       int ret, i;
-       u32 idx = 0;
-
-       ret = xsk_ring_prod__reserve(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS, &idx);
-       if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS)
-               exit_with_error(-ret);
-       for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++)
-               *xsk_ring_prod__fill_addr(&umem->fq, idx++) = i * XSK_UMEM__DEFAULT_FRAME_SIZE;
-       xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS);
+       return 0;
 }
 
-static int xsk_configure_socket(struct ifobject *ifobject, int idx)
+static int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem,
+                               struct ifobject *ifobject, u32 qid)
 {
        struct xsk_socket_config cfg;
-       struct xsk_socket_info *xsk;
        struct xsk_ring_cons *rxr;
        struct xsk_ring_prod *txr;
-       int ret;
 
-       xsk = calloc(1, sizeof(struct xsk_socket_info));
-       if (!xsk)
-               exit_with_error(errno);
-
-       xsk->umem = ifobject->umem;
-       cfg.rx_size = rxqsize;
+       xsk->umem = umem;
+       cfg.rx_size = xsk->rxqsize;
        cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
        cfg.libbpf_flags = 0;
-       cfg.xdp_flags = xdp_flags;
-       cfg.bind_flags = xdp_bind_flags;
-
-       if (test_type != TEST_TYPE_BIDI) {
-               rxr = (ifobject->fv.vector == rx) ? &xsk->rx : NULL;
-               txr = (ifobject->fv.vector == tx) ? &xsk->tx : NULL;
-       } else {
-               rxr = &xsk->rx;
-               txr = &xsk->tx;
-       }
-
-       ret = xsk_socket__create(&xsk->xsk, ifobject->ifname, idx,
-                                ifobject->umem->umem, rxr, txr, &cfg);
-       if (ret)
-               return 1;
+       cfg.xdp_flags = ifobject->xdp_flags;
+       cfg.bind_flags = ifobject->bind_flags;
 
-       ifobject->xsk_arr[idx] = xsk;
-
-       return 0;
+       txr = ifobject->tx_on ? &xsk->tx : NULL;
+       rxr = ifobject->rx_on ? &xsk->rx : NULL;
+       return xsk_socket__create(&xsk->xsk, ifobject->ifname, qid, umem->umem, rxr, txr, &cfg);
 }
 
 static struct option long_options[] = {
@@ -354,45 +321,44 @@ static int switch_namespace(const char *nsname)
        return nsfd;
 }
 
-static int validate_interfaces(void)
+static bool validate_interface(struct ifobject *ifobj)
 {
-       bool ret = true;
-
-       for (int i = 0; i < MAX_INTERFACES; i++) {
-               if (!strcmp(ifdict[i]->ifname, "")) {
-                       ret = false;
-                       ksft_test_result_fail("ERROR: interfaces: -i <int>,<ns> -i <int>,<ns>.");
-               }
-       }
-       return ret;
+       if (!strcmp(ifobj->ifname, ""))
+               return false;
+       return true;
 }
 
-static void parse_command_line(int argc, char **argv)
+static void parse_command_line(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx, int argc,
+                              char **argv)
 {
-       int option_index, interface_index = 0, c;
+       struct ifobject *ifobj;
+       u32 interface_nb = 0;
+       int option_index, c;
 
        opterr = 0;
 
        for (;;) {
-               c = getopt_long(argc, argv, "i:Dv", long_options, &option_index);
+               char *sptr, *token;
 
+               c = getopt_long(argc, argv, "i:Dv", long_options, &option_index);
                if (c == -1)
                        break;
 
                switch (c) {
                case 'i':
-                       if (interface_index == MAX_INTERFACES)
+                       if (interface_nb == 0)
+                               ifobj = ifobj_tx;
+                       else if (interface_nb == 1)
+                               ifobj = ifobj_rx;
+                       else
                                break;
-                       char *sptr, *token;
 
                        sptr = strndupa(optarg, strlen(optarg));
-                       memcpy(ifdict[interface_index]->ifname,
-                              strsep(&sptr, ","), MAX_INTERFACE_NAME_CHARS);
+                       memcpy(ifobj->ifname, strsep(&sptr, ","), MAX_INTERFACE_NAME_CHARS);
                        token = strsep(&sptr, ",");
                        if (token)
-                               memcpy(ifdict[interface_index]->nsname, token,
-                                      MAX_INTERFACES_NAMESPACE_CHARS);
-                       interface_index++;
+                               memcpy(ifobj->nsname, token, MAX_INTERFACES_NAMESPACE_CHARS);
+                       interface_nb++;
                        break;
                case 'D':
                        opt_pkt_dump = true;
@@ -405,11 +371,84 @@ static void parse_command_line(int argc, char **argv)
                        ksft_exit_xfail();
                }
        }
+}
 
-       if (!validate_interfaces()) {
-               usage(basename(argv[0]));
-               ksft_exit_xfail();
+static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
+                            struct ifobject *ifobj_rx)
+{
+       u32 i, j;
+
+       for (i = 0; i < MAX_INTERFACES; i++) {
+               struct ifobject *ifobj = i ? ifobj_rx : ifobj_tx;
+
+               ifobj->umem = &ifobj->umem_arr[0];
+               ifobj->xsk = &ifobj->xsk_arr[0];
+               ifobj->use_poll = false;
+               ifobj->pkt_stream = test->pkt_stream_default;
+
+               if (i == 0) {
+                       ifobj->rx_on = false;
+                       ifobj->tx_on = true;
+               } else {
+                       ifobj->rx_on = true;
+                       ifobj->tx_on = false;
+               }
+
+               for (j = 0; j < MAX_SOCKETS; j++) {
+                       memset(&ifobj->umem_arr[j], 0, sizeof(ifobj->umem_arr[j]));
+                       memset(&ifobj->xsk_arr[j], 0, sizeof(ifobj->xsk_arr[j]));
+                       ifobj->umem_arr[j].num_frames = DEFAULT_UMEM_BUFFERS;
+                       ifobj->umem_arr[j].frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
+                       ifobj->xsk_arr[j].rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
+               }
        }
+
+       test->ifobj_tx = ifobj_tx;
+       test->ifobj_rx = ifobj_rx;
+       test->current_step = 0;
+       test->total_steps = 1;
+       test->nb_sockets = 1;
+}
+
+static void test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
+                          struct ifobject *ifobj_rx, enum test_mode mode)
+{
+       struct pkt_stream *pkt_stream;
+       u32 i;
+
+       pkt_stream = test->pkt_stream_default;
+       memset(test, 0, sizeof(*test));
+       test->pkt_stream_default = pkt_stream;
+
+       for (i = 0; i < MAX_INTERFACES; i++) {
+               struct ifobject *ifobj = i ? ifobj_rx : ifobj_tx;
+
+               ifobj->xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+               if (mode == TEST_MODE_SKB)
+                       ifobj->xdp_flags |= XDP_FLAGS_SKB_MODE;
+               else
+                       ifobj->xdp_flags |= XDP_FLAGS_DRV_MODE;
+
+               ifobj->bind_flags = XDP_USE_NEED_WAKEUP | XDP_COPY;
+       }
+
+       __test_spec_init(test, ifobj_tx, ifobj_rx);
+}
+
+static void test_spec_reset(struct test_spec *test)
+{
+       __test_spec_init(test, test->ifobj_tx, test->ifobj_rx);
+}
+
+static void test_spec_set_name(struct test_spec *test, const char *name)
+{
+       strncpy(test->name, name, MAX_TEST_NAME_SIZE);
+}
+
+static void pkt_stream_reset(struct pkt_stream *pkt_stream)
+{
+       if (pkt_stream)
+               pkt_stream->rx_pkt_nb = 0;
 }
 
 static struct pkt *pkt_stream_get_pkt(struct pkt_stream *pkt_stream, u32 pkt_nb)
@@ -420,29 +459,105 @@ static struct pkt *pkt_stream_get_pkt(struct pkt_stream *pkt_stream, u32 pkt_nb)
        return &pkt_stream->pkts[pkt_nb];
 }
 
-static struct pkt_stream *pkt_stream_generate(u32 nb_pkts, u32 pkt_len)
+static struct pkt *pkt_stream_get_next_rx_pkt(struct pkt_stream *pkt_stream)
+{
+       while (pkt_stream->rx_pkt_nb < pkt_stream->nb_pkts) {
+               if (pkt_stream->pkts[pkt_stream->rx_pkt_nb].valid)
+                       return &pkt_stream->pkts[pkt_stream->rx_pkt_nb++];
+               pkt_stream->rx_pkt_nb++;
+       }
+       return NULL;
+}
+
+static void pkt_stream_delete(struct pkt_stream *pkt_stream)
+{
+       free(pkt_stream->pkts);
+       free(pkt_stream);
+}
+
+static void pkt_stream_restore_default(struct test_spec *test)
+{
+       if (test->ifobj_tx->pkt_stream != test->pkt_stream_default) {
+               pkt_stream_delete(test->ifobj_tx->pkt_stream);
+               test->ifobj_tx->pkt_stream = test->pkt_stream_default;
+       }
+       test->ifobj_rx->pkt_stream = test->pkt_stream_default;
+}
+
+static struct pkt_stream *__pkt_stream_alloc(u32 nb_pkts)
 {
        struct pkt_stream *pkt_stream;
-       u32 i;
 
-       pkt_stream = malloc(sizeof(*pkt_stream));
+       pkt_stream = calloc(1, sizeof(*pkt_stream));
        if (!pkt_stream)
-               exit_with_error(ENOMEM);
+               return NULL;
 
        pkt_stream->pkts = calloc(nb_pkts, sizeof(*pkt_stream->pkts));
-       if (!pkt_stream->pkts)
+       if (!pkt_stream->pkts) {
+               free(pkt_stream);
+               return NULL;
+       }
+
+       pkt_stream->nb_pkts = nb_pkts;
+       return pkt_stream;
+}
+
+static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb_pkts, u32 pkt_len)
+{
+       struct pkt_stream *pkt_stream;
+       u32 i;
+
+       pkt_stream = __pkt_stream_alloc(nb_pkts);
+       if (!pkt_stream)
                exit_with_error(ENOMEM);
 
        pkt_stream->nb_pkts = nb_pkts;
        for (i = 0; i < nb_pkts; i++) {
-               pkt_stream->pkts[i].addr = (i % num_frames) * XSK_UMEM__DEFAULT_FRAME_SIZE;
+               pkt_stream->pkts[i].addr = (i % umem->num_frames) * umem->frame_size +
+                       DEFAULT_OFFSET;
                pkt_stream->pkts[i].len = pkt_len;
                pkt_stream->pkts[i].payload = i;
+
+               if (pkt_len > umem->frame_size)
+                       pkt_stream->pkts[i].valid = false;
+               else
+                       pkt_stream->pkts[i].valid = true;
        }
 
        return pkt_stream;
 }
 
+static struct pkt_stream *pkt_stream_clone(struct xsk_umem_info *umem,
+                                          struct pkt_stream *pkt_stream)
+{
+       return pkt_stream_generate(umem, pkt_stream->nb_pkts, pkt_stream->pkts[0].len);
+}
+
+static void pkt_stream_replace(struct test_spec *test, u32 nb_pkts, u32 pkt_len)
+{
+       struct pkt_stream *pkt_stream;
+
+       pkt_stream = pkt_stream_generate(test->ifobj_tx->umem, nb_pkts, pkt_len);
+       test->ifobj_tx->pkt_stream = pkt_stream;
+       test->ifobj_rx->pkt_stream = pkt_stream;
+}
+
+static void pkt_stream_replace_half(struct test_spec *test, u32 pkt_len, u32 offset)
+{
+       struct xsk_umem_info *umem = test->ifobj_tx->umem;
+       struct pkt_stream *pkt_stream;
+       u32 i;
+
+       pkt_stream = pkt_stream_clone(umem, test->pkt_stream_default);
+       for (i = 0; i < test->pkt_stream_default->nb_pkts; i += 2) {
+               pkt_stream->pkts[i].addr = (i % umem->num_frames) * umem->frame_size + offset;
+               pkt_stream->pkts[i].len = pkt_len;
+       }
+
+       test->ifobj_tx->pkt_stream = pkt_stream;
+       test->ifobj_rx->pkt_stream = pkt_stream;
+}
+
 static struct pkt *pkt_generate(struct ifobject *ifobject, u32 pkt_nb)
 {
        struct pkt *pkt = pkt_stream_get_pkt(ifobject->pkt_stream, pkt_nb);
@@ -453,6 +568,8 @@ static struct pkt *pkt_generate(struct ifobject *ifobject, u32 pkt_nb)
 
        if (!pkt)
                return NULL;
+       if (!pkt->valid || pkt->len < PKT_SIZE)
+               return pkt;
 
        data = xsk_umem__get_data(ifobject->umem->buffer, pkt->addr);
        udp_hdr = (struct udphdr *)(data + sizeof(struct ethhdr) + sizeof(struct iphdr));
@@ -467,6 +584,26 @@ static struct pkt *pkt_generate(struct ifobject *ifobject, u32 pkt_nb)
        return pkt;
 }
 
+static void pkt_stream_generate_custom(struct test_spec *test, struct pkt *pkts, u32 nb_pkts)
+{
+       struct pkt_stream *pkt_stream;
+       u32 i;
+
+       pkt_stream = __pkt_stream_alloc(nb_pkts);
+       if (!pkt_stream)
+               exit_with_error(ENOMEM);
+
+       test->ifobj_tx->pkt_stream = pkt_stream;
+       test->ifobj_rx->pkt_stream = pkt_stream;
+
+       for (i = 0; i < nb_pkts; i++) {
+               pkt_stream->pkts[i].addr = pkts[i].addr;
+               pkt_stream->pkts[i].len = pkts[i].len;
+               pkt_stream->pkts[i].payload = i;
+               pkt_stream->pkts[i].valid = pkts[i].valid;
+       }
+}
+
 static void pkt_dump(void *pkt, u32 len)
 {
        char s[INET_ADDRSTRLEN];
@@ -504,9 +641,9 @@ static void pkt_dump(void *pkt, u32 len)
        fprintf(stdout, "---------------------------------------\n");
 }
 
-static bool is_pkt_valid(struct pkt *pkt, void *buffer, const struct xdp_desc *desc)
+static bool is_pkt_valid(struct pkt *pkt, void *buffer, u64 addr, u32 len)
 {
-       void *data = xsk_umem__get_data(buffer, desc->addr);
+       void *data = xsk_umem__get_data(buffer, addr);
        struct iphdr *iphdr = (struct iphdr *)(data + sizeof(struct ethhdr));
 
        if (!pkt) {
@@ -514,19 +651,24 @@ static bool is_pkt_valid(struct pkt *pkt, void *buffer, const struct xdp_desc *d
                return false;
        }
 
+       if (len < PKT_SIZE) {
+               /*Do not try to verify packets that are smaller than minimum size. */
+               return true;
+       }
+
+       if (pkt->len != len) {
+               ksft_test_result_fail
+                       ("ERROR: [%s] expected length [%d], got length [%d]\n",
+                        __func__, pkt->len, len);
+               return false;
+       }
+
        if (iphdr->version == IP_PKT_VER && iphdr->tos == IP_PKT_TOS) {
                u32 seqnum = ntohl(*((u32 *)(data + PKT_HDR_SIZE)));
 
-               if (opt_pkt_dump && test_type != TEST_TYPE_STATS)
+               if (opt_pkt_dump)
                        pkt_dump(data, PKT_SIZE);
 
-               if (pkt->len != desc->len) {
-                       ksft_test_result_fail
-                               ("ERROR: [%s] expected length [%d], got length [%d]\n",
-                                       __func__, pkt->len, desc->len);
-                       return false;
-               }
-
                if (pkt->payload != seqnum) {
                        ksft_test_result_fail
                                ("ERROR: [%s] expected seqnum [%d], got seqnum [%d]\n",
@@ -558,14 +700,20 @@ static void complete_pkts(struct xsk_socket_info *xsk, int batch_size)
        unsigned int rcvd;
        u32 idx;
 
-       if (!xsk->outstanding_tx)
-               return;
-
        if (xsk_ring_prod__needs_wakeup(&xsk->tx))
                kick_tx(xsk);
 
        rcvd = xsk_ring_cons__peek(&xsk->umem->cq, batch_size, &idx);
        if (rcvd) {
+               if (rcvd > xsk->outstanding_tx) {
+                       u64 addr = *xsk_ring_cons__comp_addr(&xsk->umem->cq, idx + rcvd - 1);
+
+                       ksft_test_result_fail("ERROR: [%s] Too many packets completed\n",
+                                             __func__);
+                       ksft_print_msg("Last completion address: %llx\n", addr);
+                       return;
+               }
+
                xsk_ring_cons__release(&xsk->umem->cq, rcvd);
                xsk->outstanding_tx -= rcvd;
        }
@@ -574,11 +722,10 @@ static void complete_pkts(struct xsk_socket_info *xsk, int batch_size)
 static void receive_pkts(struct pkt_stream *pkt_stream, struct xsk_socket_info *xsk,
                         struct pollfd *fds)
 {
-       u32 idx_rx = 0, idx_fq = 0, rcvd, i, pkt_count = 0;
-       struct pkt *pkt;
+       struct pkt *pkt = pkt_stream_get_next_rx_pkt(pkt_stream);
+       u32 idx_rx = 0, idx_fq = 0, rcvd, i;
        int ret;
 
-       pkt = pkt_stream_get_pkt(pkt_stream, pkt_count++);
        while (pkt) {
                rcvd = xsk_ring_cons__peek(&xsk->rx, BATCH_SIZE, &idx_rx);
                if (!rcvd) {
@@ -606,13 +753,21 @@ static void receive_pkts(struct pkt_stream *pkt_stream, struct xsk_socket_info *
                        const struct xdp_desc *desc = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++);
                        u64 addr = desc->addr, orig;
 
+                       if (!pkt) {
+                               ksft_test_result_fail("ERROR: [%s] Received too many packets.\n",
+                                                     __func__);
+                               ksft_print_msg("Last packet has addr: %llx len: %u\n",
+                                              addr, desc->len);
+                               return;
+                       }
+
                        orig = xsk_umem__extract_addr(addr);
                        addr = xsk_umem__add_offset_to_addr(addr);
-                       if (!is_pkt_valid(pkt, xsk->umem->buffer, desc))
+                       if (!is_pkt_valid(pkt, xsk->umem->buffer, addr, desc->len))
                                return;
 
                        *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = orig;
-                       pkt = pkt_stream_get_pkt(pkt_stream, pkt_count++);
+                       pkt = pkt_stream_get_next_rx_pkt(pkt_stream);
                }
 
                xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
@@ -623,7 +778,7 @@ static void receive_pkts(struct pkt_stream *pkt_stream, struct xsk_socket_info *
 static u32 __send_pkts(struct ifobject *ifobject, u32 pkt_nb)
 {
        struct xsk_socket_info *xsk = ifobject->xsk;
-       u32 i, idx;
+       u32 i, idx, valid_pkts = 0;
 
        while (xsk_ring_prod__reserve(&xsk->tx, BATCH_SIZE, &idx) < BATCH_SIZE)
                complete_pkts(xsk, BATCH_SIZE);
@@ -638,14 +793,13 @@ static u32 __send_pkts(struct ifobject *ifobject, u32 pkt_nb)
                tx_desc->addr = pkt->addr;
                tx_desc->len = pkt->len;
                pkt_nb++;
+               if (pkt->valid)
+                       valid_pkts++;
        }
 
        xsk_ring_prod__submit(&xsk->tx, i);
-       if (stat_test_type != STAT_TEST_TX_INVALID)
-               xsk->outstanding_tx += i;
-       else if (xsk_ring_prod__needs_wakeup(&xsk->tx))
-               kick_tx(xsk);
-       complete_pkts(xsk, i);
+       xsk->outstanding_tx += valid_pkts;
+       complete_pkts(xsk, BATCH_SIZE);
 
        return i;
 }
@@ -658,23 +812,23 @@ static void wait_for_tx_completion(struct xsk_socket_info *xsk)
 
 static void send_pkts(struct ifobject *ifobject)
 {
-       struct pollfd fds[MAX_SOCKS] = { };
+       struct pollfd fds = { };
        u32 pkt_cnt = 0;
 
-       fds[0].fd = xsk_socket__fd(ifobject->xsk->xsk);
-       fds[0].events = POLLOUT;
+       fds.fd = xsk_socket__fd(ifobject->xsk->xsk);
+       fds.events = POLLOUT;
 
        while (pkt_cnt < ifobject->pkt_stream->nb_pkts) {
                u32 sent;
 
-               if (test_type == TEST_TYPE_POLL) {
+               if (ifobject->use_poll) {
                        int ret;
 
-                       ret = poll(fds, 1, POLL_TMOUT);
+                       ret = poll(&fds, 1, POLL_TMOUT);
                        if (ret <= 0)
                                continue;
 
-                       if (!(fds[0].revents & POLLOUT))
+                       if (!(fds.revents & POLLOUT))
                                continue;
                }
 
@@ -698,7 +852,7 @@ static bool rx_stats_are_valid(struct ifobject *ifobject)
        optlen = sizeof(stats);
        err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &optlen);
        if (err) {
-               ksft_test_result_fail("ERROR: [%s] getsockopt(XDP_STATISTICS) error %u %s\n",
+               ksft_test_result_fail("ERROR Rx: [%s] getsockopt(XDP_STATISTICS) error %u %s\n",
                                      __func__, -err, strerror(-err));
                return true;
        }
@@ -739,7 +893,7 @@ static void tx_stats_validate(struct ifobject *ifobject)
        optlen = sizeof(stats);
        err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &optlen);
        if (err) {
-               ksft_test_result_fail("ERROR: [%s] getsockopt(XDP_STATISTICS) error %u %s\n",
+               ksft_test_result_fail("ERROR Tx: [%s] getsockopt(XDP_STATISTICS) error %u %s\n",
                                      __func__, -err, strerror(-err));
                return;
        }
@@ -751,71 +905,63 @@ static void tx_stats_validate(struct ifobject *ifobject)
                              __func__, stats.tx_invalid_descs, ifobject->pkt_stream->nb_pkts);
 }
 
-static void thread_common_ops(struct ifobject *ifobject, void *bufs)
+static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject)
 {
-       u64 umem_sz = num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE;
        int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
-       size_t mmap_sz = umem_sz;
-       int ctr = 0;
-       int ret;
+       u32 i;
 
        ifobject->ns_fd = switch_namespace(ifobject->nsname);
 
-       if (test_type == TEST_TYPE_BPF_RES)
-               mmap_sz *= 2;
+       if (ifobject->umem->unaligned_mode)
+               mmap_flags |= MAP_HUGETLB;
 
-       bufs = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
-       if (bufs == MAP_FAILED)
-               exit_with_error(errno);
+       for (i = 0; i < test->nb_sockets; i++) {
+               u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size;
+               u32 ctr = 0;
+               void *bufs;
 
-       while (ctr++ < SOCK_RECONF_CTR) {
-               xsk_configure_umem(ifobject, bufs, umem_sz, 0);
-               ifobject->umem = ifobject->umem_arr[0];
-               ret = xsk_configure_socket(ifobject, 0);
-               if (!ret)
-                       break;
+               bufs = mmap(NULL, umem_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
+               if (bufs == MAP_FAILED)
+                       exit_with_error(errno);
 
-               /* Retry Create Socket if it fails as xsk_socket__create() is asynchronous */
-               usleep(USLEEP_MAX);
-               if (ctr >= SOCK_RECONF_CTR)
-                       exit_with_error(-ret);
-       }
+               while (ctr++ < SOCK_RECONF_CTR) {
+                       int ret;
 
-       ifobject->umem = ifobject->umem_arr[0];
-       ifobject->xsk = ifobject->xsk_arr[0];
+                       ret = xsk_configure_umem(&ifobject->umem_arr[i], bufs, umem_sz);
+                       if (ret)
+                               exit_with_error(-ret);
 
-       if (test_type == TEST_TYPE_BPF_RES) {
-               xsk_configure_umem(ifobject, (u8 *)bufs + umem_sz, umem_sz, 1);
-               ifobject->umem = ifobject->umem_arr[1];
-               ret = xsk_configure_socket(ifobject, 1);
-       }
+                       ret = xsk_configure_socket(&ifobject->xsk_arr[i], &ifobject->umem_arr[i],
+                                                  ifobject, i);
+                       if (!ret)
+                               break;
 
-       ifobject->umem = ifobject->umem_arr[0];
-       ifobject->xsk = ifobject->xsk_arr[0];
-       print_verbose("Interface [%s] vector [%s]\n",
-                     ifobject->ifname, ifobject->fv.vector == tx ? "Tx" : "Rx");
-}
+                       /* Retry if it fails as xsk_socket__create() is asynchronous */
+                       if (ctr >= SOCK_RECONF_CTR)
+                               exit_with_error(-ret);
+                       usleep(USLEEP_MAX);
+               }
+       }
 
-static bool testapp_is_test_two_stepped(void)
-{
-       return (test_type != TEST_TYPE_BIDI && test_type != TEST_TYPE_BPF_RES) || second_step;
+       ifobject->umem = &ifobject->umem_arr[0];
+       ifobject->xsk = &ifobject->xsk_arr[0];
 }
 
 static void testapp_cleanup_xsk_res(struct ifobject *ifobj)
 {
-       if (testapp_is_test_two_stepped()) {
-               xsk_socket__delete(ifobj->xsk->xsk);
-               (void)xsk_umem__delete(ifobj->umem->umem);
-       }
+       print_verbose("Destroying socket\n");
+       xsk_socket__delete(ifobj->xsk->xsk);
+       munmap(ifobj->umem->buffer, ifobj->umem->num_frames * ifobj->umem->frame_size);
+       xsk_umem__delete(ifobj->umem->umem);
 }
 
 static void *worker_testapp_validate_tx(void *arg)
 {
-       struct ifobject *ifobject = (struct ifobject *)arg;
-       void *bufs = NULL;
+       struct test_spec *test = (struct test_spec *)arg;
+       struct ifobject *ifobject = test->ifobj_tx;
 
-       if (!second_step)
-               thread_common_ops(ifobject, bufs);
+       if (test->current_step == 1)
+               thread_common_ops(test, ifobject);
 
        print_verbose("Sending %d packets on interface %s\n", ifobject->pkt_stream->nb_pkts,
                      ifobject->ifname);
@@ -824,24 +970,55 @@ static void *worker_testapp_validate_tx(void *arg)
        if (stat_test_type == STAT_TEST_TX_INVALID)
                tx_stats_validate(ifobject);
 
-       testapp_cleanup_xsk_res(ifobject);
+       if (test->total_steps == test->current_step)
+               testapp_cleanup_xsk_res(ifobject);
        pthread_exit(NULL);
 }
 
+static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream)
+{
+       u32 idx = 0, i, buffers_to_fill;
+       int ret;
+
+       if (umem->num_frames < XSK_RING_PROD__DEFAULT_NUM_DESCS)
+               buffers_to_fill = umem->num_frames;
+       else
+               buffers_to_fill = XSK_RING_PROD__DEFAULT_NUM_DESCS;
+
+       ret = xsk_ring_prod__reserve(&umem->fq, buffers_to_fill, &idx);
+       if (ret != buffers_to_fill)
+               exit_with_error(ENOSPC);
+       for (i = 0; i < buffers_to_fill; i++) {
+               u64 addr;
+
+               if (pkt_stream->use_addr_for_fill) {
+                       struct pkt *pkt = pkt_stream_get_pkt(pkt_stream, i);
+
+                       if (!pkt)
+                               break;
+                       addr = pkt->addr;
+               } else {
+                       addr = i * umem->frame_size + DEFAULT_OFFSET;
+               }
+
+               *xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr;
+       }
+       xsk_ring_prod__submit(&umem->fq, buffers_to_fill);
+}
+
 static void *worker_testapp_validate_rx(void *arg)
 {
-       struct ifobject *ifobject = (struct ifobject *)arg;
-       struct pollfd fds[MAX_SOCKS] = { };
-       void *bufs = NULL;
+       struct test_spec *test = (struct test_spec *)arg;
+       struct ifobject *ifobject = test->ifobj_rx;
+       struct pollfd fds = { };
 
-       if (!second_step)
-               thread_common_ops(ifobject, bufs);
+       if (test->current_step == 1)
+               thread_common_ops(test, ifobject);
 
-       if (stat_test_type != STAT_TEST_RX_FILL_EMPTY)
-               xsk_populate_fill_ring(ifobject->umem);
+       xsk_populate_fill_ring(ifobject->umem, ifobject->pkt_stream);
 
-       fds[0].fd = xsk_socket__fd(ifobject->xsk->xsk);
-       fds[0].events = POLLIN;
+       fds.fd = xsk_socket__fd(ifobject->xsk->xsk);
+       fds.events = POLLIN;
 
        pthread_barrier_wait(&barr);
 
@@ -849,151 +1026,220 @@ static void *worker_testapp_validate_rx(void *arg)
                while (!rx_stats_are_valid(ifobject))
                        continue;
        else
-               receive_pkts(ifobject->pkt_stream, ifobject->xsk, fds);
-
-       if (test_type == TEST_TYPE_TEARDOWN)
-               print_verbose("Destroying socket\n");
+               receive_pkts(ifobject->pkt_stream, ifobject->xsk, &fds);
 
-       testapp_cleanup_xsk_res(ifobject);
+       if (test->total_steps == test->current_step)
+               testapp_cleanup_xsk_res(ifobject);
        pthread_exit(NULL);
 }
 
-static void testapp_validate(void)
+static void testapp_validate_traffic(struct test_spec *test)
 {
-       bool bidi = test_type == TEST_TYPE_BIDI;
-       bool bpf = test_type == TEST_TYPE_BPF_RES;
-       struct pkt_stream *pkt_stream;
+       struct ifobject *ifobj_tx = test->ifobj_tx;
+       struct ifobject *ifobj_rx = test->ifobj_rx;
+       pthread_t t0, t1;
 
        if (pthread_barrier_init(&barr, NULL, 2))
                exit_with_error(errno);
 
-       if (stat_test_type == STAT_TEST_TX_INVALID)
-               pkt_stream = pkt_stream_generate(DEFAULT_PKT_CNT, XSK_UMEM__INVALID_FRAME_SIZE);
-       else
-               pkt_stream = pkt_stream_generate(DEFAULT_PKT_CNT, PKT_SIZE);
-       ifdict_tx->pkt_stream = pkt_stream;
-       ifdict_rx->pkt_stream = pkt_stream;
+       test->current_step++;
+       pkt_stream_reset(ifobj_rx->pkt_stream);
 
        /*Spawn RX thread */
-       pthread_create(&t0, NULL, ifdict_rx->func_ptr, ifdict_rx);
+       pthread_create(&t0, NULL, ifobj_rx->func_ptr, test);
 
        pthread_barrier_wait(&barr);
        if (pthread_barrier_destroy(&barr))
                exit_with_error(errno);
 
        /*Spawn TX thread */
-       pthread_create(&t1, NULL, ifdict_tx->func_ptr, ifdict_tx);
+       pthread_create(&t1, NULL, ifobj_tx->func_ptr, test);
 
        pthread_join(t1, NULL);
        pthread_join(t0, NULL);
-
-       if (!(test_type == TEST_TYPE_TEARDOWN) && !bidi && !bpf && !(test_type == TEST_TYPE_STATS))
-               print_ksft_result();
 }
 
-static void testapp_teardown(void)
+static void testapp_teardown(struct test_spec *test)
 {
        int i;
 
+       test_spec_set_name(test, "TEARDOWN");
        for (i = 0; i < MAX_TEARDOWN_ITER; i++) {
-               print_verbose("Creating socket\n");
-               testapp_validate();
+               testapp_validate_traffic(test);
+               test_spec_reset(test);
        }
-
-       print_ksft_result();
 }
 
-static void swap_vectors(struct ifobject *ifobj1, struct ifobject *ifobj2)
+static void swap_directions(struct ifobject **ifobj1, struct ifobject **ifobj2)
 {
-       void *(*tmp_func_ptr)(void *) = ifobj1->func_ptr;
-       enum fvector tmp_vector = ifobj1->fv.vector;
+       thread_func_t tmp_func_ptr = (*ifobj1)->func_ptr;
+       struct ifobject *tmp_ifobj = (*ifobj1);
 
-       ifobj1->func_ptr = ifobj2->func_ptr;
-       ifobj1->fv.vector = ifobj2->fv.vector;
+       (*ifobj1)->func_ptr = (*ifobj2)->func_ptr;
+       (*ifobj2)->func_ptr = tmp_func_ptr;
 
-       ifobj2->func_ptr = tmp_func_ptr;
-       ifobj2->fv.vector = tmp_vector;
-
-       ifdict_tx = ifobj1;
-       ifdict_rx = ifobj2;
+       *ifobj1 = *ifobj2;
+       *ifobj2 = tmp_ifobj;
 }
 
-static void testapp_bidi(void)
+static void testapp_bidi(struct test_spec *test)
 {
-       for (int i = 0; i < MAX_BIDI_ITER; i++) {
-               print_verbose("Creating socket\n");
-               testapp_validate();
-               if (!second_step) {
-                       print_verbose("Switching Tx/Rx vectors\n");
-                       swap_vectors(ifdict[1], ifdict[0]);
-               }
-               second_step = true;
-       }
+       test_spec_set_name(test, "BIDIRECTIONAL");
+       test->ifobj_tx->rx_on = true;
+       test->ifobj_rx->tx_on = true;
+       test->total_steps = 2;
+       testapp_validate_traffic(test);
 
-       swap_vectors(ifdict[0], ifdict[1]);
+       print_verbose("Switching Tx/Rx vectors\n");
+       swap_directions(&test->ifobj_rx, &test->ifobj_tx);
+       testapp_validate_traffic(test);
 
-       print_ksft_result();
+       swap_directions(&test->ifobj_rx, &test->ifobj_tx);
 }
 
-static void swap_xsk_res(void)
+static void swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx)
 {
-       xsk_socket__delete(ifdict_tx->xsk->xsk);
-       xsk_umem__delete(ifdict_tx->umem->umem);
-       xsk_socket__delete(ifdict_rx->xsk->xsk);
-       xsk_umem__delete(ifdict_rx->umem->umem);
-       ifdict_tx->umem = ifdict_tx->umem_arr[1];
-       ifdict_tx->xsk = ifdict_tx->xsk_arr[1];
-       ifdict_rx->umem = ifdict_rx->umem_arr[1];
-       ifdict_rx->xsk = ifdict_rx->xsk_arr[1];
+       xsk_socket__delete(ifobj_tx->xsk->xsk);
+       xsk_umem__delete(ifobj_tx->umem->umem);
+       xsk_socket__delete(ifobj_rx->xsk->xsk);
+       xsk_umem__delete(ifobj_rx->umem->umem);
+       ifobj_tx->umem = &ifobj_tx->umem_arr[1];
+       ifobj_tx->xsk = &ifobj_tx->xsk_arr[1];
+       ifobj_rx->umem = &ifobj_rx->umem_arr[1];
+       ifobj_rx->xsk = &ifobj_rx->xsk_arr[1];
 }
 
-static void testapp_bpf_res(void)
+static void testapp_bpf_res(struct test_spec *test)
 {
-       int i;
-
-       for (i = 0; i < MAX_BPF_ITER; i++) {
-               print_verbose("Creating socket\n");
-               testapp_validate();
-               if (!second_step)
-                       swap_xsk_res();
-               second_step = true;
-       }
+       test_spec_set_name(test, "BPF_RES");
+       test->total_steps = 2;
+       test->nb_sockets = 2;
+       testapp_validate_traffic(test);
 
-       print_ksft_result();
+       swap_xsk_resources(test->ifobj_tx, test->ifobj_rx);
+       testapp_validate_traffic(test);
 }
 
-static void testapp_stats(void)
+static void testapp_stats(struct test_spec *test)
 {
-       for (int i = 0; i < STAT_TEST_TYPE_MAX; i++) {
-               stat_test_type = i;
+       int i;
 
-               /* reset defaults */
-               rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
-               frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
+       for (i = 0; i < STAT_TEST_TYPE_MAX; i++) {
+               test_spec_reset(test);
+               stat_test_type = i;
 
                switch (stat_test_type) {
                case STAT_TEST_RX_DROPPED:
-                       frame_headroom = XSK_UMEM__DEFAULT_FRAME_SIZE -
-                                               XDP_PACKET_HEADROOM - 1;
+                       test_spec_set_name(test, "STAT_RX_DROPPED");
+                       test->ifobj_rx->umem->frame_headroom = test->ifobj_rx->umem->frame_size -
+                               XDP_PACKET_HEADROOM - 1;
+                       testapp_validate_traffic(test);
                        break;
                case STAT_TEST_RX_FULL:
-                       rxqsize = RX_FULL_RXQSIZE;
+                       test_spec_set_name(test, "STAT_RX_FULL");
+                       test->ifobj_rx->xsk->rxqsize = RX_FULL_RXQSIZE;
+                       testapp_validate_traffic(test);
                        break;
                case STAT_TEST_TX_INVALID:
-                       continue;
+                       test_spec_set_name(test, "STAT_TX_INVALID");
+                       pkt_stream_replace(test, DEFAULT_PKT_CNT, XSK_UMEM__INVALID_FRAME_SIZE);
+                       testapp_validate_traffic(test);
+
+                       pkt_stream_restore_default(test);
+                       break;
+               case STAT_TEST_RX_FILL_EMPTY:
+                       test_spec_set_name(test, "STAT_RX_FILL_EMPTY");
+                       test->ifobj_rx->pkt_stream = pkt_stream_generate(test->ifobj_rx->umem, 0,
+                                                                        MIN_PKT_SIZE);
+                       if (!test->ifobj_rx->pkt_stream)
+                               exit_with_error(ENOMEM);
+                       test->ifobj_rx->pkt_stream->use_addr_for_fill = true;
+                       testapp_validate_traffic(test);
+
+                       pkt_stream_restore_default(test);
+                       break;
                default:
                        break;
                }
-               testapp_validate();
        }
 
-       print_ksft_result();
+       /* To only see the whole stat set being completed unless an individual test fails. */
+       test_spec_set_name(test, "STATS");
+}
+
+/* Simple test */
+static bool hugepages_present(struct ifobject *ifobject)
+{
+       const size_t mmap_sz = 2 * ifobject->umem->num_frames * ifobject->umem->frame_size;
+       void *bufs;
+
+       bufs = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_HUGETLB, -1, 0);
+       if (bufs == MAP_FAILED)
+               return false;
+
+       munmap(bufs, mmap_sz);
+       return true;
+}
+
+static bool testapp_unaligned(struct test_spec *test)
+{
+       if (!hugepages_present(test->ifobj_tx)) {
+               ksft_test_result_skip("No 2M huge pages present.\n");
+               return false;
+       }
+
+       test_spec_set_name(test, "UNALIGNED_MODE");
+       test->ifobj_tx->umem->unaligned_mode = true;
+       test->ifobj_rx->umem->unaligned_mode = true;
+       /* Let half of the packets straddle a buffer boundrary */
+       pkt_stream_replace_half(test, PKT_SIZE, test->ifobj_tx->umem->frame_size - 32);
+       test->ifobj_rx->pkt_stream->use_addr_for_fill = true;
+       testapp_validate_traffic(test);
+
+       pkt_stream_restore_default(test);
+       return true;
+}
+
+static void testapp_invalid_desc(struct test_spec *test)
+{
+       struct pkt pkts[] = {
+               /* Zero packet length at address zero allowed */
+               {0, 0, 0, true},
+               /* Zero packet length allowed */
+               {0x1000, 0, 0, true},
+               /* Straddling the start of umem */
+               {-2, PKT_SIZE, 0, false},
+               /* Packet too large */
+               {0x2000, XSK_UMEM__INVALID_FRAME_SIZE, 0, false},
+               /* After umem ends */
+               {UMEM_SIZE, PKT_SIZE, 0, false},
+               /* Straddle the end of umem */
+               {UMEM_SIZE - PKT_SIZE / 2, PKT_SIZE, 0, false},
+               /* Straddle a page boundrary */
+               {0x3000 - PKT_SIZE / 2, PKT_SIZE, 0, false},
+               /* Straddle a 2K boundrary */
+               {0x3800 - PKT_SIZE / 2, PKT_SIZE, 0, true},
+               /* Valid packet for synch so that something is received */
+               {0x4000, PKT_SIZE, 0, true}};
+
+       if (test->ifobj_tx->umem->unaligned_mode) {
+               /* Crossing a page boundrary allowed */
+               pkts[6].valid = true;
+       }
+       if (test->ifobj_tx->umem->frame_size == XSK_UMEM__DEFAULT_FRAME_SIZE / 2) {
+               /* Crossing a 2K frame size boundrary not allowed */
+               pkts[7].valid = false;
+       }
+
+       pkt_stream_generate_custom(test, pkts, ARRAY_SIZE(pkts));
+       testapp_validate_traffic(test);
+       pkt_stream_restore_default(test);
 }
 
-static void init_iface(struct ifobject *ifobj, const char *dst_mac,
-                      const char *src_mac, const char *dst_ip,
-                      const char *src_ip, const u16 dst_port,
-                      const u16 src_port, enum fvector vector)
+static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char *src_mac,
+                      const char *dst_ip, const char *src_ip, const u16 dst_port,
+                      const u16 src_port, thread_func_t func_ptr)
 {
        struct in_addr ip;
 
@@ -1009,58 +1255,73 @@ static void init_iface(struct ifobject *ifobj, const char *dst_mac,
        ifobj->dst_port = dst_port;
        ifobj->src_port = src_port;
 
-       if (vector == tx) {
-               ifobj->fv.vector = tx;
-               ifobj->func_ptr = worker_testapp_validate_tx;
-               ifdict_tx = ifobj;
-       } else {
-               ifobj->fv.vector = rx;
-               ifobj->func_ptr = worker_testapp_validate_rx;
-               ifdict_rx = ifobj;
-       }
+       ifobj->func_ptr = func_ptr;
 }
 
-static void run_pkt_test(int mode, int type)
+static void run_pkt_test(struct test_spec *test, enum test_mode mode, enum test_type type)
 {
        test_type = type;
 
        /* reset defaults after potential previous test */
-       xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
-       second_step = 0;
        stat_test_type = -1;
-       rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
-       frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
-
-       configured_mode = mode;
-
-       switch (mode) {
-       case (TEST_MODE_SKB):
-               xdp_flags |= XDP_FLAGS_SKB_MODE;
-               break;
-       case (TEST_MODE_DRV):
-               xdp_flags |= XDP_FLAGS_DRV_MODE;
-               break;
-       default:
-               break;
-       }
 
        switch (test_type) {
        case TEST_TYPE_STATS:
-               testapp_stats();
+               testapp_stats(test);
                break;
        case TEST_TYPE_TEARDOWN:
-               testapp_teardown();
+               testapp_teardown(test);
                break;
        case TEST_TYPE_BIDI:
-               testapp_bidi();
+               testapp_bidi(test);
                break;
        case TEST_TYPE_BPF_RES:
-               testapp_bpf_res();
+               testapp_bpf_res(test);
+               break;
+       case TEST_TYPE_RUN_TO_COMPLETION:
+               test_spec_set_name(test, "RUN_TO_COMPLETION");
+               testapp_validate_traffic(test);
+               break;
+       case TEST_TYPE_RUN_TO_COMPLETION_2K_FRAME:
+               test_spec_set_name(test, "RUN_TO_COMPLETION_2K_FRAME_SIZE");
+               test->ifobj_tx->umem->frame_size = 2048;
+               test->ifobj_rx->umem->frame_size = 2048;
+               pkt_stream_replace(test, DEFAULT_PKT_CNT, MIN_PKT_SIZE);
+               testapp_validate_traffic(test);
+
+               pkt_stream_restore_default(test);
+               break;
+       case TEST_TYPE_POLL:
+               test->ifobj_tx->use_poll = true;
+               test->ifobj_rx->use_poll = true;
+               test_spec_set_name(test, "POLL");
+               testapp_validate_traffic(test);
+               break;
+       case TEST_TYPE_ALIGNED_INV_DESC:
+               test_spec_set_name(test, "ALIGNED_INV_DESC");
+               testapp_invalid_desc(test);
+               break;
+       case TEST_TYPE_ALIGNED_INV_DESC_2K_FRAME:
+               test_spec_set_name(test, "ALIGNED_INV_DESC_2K_FRAME_SIZE");
+               test->ifobj_tx->umem->frame_size = 2048;
+               test->ifobj_rx->umem->frame_size = 2048;
+               testapp_invalid_desc(test);
+               break;
+       case TEST_TYPE_UNALIGNED_INV_DESC:
+               test_spec_set_name(test, "UNALIGNED_INV_DESC");
+               test->ifobj_tx->umem->unaligned_mode = true;
+               test->ifobj_rx->umem->unaligned_mode = true;
+               testapp_invalid_desc(test);
+               break;
+       case TEST_TYPE_UNALIGNED:
+               if (!testapp_unaligned(test))
+                       return;
                break;
        default:
-               testapp_validate();
                break;
        }
+
+       print_ksft_result(test);
 }
 
 static struct ifobject *ifobject_create(void)
@@ -1071,11 +1332,11 @@ static struct ifobject *ifobject_create(void)
        if (!ifobj)
                return NULL;
 
-       ifobj->xsk_arr = calloc(2, sizeof(struct xsk_socket_info *));
+       ifobj->xsk_arr = calloc(MAX_SOCKETS, sizeof(*ifobj->xsk_arr));
        if (!ifobj->xsk_arr)
                goto out_xsk_arr;
 
-       ifobj->umem_arr = calloc(2, sizeof(struct xsk_umem_info *));
+       ifobj->umem_arr = calloc(MAX_SOCKETS, sizeof(*ifobj->umem_arr));
        if (!ifobj->umem_arr)
                goto out_umem_arr;
 
@@ -1098,34 +1359,53 @@ static void ifobject_delete(struct ifobject *ifobj)
 int main(int argc, char **argv)
 {
        struct rlimit _rlim = { RLIM_INFINITY, RLIM_INFINITY };
-       int i, j;
+       struct pkt_stream *pkt_stream_default;
+       struct ifobject *ifobj_tx, *ifobj_rx;
+       struct test_spec test;
+       u32 i, j;
 
        if (setrlimit(RLIMIT_MEMLOCK, &_rlim))
                exit_with_error(errno);
 
-       for (i = 0; i < MAX_INTERFACES; i++) {
-               ifdict[i] = ifobject_create();
-               if (!ifdict[i])
-                       exit_with_error(ENOMEM);
-       }
+       ifobj_tx = ifobject_create();
+       if (!ifobj_tx)
+               exit_with_error(ENOMEM);
+       ifobj_rx = ifobject_create();
+       if (!ifobj_rx)
+               exit_with_error(ENOMEM);
 
        setlocale(LC_ALL, "");
 
-       parse_command_line(argc, argv);
+       parse_command_line(ifobj_tx, ifobj_rx, argc, argv);
 
-       init_iface(ifdict[tx], MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2, tx);
-       init_iface(ifdict[rx], MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1, rx);
+       if (!validate_interface(ifobj_tx) || !validate_interface(ifobj_rx)) {
+               usage(basename(argv[0]));
+               ksft_exit_xfail();
+       }
+
+       init_iface(ifobj_tx, MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2,
+                  worker_testapp_validate_tx);
+       init_iface(ifobj_rx, MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1,
+                  worker_testapp_validate_rx);
+
+       test_spec_init(&test, ifobj_tx, ifobj_rx, 0);
+       pkt_stream_default = pkt_stream_generate(ifobj_tx->umem, DEFAULT_PKT_CNT, PKT_SIZE);
+       if (!pkt_stream_default)
+               exit_with_error(ENOMEM);
+       test.pkt_stream_default = pkt_stream_default;
 
        ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);
 
        for (i = 0; i < TEST_MODE_MAX; i++)
                for (j = 0; j < TEST_TYPE_MAX; j++) {
-                       run_pkt_test(i, j);
+                       test_spec_init(&test, ifobj_tx, ifobj_rx, i);
+                       run_pkt_test(&test, i, j);
                        usleep(USLEEP_MAX);
                }
 
-       for (i = 0; i < MAX_INTERFACES; i++)
-               ifobject_delete(ifdict[i]);
+       pkt_stream_delete(pkt_stream_default);
+       ifobject_delete(ifobj_tx);
+       ifobject_delete(ifobj_rx);
 
        ksft_exit_pass();
        return 0;