*    c. Socket Teardown
  *       Create a Tx and a Rx socket, Tx from one socket, Rx on another. Destroy
  *       both sockets, then repeat multiple times. Only nopoll mode is used
+ *    d. Bi-directional sockets
+ *       Configure sockets as bi-directional tx/rx sockets, sets up fill and
+ *       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
  *    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: 6
+ * Total tests: 8
  *
  * Flow:
  * -----
 #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\n", uut ? "DRV" : "SKB", opt_poll ? "POLL" :\
-                              "NOPOLL", opt_teardown ? "Socket Teardown" : ""))
+       (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" : ""))
 
 static void pthread_init_mutex(void)
 {
        cfg.xdp_flags = opt_xdp_flags;
        cfg.bind_flags = opt_xdp_bind_flags;
 
-       rxr = (ifobject->fv.vector == rx) ? &ifobject->xsk->rx : NULL;
-       txr = (ifobject->fv.vector == tx) ? &ifobject->xsk->tx : NULL;
+       if (!opt_bidi) {
+               rxr = (ifobject->fv.vector == rx) ? &ifobject->xsk->rx : NULL;
+               txr = (ifobject->fv.vector == tx) ? &ifobject->xsk->tx : NULL;
+       } else {
+               rxr = &ifobject->xsk->rx;
+               txr = &ifobject->xsk->tx;
+       }
 
        ret = xsk_socket__create(&ifobject->xsk->xsk, ifobject->ifname,
                                 opt_queue, ifobject->umem->umem, rxr, txr, &cfg);
        {"xdp-native", no_argument, 0, 'N'},
        {"copy", no_argument, 0, 'c'},
        {"tear-down", no_argument, 0, 'T'},
+       {"bidi", optional_argument, 0, 'B'},
        {"debug", optional_argument, 0, 'D'},
        {"tx-pkt-count", optional_argument, 0, 'C'},
        {0, 0, 0, 0}
            "  -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, --debug          Debug mode - dump packets L2 - L5\n"
            "  -C, --tx-pkt-count=n Number of packets to send\n";
        ksft_print_msg(str, prog);
        opterr = 0;
 
        for (;;) {
-               c = getopt_long(argc, argv, "i:q:pSNcTDC:", long_options, &option_index);
+               c = getopt_long(argc, argv, "i:q:pSNcTBDC:", long_options, &option_index);
 
                if (c == -1)
                        break;
                case 'T':
                        opt_teardown = 1;
                        break;
+               case 'B':
+                       opt_bidi = 1;
+                       break;
                case 'D':
                        debug_pkt_dump = 1;
                        break;
        struct generic_data *data = (struct generic_data *)malloc(sizeof(struct generic_data));
        struct iphdr *ip_hdr = (struct iphdr *)(pkt_data + sizeof(struct ethhdr));
        struct ethhdr *eth_hdr = (struct ethhdr *)pkt_data;
-       void *bufs;
+       void *bufs = NULL;
 
        pthread_attr_setstacksize(&attr, THREAD_STACK);
 
-       bufs = mmap(NULL, num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE,
-                   PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-       if (bufs == MAP_FAILED)
-               exit_with_error(errno);
+       if (!bidi_pass) {
+               bufs = mmap(NULL, num_frames * XSK_UMEM__DEFAULT_FRAME_SIZE,
+                           PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+               if (bufs == MAP_FAILED)
+                       exit_with_error(errno);
 
-       if (strcmp(((struct ifobject *)arg)->nsname, ""))
-               switch_namespace(((struct ifobject *)arg)->ifdict_index);
+               if (strcmp(((struct ifobject *)arg)->nsname, ""))
+                       switch_namespace(((struct ifobject *)arg)->ifdict_index);
+       }
 
        if (((struct ifobject *)arg)->fv.vector == tx) {
                int spinningrxctr = 0;
 
-               thread_common_ops(arg, bufs, &sync_mutex_tx, &spinning_tx);
+               if (!bidi_pass)
+                       thread_common_ops(arg, bufs, &sync_mutex_tx, &spinning_tx);
 
                while (atomic_load(&spinning_rx) && spinningrxctr < SOCK_RECONF_CTR) {
                        spinningrxctr++;
                struct pollfd fds[MAX_SOCKS] = { };
                int ret;
 
-               thread_common_ops(arg, bufs, &sync_mutex_tx, &spinning_rx);
+               if (!bidi_pass)
+                       thread_common_ops(arg, bufs, &sync_mutex_tx, &spinning_rx);
 
                ksft_print_msg("Interface [%s] vector [Rx]\n", ((struct ifobject *)arg)->ifname);
                xsk_populate_fill_ring(((struct ifobject *)arg)->umem);
                        ksft_print_msg("Destroying socket\n");
        }
 
-       xsk_socket__delete(((struct ifobject *)arg)->xsk->xsk);
-       (void)xsk_umem__delete(((struct ifobject *)arg)->umem->umem);
+       if (!opt_bidi || (opt_bidi && bidi_pass)) {
+               xsk_socket__delete(((struct ifobject *)arg)->xsk->xsk);
+               (void)xsk_umem__delete(((struct ifobject *)arg)->umem->umem);
+       }
        pthread_exit(NULL);
 }
 
        pthread_attr_init(&attr);
        pthread_attr_setstacksize(&attr, THREAD_STACK);
 
+       if (opt_bidi && bidi_pass) {
+               pthread_init_mutex();
+               if (!switching_notify) {
+                       ksft_print_msg("Switching Tx/Rx vectors\n");
+                       switching_notify++;
+               }
+       }
+
        pthread_mutex_lock(&sync_mutex);
 
        /*Spawn RX thread */
-       if (pthread_create(&t0, &attr, worker_testapp_validate, (void *)ifdict[1]))
-               exit_with_error(errno);
+       if (!opt_bidi || (opt_bidi && !bidi_pass)) {
+               if (pthread_create(&t0, &attr, worker_testapp_validate, (void *)ifdict[1]))
+                       exit_with_error(errno);
+       } else if (opt_bidi && bidi_pass) {
+               /*switch Tx/Rx vectors */
+               ifdict[0]->fv.vector = rx;
+               if (pthread_create(&t0, &attr, worker_testapp_validate, (void *)ifdict[0]))
+                       exit_with_error(errno);
+       }
 
        struct timespec max_wait = { 0, 0 };
 
        pthread_mutex_unlock(&sync_mutex);
 
        /*Spawn TX thread */
-       if (pthread_create(&t1, &attr, worker_testapp_validate, (void *)ifdict[0]))
-               exit_with_error(errno);
+       if (!opt_bidi || (opt_bidi && !bidi_pass)) {
+               if (pthread_create(&t1, &attr, worker_testapp_validate, (void *)ifdict[0]))
+                       exit_with_error(errno);
+       } else if (opt_bidi && bidi_pass) {
+               /*switch Tx/Rx vectors */
+               ifdict[1]->fv.vector = tx;
+               if (pthread_create(&t1, &attr, worker_testapp_validate, (void *)ifdict[1]))
+                       exit_with_error(errno);
+       }
 
        pthread_join(t1, NULL);
        pthread_join(t0, NULL);
                free(pkt_buf);
        }
 
-       if (!opt_teardown)
+       if (!opt_teardown && !opt_bidi)
                print_ksft_result();
 }
 
 static void testapp_sockets(void)
 {
-       for (int i = 0; i < MAX_TEARDOWN_ITER; i++) {
+       for (int i = 0; i < (opt_teardown ? MAX_TEARDOWN_ITER : MAX_BIDI_ITER); i++) {
                pkt_counter = 0;
                prev_pkt = -1;
                sigvar = 0;
                ksft_print_msg("Creating socket\n");
                testapp_validate();
+               opt_bidi ? bidi_pass++ : bidi_pass;
        }
 
        print_ksft_result();
 
        ksft_set_plan(1);
 
-       opt_teardown ? testapp_sockets() : testapp_validate();
+       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 (int i = 0; i < MAX_INTERFACES; i++)
                free(ifdict[i]);