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;
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) {
}
dump->ct = ct;
+ *ptot_bkts = CONNTRACK_BUCKETS;
+
return 0;
}
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
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 *);
* 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) {
/* Timeout for this entry in seconds */
uint32_t timeout;
uint32_t mark;
+ uint32_t bkt; /* CT bucket number. */
};
enum {
};
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);
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;
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);
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;
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);
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 error;
+
+ if (argc > 1 && !strncmp(argv[argc - 1], CT_BKTS_GT, strlen(CT_BKTS_GT))) {
+ if (ovs_scan(argv[argc - 1], CT_BKTS_GT"%"SCNu16, >)) {
+ argc--;
+ }
+ }
+
+ name = (argc == 2) ? 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);
+ dpif_close(dpif);
+ return 0;
+ }
+
+ dpctl_print(dpctl_p, "Total Buckets: %d\n", tot_bkts);
+
+ int tot_conn = 0;
+ uint32_t *conn_per_bkts = xzalloc(tot_bkts * sizeof(uint32_t));
+
+ 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);
+ free(conn_per_bkts);
+ return error;
+}
\f
/* Undocumented commands for unit testing. */
{ "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 },
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.
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;
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;
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;
* 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);
/* 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;
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;
}
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 *);
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)) {
}
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");
}
"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"