mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 09:58:01 +00:00
dpctl: add CT Stats for Connections per protocol.
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 <antonio.fischetti@intel.com> Signed-off-by: Bhanuprakash Bodireddy <bhanuprakash.bodireddy@intel.com> Co-authored-by: Bhanuprakash Bodireddy <bhanuprakash.bodireddy@intel.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
parent
1401f6deb6
commit
8a0d9d85b3
1
NEWS
1
NEWS
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
@ -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 {
|
||||
@ -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
lib/dpctl.c
139
lib/dpctl.c
@ -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. */
|
||||
|
||||
@ -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 },
|
||||
|
||||
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user