Skip to content

Commit

Permalink
dpctl: add CT Stats for Connections per protocol.
Browse files Browse the repository at this point in the history
Adds CT stats to report number of connections grouped by
protocol.
By using
 utilities/ovs-appctl dpctl/ct-stats-show
it can display something like:
Connections Stats:
    Total: 1808
        TCP: 1808

With the verbose options:
 utilities/ovs-appctl dpctl/ct-stats-show verbose
it can display:
Connections Stats:
    Total: 2671
        TCP: 2671
          Conn per TCP states:
          [ESTABLISHED]=1000
          [CLOSING]=1
          [TIME_WAIT]=1670

Signed-off-by: Antonio Fischetti <[email protected]>
Signed-off-by: Bhanuprakash Bodireddy <[email protected]>
Co-authored-by: Bhanuprakash Bodireddy <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
2 people authored and blp committed Jul 11, 2017
1 parent 1401f6d commit 8a0d9d8
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 2 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Post-v2.7.0
default it always accepts names and in interactive use it displays them;
use --names or --no-names to override. See ovs-ofctl(8) for details.
* "ovs-ofctl dump-flows" now accepts --no-stats to omit flow statistics.
- New ovs-dpctl command "ct-stats-show" to show connection tracking stats.
- Tunnels:
* Added support to set packet mark for tunnel endpoint using
`egress_pkt_mark` OVSDB option.
Expand Down
14 changes: 14 additions & 0 deletions lib/ct-dpif.c
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,17 @@ ct_dpif_format_helper(struct ds *ds, const char *title,
ds_put_cstr(ds, helper->name);
}
}

uint8_t
ct_dpif_coalesce_tcp_state(uint8_t state)
{
return coalesce_tcp_state(state);
}

void
ct_dpif_format_tcp_stat(struct ds * ds, int tcp_state, int conn_per_state)
{
ct_dpif_format_enum(ds, "\t [", tcp_state, ct_dpif_tcp_state_string);
ds_put_cstr(ds, "]");
ds_put_format(ds, "=%u", conn_per_state);
}
18 changes: 17 additions & 1 deletion lib/ct-dpif.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ struct ct_dpif_timestamp {
CT_DPIF_TCP_STATE(CLOSING) \
CT_DPIF_TCP_STATE(LAST_ACK) \
CT_DPIF_TCP_STATE(FIN_WAIT_2) \
CT_DPIF_TCP_STATE(TIME_WAIT)
CT_DPIF_TCP_STATE(TIME_WAIT) \
CT_DPIF_TCP_STATE(MAX_NUM)

enum ct_dpif_tcp_state {
#define CT_DPIF_TCP_STATE(STATE) CT_DPIF_TCPS_##STATE,
Expand Down Expand Up @@ -170,6 +171,19 @@ struct ct_dpif_entry {
uint32_t mark;
};

enum {
CT_STATS_UDP,
CT_STATS_TCP,
CT_STATS_SCTP,
CT_STATS_ICMP,
CT_STATS_ICMPV6,
CT_STATS_UDPLITE,
CT_STATS_DCCP,
CT_STATS_IGMP,
CT_STATS_OTHER,
CT_STATS_MAX,
};

struct dpif;

struct ct_dpif_dump_state {
Expand All @@ -185,5 +199,7 @@ void ct_dpif_entry_uninit(struct ct_dpif_entry *);
void ct_dpif_format_entry(const struct ct_dpif_entry *, struct ds *,
bool verbose, bool print_stats);
void ct_dpif_format_tuple(struct ds *, const struct ct_dpif_tuple *);
uint8_t ct_dpif_coalesce_tcp_state(uint8_t state);
void ct_dpif_format_tcp_stat(struct ds *, int, int);

#endif /* CT_DPIF_H */
139 changes: 138 additions & 1 deletion lib/dpctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,142 @@ dpctl_flush_conntrack(int argc, const char *argv[],
dpif_close(dpif);
return error;
}

static int
dpctl_ct_stats_show(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 zone, *pzone = NULL;
bool verbose = false;
int lastargc = 0;

int proto_stats[CT_STATS_MAX];
int tcp_conn_per_states[CT_DPIF_TCPS_MAX_NUM];
int error;

while (argc > 1 && lastargc != argc) {
lastargc = argc;
if (!strncmp(argv[argc - 1], "verbose", 7)) {
verbose = true;
argc--;
} else if (!strncmp(argv[argc - 1], "zone=", 5)) {
if (ovs_scan(argv[argc - 1], "zone=%"SCNu16, &zone)) {
pzone = &zone;
argc--;
}
}
}

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;
}

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);
if (error) {
dpctl_error(dpctl_p, error, "starting conntrack dump");
dpif_close(dpif);
return error;
}

int tot_conn = 0;
while (!ct_dpif_dump_next(dump, &cte)) {
ct_dpif_entry_uninit(&cte);
tot_conn++;
switch (cte.tuple_orig.ip_proto) {
case IPPROTO_ICMP:
proto_stats[CT_STATS_ICMP]++;
break;
case IPPROTO_ICMPV6:
proto_stats[CT_STATS_ICMPV6]++;
break;
case IPPROTO_TCP:
proto_stats[CT_STATS_TCP]++;
uint8_t tcp_state;
/* We keep two separate tcp states, but we print just one. The
* Linux kernel connection tracker internally keeps only one state,
* so 'state_orig' and 'state_reply', will be the same. */
tcp_state = MAX(cte.protoinfo.tcp.state_orig,
cte.protoinfo.tcp.state_reply);
tcp_state = ct_dpif_coalesce_tcp_state(tcp_state);
tcp_conn_per_states[tcp_state]++;
break;
case IPPROTO_UDP:
proto_stats[CT_STATS_UDP]++;
break;
case IPPROTO_SCTP:
proto_stats[CT_STATS_SCTP]++;
break;
case IPPROTO_UDPLITE:
proto_stats[CT_STATS_UDPLITE]++;
break;
case IPPROTO_DCCP:
proto_stats[CT_STATS_DCCP]++;
break;
case IPPROTO_IGMP:
proto_stats[CT_STATS_IGMP]++;
break;
default:
proto_stats[CT_STATS_OTHER]++;
break;
}
}

dpctl_print(dpctl_p, "Connections Stats:\n Total: %d\n", tot_conn);
if (proto_stats[CT_STATS_TCP]) {
dpctl_print(dpctl_p, "\tTCP: %d\n", proto_stats[CT_STATS_TCP]);
if (verbose) {
dpctl_print(dpctl_p, "\t Conn per TCP states:\n");
for (int i = 0; i < CT_DPIF_TCPS_MAX_NUM; i++) {
if (tcp_conn_per_states[i]) {
struct ds s = DS_EMPTY_INITIALIZER;
ct_dpif_format_tcp_stat(&s, i, tcp_conn_per_states[i]);
dpctl_print(dpctl_p, "%s\n", ds_cstr(&s));
ds_destroy(&s);
}
}
}
}
if (proto_stats[CT_STATS_UDP]) {
dpctl_print(dpctl_p, "\tUDP: %d\n", proto_stats[CT_STATS_UDP]);
}
if (proto_stats[CT_STATS_UDPLITE]) {
dpctl_print(dpctl_p, "\tUDPLITE: %d\n", proto_stats[CT_STATS_UDPLITE]);
}
if (proto_stats[CT_STATS_SCTP]) {
dpctl_print(dpctl_p, "\tSCTP: %d\n", proto_stats[CT_STATS_SCTP]);
}
if (proto_stats[CT_STATS_ICMP]) {
dpctl_print(dpctl_p, "\tICMP: %d\n", proto_stats[CT_STATS_ICMP]);
}
if (proto_stats[CT_STATS_DCCP]) {
dpctl_print(dpctl_p, "\tDCCP: %d\n", proto_stats[CT_STATS_DCCP]);
}
if (proto_stats[CT_STATS_IGMP]) {
dpctl_print(dpctl_p, "\tIGMP: %d\n", proto_stats[CT_STATS_IGMP]);
}
if (proto_stats[CT_STATS_OTHER]) {
dpctl_print(dpctl_p, "\tOther: %d\n", proto_stats[CT_STATS_OTHER]);
}

ct_dpif_dump_done(dump);
dpif_close(dpif);
return error;
}

/* Undocumented commands for unit testing. */

Expand Down Expand Up @@ -1622,6 +1758,8 @@ static const struct dpctl_command all_commands[] = {
{ "del-flows", "[dp]", 0, 1, dpctl_del_flows, DP_RW },
{ "dump-conntrack", "[dp] [zone=N]", 0, 2, dpctl_dump_conntrack, DP_RO },
{ "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 },
{ "help", "", 0, INT_MAX, dpctl_help, DP_RO },
{ "list-commands", "", 0, INT_MAX, dpctl_list_commands, DP_RO },

Expand All @@ -1645,7 +1783,6 @@ int
dpctl_run_command(int argc, const char *argv[], struct dpctl_params *dpctl_p)
{
const struct dpctl_command *p;

if (argc < 1) {
dpctl_error(dpctl_p, 0, "missing command name; use --help for help");
return EINVAL;
Expand Down
7 changes: 7 additions & 0 deletions lib/dpctl.man
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,10 @@ added to the output.
Flushes all the connection entries in the tracker used by \fIdp\fR.
If \fBzone=\fIzone\fR is specified, only flushes the connections in
\fBzone\fR.
.
.TP
\*(DX\fBct\-stats\-show\fR [\fIdp\fR] [\fBzone=\fIzone\fR] [\fBverbose\fR]
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.
2 changes: 2 additions & 0 deletions utilities/ovs-dpctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ usage(void *userdata OVS_UNUSED)
"display conntrack entries for ZONE\n"
" flush-conntrack [DP] [zone=ZONE] " \
"delete all conntrack entries in ZONE\n"
" ct-stats-show [DP] [zone=ZONE] [verbose] " \
"CT connections grouped by protocol\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"
Expand Down

0 comments on commit 8a0d9d8

Please sign in to comment.