* These selftests test AF_XDP SKB and Native/DRV modes using veth
  * Virtual Ethernet interfaces.
  *
- * The following tests are run:
- *
- * 1. AF_XDP SKB mode
- *    Generic mode XDP is driver independent, used when the driver does
- *    not have support for XDP. Works on any netdevice using sockets and
- *    generic XDP path. XDP hook from netif_receive_skb().
+ * For each mode, the following tests are run:
  *    a. nopoll - soft-irq processing
  *    b. poll - using poll() syscall
  *    c. Socket Teardown
  *       completion rings on each socket, tx/rx in both directions. Only nopoll
  *       mode is used
  *
- * 2. AF_XDP DRV/Native mode
- *    Works on any netdevice with XDP_REDIRECT support, driver dependent. Processes
- *    packets before SKB allocation. Provides better performance than SKB. Driver
- *    hook available just after DMA of buffer descriptor.
- *    a. nopoll
- *    b. poll
- *    c. Socket Teardown
- *    d. Bi-directional sockets
- *    - Only copy mode is supported because veth does not currently support
- *      zero-copy mode
- *
  * Total tests: 8
  *
  * Flow:
 
 static void __exit_with_error(int error, const char *file, const char *func, int line)
 {
-       ksft_test_result_fail
-           ("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file, func, line, error, strerror(error));
-       ksft_exit_xfail();
+       if (configured_mode == TEST_MODE_UNCONFIGURED) {
+               ksft_exit_fail_msg
+               ("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file, func, line, error, strerror(error));
+       } else {
+               ksft_test_result_fail
+               ("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file, func, line, error, strerror(error));
+               ksft_exit_xfail();
+       }
 }
 
 #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\n", uut ? "DRV" : "SKB", opt_poll ? "POLL" :\
-                              "NOPOLL", opt_teardown ? "Socket Teardown" : "",\
-                              opt_bidi ? "Bi-directional Sockets" : ""))
+       (ksft_test_result_pass("PASS: %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" : ""))
 
 static void pthread_init_mutex(void)
 {
        cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
        cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
        cfg.libbpf_flags = 0;
-       cfg.xdp_flags = opt_xdp_flags;
-       cfg.bind_flags = opt_xdp_bind_flags;
+       cfg.xdp_flags = xdp_flags;
+       cfg.bind_flags = xdp_bind_flags;
 
-       if (!opt_bidi) {
+       if (test_type != TEST_TYPE_BIDI) {
                rxr = (ifobject->fv.vector == rx) ? &ifobject->xsk->rx : NULL;
                txr = (ifobject->fv.vector == tx) ? &ifobject->xsk->tx : NULL;
        } else {
 static struct option long_options[] = {
        {"interface", required_argument, 0, 'i'},
        {"queue", optional_argument, 0, 'q'},
-       {"poll", no_argument, 0, 'p'},
-       {"xdp-skb", no_argument, 0, 'S'},
-       {"xdp-native", no_argument, 0, 'N'},
-       {"copy", no_argument, 0, 'c'},
-       {"tear-down", no_argument, 0, 'T'},
-       {"bidi", optional_argument, 0, 'B'},
        {"dump-pkts", optional_argument, 0, 'D'},
        {"verbose", no_argument, 0, 'v'},
        {"tx-pkt-count", optional_argument, 0, 'C'},
            "  Options:\n"
            "  -i, --interface      Use interface\n"
            "  -q, --queue=n        Use queue n (default 0)\n"
-           "  -p, --poll           Use poll syscall\n"
-           "  -S, --xdp-skb=n      Use XDP SKB mode\n"
-           "  -N, --xdp-native=n   Enforce XDP DRV (native) mode\n"
-           "  -c, --copy           Force copy mode\n"
-           "  -T, --tear-down      Tear down sockets by repeatedly recreating them\n"
-           "  -B, --bidi           Bi-directional sockets test\n"
            "  -D, --dump-pkts      Dump packets L2 - L5\n"
            "  -v, --verbose        Verbose output\n"
            "  -C, --tx-pkt-count=n Number of packets to send\n";
        opterr = 0;
 
        for (;;) {
-               c = getopt_long(argc, argv, "i:q:pSNcTBDC:v", long_options, &option_index);
+               c = getopt_long(argc, argv, "i:q:DC:v", long_options, &option_index);
 
                if (c == -1)
                        break;
                case 'q':
                        opt_queue = atoi(optarg);
                        break;
-               case 'p':
-                       opt_poll = 1;
-                       break;
-               case 'S':
-                       opt_xdp_flags |= XDP_FLAGS_SKB_MODE;
-                       opt_xdp_bind_flags |= XDP_COPY;
-                       uut = ORDER_CONTENT_VALIDATE_XDP_SKB;
-                       break;
-               case 'N':
-                       opt_xdp_flags |= XDP_FLAGS_DRV_MODE;
-                       opt_xdp_bind_flags |= XDP_COPY;
-                       uut = ORDER_CONTENT_VALIDATE_XDP_DRV;
-                       break;
-               case 'c':
-                       opt_xdp_bind_flags |= XDP_COPY;
-                       break;
-               case 'T':
-                       opt_teardown = 1;
-                       break;
-               case 'B':
-                       opt_bidi = 1;
-                       break;
                case 'D':
                        debug_pkt_dump = 1;
                        break;
                }
        }
 
+       if (!opt_pkt_count) {
+               print_verbose("No tx-pkt-count specified, using default %u\n", DEFAULT_PKT_CNT);
+               opt_pkt_count = DEFAULT_PKT_CNT;
+       }
+
        if (!validate_interfaces()) {
                usage(basename(argv[0]));
                ksft_exit_xfail();
        while ((opt_pkt_count && pkt_cnt < opt_pkt_count) || !opt_pkt_count) {
                int batch_size = get_batch_size(pkt_cnt);
 
-               if (opt_poll) {
+               if (test_type == TEST_TYPE_POLL) {
                        ret = poll(fds, 1, POLL_TMOUT);
                        if (ret <= 0)
                                continue;
                pthread_mutex_unlock(&sync_mutex);
 
                while (1) {
-                       if (opt_poll) {
+                       if (test_type == TEST_TYPE_POLL) {
                                ret = poll(fds, 1, POLL_TMOUT);
                                if (ret <= 0)
                                        continue;
                print_verbose("Received %d packets on interface %s\n",
                               pkt_counter, ifobject->ifname);
 
-               if (opt_teardown)
+               if (test_type == TEST_TYPE_TEARDOWN)
                        print_verbose("Destroying socket\n");
        }
 
-       if (!opt_bidi || bidi_pass) {
+       if ((test_type != TEST_TYPE_BIDI) || bidi_pass) {
                xsk_socket__delete(ifobject->xsk->xsk);
                (void)xsk_umem__delete(ifobject->umem->umem);
        }
 static void testapp_validate(void)
 {
        struct timespec max_wait = { 0, 0 };
+       bool bidi = test_type == TEST_TYPE_BIDI;
 
        pthread_attr_init(&attr);
        pthread_attr_setstacksize(&attr, THREAD_STACK);
 
-       if (opt_bidi && bidi_pass) {
+       if ((test_type == TEST_TYPE_BIDI) && bidi_pass) {
                pthread_init_mutex();
                if (!switching_notify) {
                        print_verbose("Switching Tx/Rx vectors\n");
        pthread_mutex_lock(&sync_mutex);
 
        /*Spawn RX thread */
-       if (!opt_bidi || !bidi_pass) {
+       if (!bidi || !bidi_pass) {
                if (pthread_create(&t0, &attr, worker_testapp_validate, ifdict[1]))
                        exit_with_error(errno);
-       } else if (opt_bidi && bidi_pass) {
+       } else if (bidi && bidi_pass) {
                /*switch Tx/Rx vectors */
                ifdict[0]->fv.vector = rx;
                if (pthread_create(&t0, &attr, worker_testapp_validate, ifdict[0]))
        pthread_mutex_unlock(&sync_mutex);
 
        /*Spawn TX thread */
-       if (!opt_bidi || !bidi_pass) {
+       if (!bidi || !bidi_pass) {
                if (pthread_create(&t1, &attr, worker_testapp_validate, ifdict[0]))
                        exit_with_error(errno);
-       } else if (opt_bidi && bidi_pass) {
+       } else if (bidi && bidi_pass) {
                /*switch Tx/Rx vectors */
                ifdict[1]->fv.vector = tx;
                if (pthread_create(&t1, &attr, worker_testapp_validate, ifdict[1]))
                free(pkt_buf);
        }
 
-       if (!opt_teardown && !opt_bidi)
+       if (!(test_type == TEST_TYPE_TEARDOWN) && !bidi)
                print_ksft_result();
 }
 
 static void testapp_sockets(void)
 {
-       for (int i = 0; i < (opt_teardown ? MAX_TEARDOWN_ITER : MAX_BIDI_ITER); i++) {
+       for (int i = 0; i < ((test_type == TEST_TYPE_TEARDOWN) ? MAX_TEARDOWN_ITER : MAX_BIDI_ITER);
+            i++) {
                pkt_counter = 0;
                prev_pkt = -1;
                sigvar = 0;
                print_verbose("Creating socket\n");
                testapp_validate();
-               opt_bidi ? bidi_pass++ : bidi_pass;
+               test_type == TEST_TYPE_BIDI ? bidi_pass++ : bidi_pass;
        }
 
        print_ksft_result();
        ifdict[1]->src_port = ifaceconfig->dst_port;
 }
 
+static void *nsdisablemodethread(void *args)
+{
+       struct targs *targs = args;
+
+       targs->retptr = false;
+
+       if (switch_namespace(targs->idx)) {
+               targs->retptr = bpf_set_link_xdp_fd(ifdict[targs->idx]->ifindex, -1, targs->flags);
+       } else {
+               targs->retptr = errno;
+               print_verbose("Failed to switch namespace to %s\n", ifdict[targs->idx]->nsname);
+       }
+
+       pthread_exit(NULL);
+}
+
+static void disable_xdp_mode(int mode)
+{
+       int err = 0;
+       __u32 flags = XDP_FLAGS_UPDATE_IF_NOEXIST | mode;
+       char *mode_str = mode & XDP_FLAGS_SKB_MODE ? "skb" : "drv";
+
+       for (int i = 0; i < MAX_INTERFACES; i++) {
+               if (strcmp(ifdict[i]->nsname, "")) {
+                       struct targs *targs;
+
+                       targs = malloc(sizeof(*targs));
+                       memset(targs, 0, sizeof(*targs));
+                       if (!targs)
+                               exit_with_error(errno);
+
+                       targs->idx = i;
+                       targs->flags = flags;
+                       if (pthread_create(&ns_thread, NULL, nsdisablemodethread, targs))
+                               exit_with_error(errno);
+
+                       pthread_join(ns_thread, NULL);
+                       err = targs->retptr;
+                       free(targs);
+               } else {
+                       err = bpf_set_link_xdp_fd(ifdict[i]->ifindex, -1, flags);
+               }
+
+               if (err) {
+                       print_verbose("Failed to disable %s mode on interface %s\n",
+                                               mode_str, ifdict[i]->ifname);
+                       exit_with_error(err);
+               }
+
+               print_verbose("Disabled %s mode for interface: %s\n", mode_str, ifdict[i]->ifname);
+               configured_mode = mode & XDP_FLAGS_SKB_MODE ? TEST_MODE_DRV : TEST_MODE_SKB;
+       }
+}
+
+static void run_pkt_test(int mode, int type)
+{
+       test_type = type;
+
+       /* reset defaults after potential previous test */
+       xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+       pkt_counter = 0;
+       switching_notify = 0;
+       bidi_pass = 0;
+       prev_pkt = -1;
+       ifdict[0]->fv.vector = tx;
+       ifdict[1]->fv.vector = rx;
+
+       switch (mode) {
+       case (TEST_MODE_SKB):
+               if (configured_mode == TEST_MODE_DRV)
+                       disable_xdp_mode(XDP_FLAGS_DRV_MODE);
+               xdp_flags |= XDP_FLAGS_SKB_MODE;
+               break;
+       case (TEST_MODE_DRV):
+               if (configured_mode == TEST_MODE_SKB)
+                       disable_xdp_mode(XDP_FLAGS_SKB_MODE);
+               xdp_flags |= XDP_FLAGS_DRV_MODE;
+               break;
+       default:
+               break;
+       }
+
+       pthread_init_mutex();
+
+       if ((test_type != TEST_TYPE_TEARDOWN) && (test_type != TEST_TYPE_BIDI))
+               testapp_validate();
+       else
+               testapp_sockets();
+
+       pthread_destroy_mutex();
+}
+
 int main(int argc, char **argv)
 {
        struct rlimit _rlim = { RLIM_INFINITY, RLIM_INFINITY };
        const char *IP2 = "192.168.100.161";
        u16 UDP_DST_PORT = 2020;
        u16 UDP_SRC_PORT = 2121;
+       int i, j;
 
        ifaceconfig = malloc(sizeof(struct ifaceconfigobj));
        memcpy(ifaceconfig->dst_mac, MAC1, ETH_ALEN);
 
        init_iface_config(ifaceconfig);
 
-       pthread_init_mutex();
+       disable_xdp_mode(XDP_FLAGS_DRV_MODE);
 
-       ksft_set_plan(1);
+       ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);
 
-       if (!opt_teardown && !opt_bidi) {
-               testapp_validate();
-       } else if (opt_teardown && opt_bidi) {
-               ksft_test_result_fail("ERROR: parameters -T and -B cannot be used together\n");
-               ksft_exit_xfail();
-       } else {
-               testapp_sockets();
+       for (i = 0; i < TEST_MODE_MAX; i++) {
+               for (j = 0; j < TEST_TYPE_MAX; j++)
+                       run_pkt_test(i, j);
        }
 
        for (int i = 0; i < MAX_INTERFACES; i++)
                free(ifdict[i]);
 
-       pthread_destroy_mutex();
-
        ksft_exit_pass();
 
        return 0;