diff --git a/lib/conntrack.c b/lib/conntrack.c
index de46a6b..e290b20 100644
--- a/lib/conntrack.c
+++ b/lib/conntrack.c
@@ -1931,7 +1931,7 @@ conn_key_to_tuple(const struct conn_key *key, struct ct_dpif_tuple *tuple)
 
 static void
 conn_to_ct_dpif_entry(const struct conn *conn, struct ct_dpif_entry *entry,
-                      long long now)
+                      long long now, int bkt)
 {
     struct ct_l4_proto *class;
     long long expiration;
@@ -1954,11 +1954,12 @@ conn_to_ct_dpif_entry(const struct conn *conn, struct ct_dpif_entry *entry,
     if (class->conn_get_protoinfo) {
         class->conn_get_protoinfo(conn, &entry->protoinfo);
     }
+    entry->bkt = bkt;
 }
 
 int
 conntrack_dump_start(struct conntrack *ct, struct conntrack_dump *dump,
-                     const uint16_t *pzone)
+                     const uint16_t *pzone, int *ptot_bkts)
 {
     memset(dump, 0, sizeof(*dump));
     if (pzone) {
@@ -1967,6 +1968,8 @@ conntrack_dump_start(struct conntrack *ct, struct conntrack_dump *dump,
     }
     dump->ct = ct;
 
+    *ptot_bkts = CONNTRACK_BUCKETS;
+
     return 0;
 }
 
@@ -1991,7 +1994,7 @@ conntrack_dump_next(struct conntrack_dump *dump, struct ct_dpif_entry *entry)
             INIT_CONTAINER(conn, node, node);
             if ((!dump->filter_zone || conn->key.zone == dump->zone) &&
                  (conn->conn_type != CT_CONN_TYPE_UN_NAT)) {
-                conn_to_ct_dpif_entry(conn, entry, now);
+                conn_to_ct_dpif_entry(conn, entry, now, dump->bucket);
                 break;
             }
             /* Else continue, until we find an entry in the appropriate zone
diff --git a/lib/conntrack.h b/lib/conntrack.h
index defde4c..3f48444 100644
--- a/lib/conntrack.h
+++ b/lib/conntrack.h
@@ -108,7 +108,7 @@ struct conntrack_dump {
 struct ct_dpif_entry;
 
 int conntrack_dump_start(struct conntrack *, struct conntrack_dump *,
-                         const uint16_t *pzone);
+                         const uint16_t *pzone, int *);
 int conntrack_dump_next(struct conntrack_dump *, struct ct_dpif_entry *);
 int conntrack_dump_done(struct conntrack_dump *);
 
diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
index f8d2cf1..c79e69e 100644
--- a/lib/ct-dpif.c
+++ b/lib/ct-dpif.c
@@ -65,12 +65,12 @@ static const struct flags ct_dpif_status_flags[] = {
  * that represents the error.  Otherwise it returns zero. */
 int
 ct_dpif_dump_start(struct dpif *dpif, struct ct_dpif_dump_state **dump,
-                   const uint16_t *zone)
+                   const uint16_t *zone, int *ptot_bkts)
 {
     int err;
 
     err = (dpif->dpif_class->ct_dump_start
-           ? dpif->dpif_class->ct_dump_start(dpif, dump, zone)
+           ? dpif->dpif_class->ct_dump_start(dpif, dump, zone, ptot_bkts)
            : EOPNOTSUPP);
 
     if (!err) {
diff --git a/lib/ct-dpif.h b/lib/ct-dpif.h
index cd35f3e..d5f9661 100644
--- a/lib/ct-dpif.h
+++ b/lib/ct-dpif.h
@@ -169,6 +169,7 @@ struct ct_dpif_entry {
     /* Timeout for this entry in seconds */
     uint32_t timeout;
     uint32_t mark;
+    uint32_t bkt;       /* CT bucket number. */
 };
 
 enum {
@@ -191,7 +192,7 @@ struct ct_dpif_dump_state {
 };
 
 int ct_dpif_dump_start(struct dpif *, struct ct_dpif_dump_state **,
-                       const uint16_t *zone);
+                       const uint16_t *zone, int *);
 int ct_dpif_dump_next(struct ct_dpif_dump_state *, struct ct_dpif_entry *);
 int ct_dpif_dump_done(struct ct_dpif_dump_state *);
 int ct_dpif_flush(struct dpif *, const uint16_t *zone);
diff --git a/lib/dpctl.c b/lib/dpctl.c
index 6aa6c8e..3ccc7a3 100644
--- a/lib/dpctl.c
+++ b/lib/dpctl.c
@@ -1258,6 +1258,7 @@ dpctl_dump_conntrack(int argc, const char *argv[],
     struct ct_dpif_dump_state *dump;
     struct ct_dpif_entry cte;
     uint16_t zone, *pzone = NULL;
+    int tot_bkts;
     struct dpif *dpif;
     char *name;
     int error;
@@ -1277,7 +1278,7 @@ dpctl_dump_conntrack(int argc, const char *argv[],
         return error;
     }
 
-    error = ct_dpif_dump_start(dpif, &dump, pzone);
+    error = ct_dpif_dump_start(dpif, &dump, pzone, &tot_bkts);
     if (error) {
         dpctl_error(dpctl_p, error, "starting conntrack dump");
         dpif_close(dpif);
@@ -1339,6 +1340,7 @@ dpctl_ct_stats_show(int argc, const char *argv[],
     struct ct_dpif_dump_state *dump;
     struct ct_dpif_entry cte;
     uint16_t zone, *pzone = NULL;
+    int tot_bkts;
     bool verbose = false;
     int lastargc = 0;
 
@@ -1373,7 +1375,7 @@ dpctl_ct_stats_show(int argc, const char *argv[],
 
     memset(proto_stats, 0, sizeof(proto_stats));
     memset(tcp_conn_per_states, 0, sizeof(tcp_conn_per_states));
-    error = ct_dpif_dump_start(dpif, &dump, pzone);
+    error = ct_dpif_dump_start(dpif, &dump, pzone, &tot_bkts);
     if (error) {
         dpctl_error(dpctl_p, error, "starting conntrack dump");
         dpif_close(dpif);
@@ -1464,6 +1466,107 @@ dpctl_ct_stats_show(int argc, const char *argv[],
     dpif_close(dpif);
     return error;
 }
+
+#define CT_BKTS_GT "gt="
+static int
+dpctl_ct_bkts(int argc, const char *argv[],
+                     struct dpctl_params *dpctl_p)
+{
+    struct dpif *dpif;
+    char *name;
+
+    struct ct_dpif_dump_state *dump;
+    struct ct_dpif_entry cte;
+    uint16_t gt = 0; /* Threshold: display value when greater than gt. */
+    uint16_t *pzone = NULL;
+    int tot_bkts = 0;
+    int lastargc = 0;
+
+    int error;
+
+    while (argc > 1 && lastargc != argc) {
+        lastargc = argc;
+        if (!strncmp(argv[argc - 1], CT_BKTS_GT, strlen(CT_BKTS_GT))) {
+            if (ovs_scan(argv[argc - 1], CT_BKTS_GT"%"SCNu16, &gt)) {
+                argc--;
+                break;
+            }
+        }
+    }
+
+    name = (argc > 1) ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
+    if (!name) {
+        return EINVAL;
+    }
+
+    error = parsed_dpif_open(name, false, &dpif);
+    free(name);
+    if (error) {
+        dpctl_error(dpctl_p, error, "opening datapath");
+        return error;
+    }
+
+    error = ct_dpif_dump_start(dpif, &dump, pzone, &tot_bkts);
+    if (error) {
+        dpctl_error(dpctl_p, error, "starting conntrack dump");
+        dpif_close(dpif);
+        return error;
+    }
+    if (tot_bkts == -1) {
+         /* Command not available when called by kernel OvS. */
+         dpctl_print(dpctl_p,
+             "Command is available for UserSpace ConnTracker only.\n");
+         ct_dpif_dump_done(dump);
+         return 0;
+    }
+
+    dpctl_print(dpctl_p, "Total Buckets: %d\n", tot_bkts);
+
+    int tot_conn = 0;
+    uint32_t conn_per_bkts[tot_bkts];
+    memset(conn_per_bkts, 0, sizeof(uint32_t) * tot_bkts);
+
+    while (!ct_dpif_dump_next(dump, &cte)) {
+        ct_dpif_entry_uninit(&cte);
+        tot_conn++;
+        if (tot_bkts > 0) {
+            if (cte.bkt < tot_bkts) {
+                conn_per_bkts[cte.bkt]++;
+            } else {
+                dpctl_print(dpctl_p, "Bucket nr out of range: %d >= %d\n",
+                        cte.bkt, tot_bkts);
+            }
+        }
+    }
+
+    dpctl_print(dpctl_p, "Current Connections: %d\n", tot_conn);
+    dpctl_print(dpctl_p, "\n");
+    if (tot_bkts && tot_conn) {
+        dpctl_print(dpctl_p, "+-----------+"
+                "-----------------------------------------+\n");
+        dpctl_print(dpctl_p, "|  Buckets  |"
+                "         Connections per Buckets         |\n");
+        dpctl_print(dpctl_p, "+-----------+"
+                "-----------------------------------------+");
+#define NUM_BKTS_DIPLAYED_PER_ROW 8
+        for (int i = 0; i < tot_bkts; i++) {
+            if (i % NUM_BKTS_DIPLAYED_PER_ROW == 0) {
+                 dpctl_print(dpctl_p, "\n %3d..%3d   | ",
+                         i, i + NUM_BKTS_DIPLAYED_PER_ROW - 1);
+            }
+            if (conn_per_bkts[i] > gt) {
+                dpctl_print(dpctl_p, "%5d", conn_per_bkts[i]);
+            } else {
+                dpctl_print(dpctl_p, "%5s", ".");
+            }
+        }
+        dpctl_print(dpctl_p, "\n\n");
+    }
+
+    ct_dpif_dump_done(dump);
+    dpif_close(dpif);
+    return error;
+}
 
 /* Undocumented commands for unit testing. */
 
@@ -1760,6 +1863,7 @@ static const struct dpctl_command all_commands[] = {
     { "flush-conntrack", "[dp] [zone=N]", 0, 2, dpctl_flush_conntrack, DP_RW },
     { "ct-stats-show", "[dp] [zone=N] [verbose]",
       0, 3, dpctl_ct_stats_show, DP_RO },
+    { "ct-bkts", "[dp] [gt=N]", 0, 2, dpctl_ct_bkts, DP_RO },
     { "help", "", 0, INT_MAX, dpctl_help, DP_RO },
     { "list-commands", "", 0, INT_MAX, dpctl_list_commands, DP_RO },
 
diff --git a/lib/dpctl.man b/lib/dpctl.man
index f95d54a..077fd99 100644
--- a/lib/dpctl.man
+++ b/lib/dpctl.man
@@ -228,3 +228,11 @@ Displays the number of connections grouped by protocol used by \fIdp\fR.
 If \fBzone=\fIzone\fR is specified, numbers refer to the connections in
 \fBzone\fR. The \fBverbose\fR option allows to group by connection state
 for each protocol.
+.
+.TP
+\*(DX\fBct\-bkts\fR [\fIdp\fR] [\fBgt=\fIThreshold\fR]
+For each ConnTracker bucket, displays the number of connections used
+by \fIdp\fR.
+If \fBgt=\fIThreshold\fR is specified, bucket numbers are displayed when
+the number of connections in a bucket is greater than \fIThreshold\fR.
+
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 47a9fa0..10435e7 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -5333,7 +5333,7 @@ struct dp_netdev_ct_dump {
 
 static int
 dpif_netdev_ct_dump_start(struct dpif *dpif, struct ct_dpif_dump_state **dump_,
-                          const uint16_t *pzone)
+                          const uint16_t *pzone, int *ptot_bkts)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     struct dp_netdev_ct_dump *dump;
@@ -5342,7 +5342,7 @@ dpif_netdev_ct_dump_start(struct dpif *dpif, struct ct_dpif_dump_state **dump_,
     dump->dp = dp;
     dump->ct = &dp->conntrack;
 
-    conntrack_dump_start(&dp->conntrack, &dump->dump, pzone);
+    conntrack_dump_start(&dp->conntrack, &dump->dump, pzone, ptot_bkts);
 
     *dump_ = &dump->up;
 
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 55effd1..b87bb16 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -2847,13 +2847,13 @@ struct dpif_netlink_ct_dump_state {
 static int
 dpif_netlink_ct_dump_start(struct dpif *dpif OVS_UNUSED,
                            struct ct_dpif_dump_state **dump_,
-                           const uint16_t *zone)
+                           const uint16_t *zone, int *ptot_bkts)
 {
     struct dpif_netlink_ct_dump_state *dump;
     int err;
 
     dump = xzalloc(sizeof *dump);
-    err = nl_ct_dump_start(&dump->nl_ct_dump, zone);
+    err = nl_ct_dump_start(&dump->nl_ct_dump, zone, ptot_bkts);
     if (err) {
         free(dump);
         return err;
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index 64ac2e2..1d82a09 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -419,7 +419,7 @@ struct dpif_class {
      * ct_dump_done() should perform any cleanup necessary (including
      * deallocating the 'state' structure, if applicable). */
     int (*ct_dump_start)(struct dpif *, struct ct_dpif_dump_state **state,
-                         const uint16_t *zone);
+                         const uint16_t *zone, int *);
     int (*ct_dump_next)(struct dpif *, struct ct_dpif_dump_state *state,
                         struct ct_dpif_entry *entry);
     int (*ct_dump_done)(struct dpif *, struct ct_dpif_dump_state *state);
diff --git a/lib/netlink-conntrack.c b/lib/netlink-conntrack.c
index f0e2aea..ac48b15 100644
--- a/lib/netlink-conntrack.c
+++ b/lib/netlink-conntrack.c
@@ -123,7 +123,8 @@ struct nl_ct_dump_state {
 
 /* Initialize a conntrack netlink dump. */
 int
-nl_ct_dump_start(struct nl_ct_dump_state **statep, const uint16_t *zone)
+nl_ct_dump_start(struct nl_ct_dump_state **statep, const uint16_t *zone,
+        int *ptot_bkts)
 {
     struct nl_ct_dump_state *state;
 
@@ -140,6 +141,9 @@ nl_ct_dump_start(struct nl_ct_dump_state **statep, const uint16_t *zone)
     nl_dump_start(&state->dump, NETLINK_NETFILTER, &state->buf);
     ofpbuf_clear(&state->buf);
 
+    /* Buckets to store connections are not used. */
+    *ptot_bkts = -1;
+
     return 0;
 }
 
diff --git a/lib/netlink-conntrack.h b/lib/netlink-conntrack.h
index 1263b21..a95aa69 100644
--- a/lib/netlink-conntrack.h
+++ b/lib/netlink-conntrack.h
@@ -35,7 +35,8 @@ enum nl_ct_event_type {
 
 struct nl_ct_dump_state;
 
-int nl_ct_dump_start(struct nl_ct_dump_state **, const uint16_t *zone);
+int nl_ct_dump_start(struct nl_ct_dump_state **, const uint16_t *zone,
+        int *ptot_bkts);
 int nl_ct_dump_next(struct nl_ct_dump_state *, struct ct_dpif_entry *);
 int nl_ct_dump_done(struct nl_ct_dump_state *);
 
diff --git a/tests/test-netlink-conntrack.c b/tests/test-netlink-conntrack.c
index 000062d..0d9dace 100644
--- a/tests/test-netlink-conntrack.c
+++ b/tests/test-netlink-conntrack.c
@@ -106,6 +106,7 @@ test_nl_ct_dump(struct ovs_cmdl_context *ctx)
     uint16_t zone, *pzone = NULL;
     struct ct_dpif_entry entry;
     int err;
+    int tot_bkts;
 
     if (ctx->argc >= 2) {
         if (!ovs_scan(ctx->argv[1], "zone=%"SCNu16, &zone)) {
@@ -113,7 +114,7 @@ test_nl_ct_dump(struct ovs_cmdl_context *ctx)
         }
         pzone = &zone;
     }
-    err = nl_ct_dump_start(&dump, pzone);
+    err = nl_ct_dump_start(&dump, pzone, &tot_bkts);
     if (err) {
         ovs_fatal(err, "Error creating conntrack netlink dump");
     }
diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
index 7292fca..7b005ac 100644
--- a/utilities/ovs-dpctl.c
+++ b/utilities/ovs-dpctl.c
@@ -202,6 +202,7 @@ usage(void *userdata OVS_UNUSED)
                "delete all conntrack entries in ZONE\n"
            "  ct-stats-show [DP] [zone=ZONE] [verbose] " \
                "CT connections grouped by protocol\n"
+           "  ct-bkts [DP] [gt=N] display connections per CT bucket\n"
            "Each IFACE on add-dp, add-if, and set-if may be followed by\n"
            "comma-separated options.  See ovs-dpctl(8) for syntax, or the\n"
            "Interface table in ovs-vswitchd.conf.db(5) for an options list.\n"
