2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-30 22:05:19 +00:00

conntrack: Allow to dump userspace conntrack expectations.

The patch introduces a new commands ovs-appctl dpctl/dump-conntrack-exp
that allows to dump the existing expectations for the userspace ct.

Signed-off-by: Paolo Valerio <pvalerio@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
Paolo Valerio
2023-06-23 12:33:43 +02:00
committed by Ilya Maximets
parent 34ace16cb8
commit 9b4d2ad8e8
13 changed files with 357 additions and 1 deletions

2
NEWS
View File

@@ -24,6 +24,8 @@ Post-v3.1.0
* New commands "dpctl/{ct-get-sweep-interval,ct-set-sweep-interval}" that * New commands "dpctl/{ct-get-sweep-interval,ct-set-sweep-interval}" that
allow to get and set, for the userspace datapath, the sweep interval allow to get and set, for the userspace datapath, the sweep interval
for the conntrack garbage collector. for the conntrack garbage collector.
* New commands "dpctl/dump-conntrack-exp" that allows to dump
conntrack's expectations for the userspace datapath.
- ovs-ctl: - ovs-ctl:
* Added new options --[ovsdb-server|ovs-vswitchd]-umask=MODE to set umask * Added new options --[ovsdb-server|ovs-vswitchd]-umask=MODE to set umask
value when starting OVS daemons. E.g., use --ovsdb-server-umask=0002 value when starting OVS daemons. E.g., use --ovsdb-server-umask=0002

View File

@@ -2670,6 +2670,72 @@ conntrack_dump_done(struct conntrack_dump *dump OVS_UNUSED)
return 0; return 0;
} }
static void
exp_node_to_ct_dpif_exp(const struct alg_exp_node *exp,
struct ct_dpif_exp *entry)
{
memset(entry, 0, sizeof *entry);
conn_key_to_tuple(&exp->key, &entry->tuple_orig);
conn_key_to_tuple(&exp->parent_key, &entry->tuple_parent);
entry->zone = exp->key.zone;
entry->mark = exp->parent_mark;
memcpy(&entry->labels, &exp->parent_label, sizeof entry->labels);
entry->protoinfo.proto = exp->key.nw_proto;
}
int
conntrack_exp_dump_start(struct conntrack *ct, struct conntrack_dump *dump,
const uint16_t *pzone)
{
memset(dump, 0, sizeof(*dump));
if (pzone) {
dump->zone = *pzone;
dump->filter_zone = true;
}
dump->ct = ct;
return 0;
}
int
conntrack_exp_dump_next(struct conntrack_dump *dump, struct ct_dpif_exp *entry)
{
struct conntrack *ct = dump->ct;
struct alg_exp_node *enode;
int ret = EOF;
ovs_rwlock_rdlock(&ct->resources_lock);
for (;;) {
struct hmap_node *node = hmap_at_position(&ct->alg_expectations,
&dump->hmap_pos);
if (!node) {
break;
}
enode = CONTAINER_OF(node, struct alg_exp_node, node);
if (!dump->filter_zone || enode->key.zone == dump->zone) {
ret = 0;
exp_node_to_ct_dpif_exp(enode, entry);
break;
}
}
ovs_rwlock_unlock(&ct->resources_lock);
return ret;
}
int
conntrack_exp_dump_done(struct conntrack_dump *dump OVS_UNUSED)
{
return 0;
}
int int
conntrack_flush(struct conntrack *ct, const uint16_t *zone) conntrack_flush(struct conntrack *ct, const uint16_t *zone)
{ {

View File

@@ -100,7 +100,10 @@ void conntrack_clear(struct dp_packet *packet);
struct conntrack_dump { struct conntrack_dump {
struct conntrack *ct; struct conntrack *ct;
unsigned bucket; unsigned bucket;
union {
struct cmap_position cm_pos; struct cmap_position cm_pos;
struct hmap_position hmap_pos;
};
bool filter_zone; bool filter_zone;
uint16_t zone; uint16_t zone;
}; };
@@ -132,6 +135,11 @@ int conntrack_dump_start(struct conntrack *, struct conntrack_dump *,
int conntrack_dump_next(struct conntrack_dump *, struct ct_dpif_entry *); int conntrack_dump_next(struct conntrack_dump *, struct ct_dpif_entry *);
int conntrack_dump_done(struct conntrack_dump *); int conntrack_dump_done(struct conntrack_dump *);
int conntrack_exp_dump_start(struct conntrack *, struct conntrack_dump *,
const uint16_t *);
int conntrack_exp_dump_next(struct conntrack_dump *, struct ct_dpif_exp *);
int conntrack_exp_dump_done(struct conntrack_dump *);
int conntrack_flush(struct conntrack *, const uint16_t *zone); int conntrack_flush(struct conntrack *, const uint16_t *zone);
int conntrack_flush_tuple(struct conntrack *, const struct ct_dpif_tuple *, int conntrack_flush_tuple(struct conntrack *, const struct ct_dpif_tuple *,
uint16_t zone); uint16_t zone);

View File

@@ -101,6 +101,65 @@ ct_dpif_dump_done(struct ct_dpif_dump_state *dump)
? dpif->dpif_class->ct_dump_done(dpif, dump) ? dpif->dpif_class->ct_dump_done(dpif, dump)
: EOPNOTSUPP); : EOPNOTSUPP);
} }
/* Start dumping the expectations from the connection tracker.
*
* 'dump' must be the address of a pointer to a struct ct_dpif_dump_state,
* which should be passed (unaltered) to ct_exp_dpif_dump_{next,done}().
*
* If 'zone' is not NULL, it should point to an integer identifing a
* conntrack zone to which the dump will be limited. If it is NULL,
* conntrack entries from all zones will be dumped.
*
* If there has been a problem the function returns a non-zero value
* that represents the error. Otherwise it returns zero. */
int
ct_exp_dpif_dump_start(struct dpif *dpif, struct ct_dpif_dump_state **dump,
const uint16_t *zone)
{
int err;
err = (dpif->dpif_class->ct_exp_dump_start
? dpif->dpif_class->ct_exp_dump_start(dpif, dump, zone)
: EOPNOTSUPP);
if (!err) {
(*dump)->dpif = dpif;
}
return err;
}
/* Dump one expectation and put it in 'entry'.
*
* 'dump' should have been initialized by ct_exp_dpif_dump_start().
*
* The function returns 0, if an entry has been dumped succesfully.
* Otherwise it returns a non-zero value which can be:
* - EOF: meaning that there are no more entries to dump.
* - an error value.
* In both cases, the user should call ct_exp_dpif_dump_done(). */
int
ct_exp_dpif_dump_next(struct ct_dpif_dump_state *dump,
struct ct_dpif_exp *entry)
{
struct dpif *dpif = dump->dpif;
return (dpif->dpif_class->ct_exp_dump_next
? dpif->dpif_class->ct_exp_dump_next(dpif, dump, entry)
: EOPNOTSUPP);
}
/* Free resources used by 'dump', if any. */
int
ct_exp_dpif_dump_done(struct ct_dpif_dump_state *dump)
{
struct dpif *dpif = dump->dpif;
return (dpif->dpif_class->ct_exp_dump_done
? dpif->dpif_class->ct_exp_dump_done(dpif, dump)
: EOPNOTSUPP);
}
/* Flushing. */ /* Flushing. */
@@ -462,6 +521,34 @@ ct_dpif_status_flags(uint32_t flags)
} }
} }
void
ct_dpif_format_exp_entry(const struct ct_dpif_exp *entry, struct ds *ds)
{
ct_dpif_format_ipproto(ds, entry->tuple_orig.ip_proto);
ds_put_cstr(ds, ",orig=(");
ct_dpif_format_tuple(ds, &entry->tuple_orig);
ds_put_cstr(ds, ")");
if (entry->zone) {
ds_put_format(ds, ",zone=%"PRIu16, entry->zone);
}
if (entry->mark) {
ds_put_format(ds, ",mark=%"PRIu32, entry->mark);
}
if (!ovs_u128_is_zero(entry->labels)) {
ovs_be128 value;
ds_put_cstr(ds, ",labels=");
value = hton128(entry->labels);
ds_put_hex(ds, &value, sizeof value);
}
ds_put_cstr(ds, ",parent=(");
ct_dpif_format_tuple(ds, &entry->tuple_parent);
ds_put_cstr(ds, ")");
}
void void
ct_dpif_format_entry(const struct ct_dpif_entry *entry, struct ds *ds, ct_dpif_format_entry(const struct ct_dpif_entry *entry, struct ds *ds,
bool verbose, bool print_stats) bool verbose, bool print_stats)

View File

@@ -179,6 +179,16 @@ enum ct_dpif_status_flags {
#define CT_DPIF_STATUS_MASK ((CT_DPIF_STATUS_UNTRACKED << 1) - 1) #define CT_DPIF_STATUS_MASK ((CT_DPIF_STATUS_UNTRACKED << 1) - 1)
struct ct_dpif_exp {
struct ct_dpif_tuple tuple_orig;
struct ct_dpif_tuple tuple_parent;
uint16_t zone;
struct ct_dpif_protoinfo protoinfo;
ovs_u128 labels;
uint32_t status;
uint32_t mark;
};
struct ct_dpif_entry { struct ct_dpif_entry {
/* Const members. */ /* Const members. */
struct ct_dpif_tuple tuple_orig; struct ct_dpif_tuple tuple_orig;
@@ -286,6 +296,10 @@ int ct_dpif_dump_start(struct dpif *, struct ct_dpif_dump_state **,
const uint16_t *zone, int *); const uint16_t *zone, int *);
int ct_dpif_dump_next(struct ct_dpif_dump_state *, struct ct_dpif_entry *); 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_dump_done(struct ct_dpif_dump_state *);
int ct_exp_dpif_dump_start(struct dpif *, struct ct_dpif_dump_state **,
const uint16_t *zone);
int ct_exp_dpif_dump_next(struct ct_dpif_dump_state *, struct ct_dpif_exp *);
int ct_exp_dpif_dump_done(struct ct_dpif_dump_state *);
int ct_dpif_flush(struct dpif *, const uint16_t *zone, int ct_dpif_flush(struct dpif *, const uint16_t *zone,
const struct ofp_ct_match *); const struct ofp_ct_match *);
int ct_dpif_set_maxconns(struct dpif *dpif, uint32_t maxconns); int ct_dpif_set_maxconns(struct dpif *dpif, uint32_t maxconns);
@@ -310,6 +324,7 @@ int ct_dpif_ipf_dump_done(struct dpif *dpif, void *);
void ct_dpif_entry_uninit(struct ct_dpif_entry *); void ct_dpif_entry_uninit(struct ct_dpif_entry *);
void ct_dpif_format_entry(const struct ct_dpif_entry *, struct ds *, void ct_dpif_format_entry(const struct ct_dpif_entry *, struct ds *,
bool verbose, bool print_stats); bool verbose, bool print_stats);
void ct_dpif_format_exp_entry(const struct ct_dpif_exp *, struct ds *);
void ct_dpif_format_ipproto(struct ds *ds, uint16_t ipproto); void ct_dpif_format_ipproto(struct ds *ds, uint16_t ipproto);
void ct_dpif_format_tuple(struct ds *, const struct ct_dpif_tuple *); void ct_dpif_format_tuple(struct ds *, const struct ct_dpif_tuple *);
uint8_t ct_dpif_coalesce_tcp_state(uint8_t state); uint8_t ct_dpif_coalesce_tcp_state(uint8_t state);

View File

@@ -1707,6 +1707,53 @@ dpctl_dump_conntrack(int argc, const char *argv[],
return error; return error;
} }
static int
dpctl_dump_conntrack_exp(int argc, const char *argv[],
struct dpctl_params *dpctl_p)
{
struct ct_dpif_dump_state *dump;
uint16_t zone, *pzone = NULL;
struct ct_dpif_exp cte;
struct dpif *dpif;
int error;
if (argc > 1 && ovs_scan(argv[argc - 1], "zone=%"SCNu16, &zone)) {
pzone = &zone;
argc--;
}
error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
if (error) {
return error;
}
error = ct_exp_dpif_dump_start(dpif, &dump, pzone);
if (error) {
dpctl_error(dpctl_p, error, "starting conntrack expectations dump");
dpif_close(dpif);
return error;
}
while (!(error = ct_exp_dpif_dump_next(dump, &cte))) {
struct ds s = DS_EMPTY_INITIALIZER;
ct_dpif_format_exp_entry(&cte, &s);
dpctl_print(dpctl_p, "%s\n", ds_cstr(&s));
ds_destroy(&s);
}
if (error == EOF) {
error = 0;
} else if (error) {
dpctl_error(dpctl_p, error, "dumping conntrack expectation");
}
ct_exp_dpif_dump_done(dump);
dpif_close(dpif);
return error;
}
static int static int
dpctl_flush_conntrack(int argc, const char *argv[], dpctl_flush_conntrack(int argc, const char *argv[],
struct dpctl_params *dpctl_p) struct dpctl_params *dpctl_p)
@@ -2951,6 +2998,8 @@ static const struct dpctl_command all_commands[] = {
0, 1, dpctl_offload_stats_show, DP_RO }, 0, 1, dpctl_offload_stats_show, DP_RO },
{ "dump-conntrack", "[-m] [-s] [dp] [zone=N]", { "dump-conntrack", "[-m] [-s] [dp] [zone=N]",
0, 4, dpctl_dump_conntrack, DP_RO }, 0, 4, dpctl_dump_conntrack, DP_RO },
{ "dump-conntrack-exp", "[dp] [zone=N]",
0, 2, dpctl_dump_conntrack_exp, DP_RO },
{ "flush-conntrack", "[dp] [zone=N] [ct-orig-tuple] [ct-reply-tuple]", { "flush-conntrack", "[dp] [zone=N] [ct-orig-tuple] [ct-reply-tuple]",
0, 4, dpctl_flush_conntrack, DP_RW }, 0, 4, dpctl_flush_conntrack, DP_RW },
{ "cache-get-size", "[dp]", 0, 1, dpctl_cache_get_size, DP_RO }, { "cache-get-size", "[dp]", 0, 1, dpctl_cache_get_size, DP_RO },

View File

@@ -302,6 +302,12 @@ are included. With \fB\-\-statistics\fR timeouts and timestamps are
added to the output. added to the output.
. .
.TP .TP
\*(DX\fBdump\-conntrack\-exp\fR [\fIdp\fR] [\fBzone=\fIzone\fR]
Prints to the console all the expectation entries in the tracker used by
\fIdp\fR. If \fBzone=\fIzone\fR is specified, only shows the expectations
in \fIzone\fR. Only supported for userspace datapath.
.
.TP
\*(DX\fBflush\-conntrack\fR [\fIdp\fR] [\fBzone=\fIzone\fR] [\fIct-origin-tuple\fR [\fIct-reply-tuple\fR]] \*(DX\fBflush\-conntrack\fR [\fIdp\fR] [\fBzone=\fIzone\fR] [\fIct-origin-tuple\fR [\fIct-reply-tuple\fR]]
Flushes the connection entries in the tracker used by \fIdp\fR based on Flushes the connection entries in the tracker used by \fIdp\fR based on
\fIzone\fR and connection tracking tuple \fIct-origin-tuple\fR. \fIzone\fR and connection tracking tuple \fIct-origin-tuple\fR.

View File

@@ -9267,6 +9267,53 @@ dpif_netdev_ct_dump_done(struct dpif *dpif OVS_UNUSED,
return err; return err;
} }
static int
dpif_netdev_ct_exp_dump_start(struct dpif *dpif,
struct ct_dpif_dump_state **dump_,
const uint16_t *pzone)
{
struct dp_netdev *dp = get_dp_netdev(dpif);
struct dp_netdev_ct_dump *dump;
dump = xzalloc(sizeof *dump);
dump->dp = dp;
dump->ct = dp->conntrack;
conntrack_exp_dump_start(dp->conntrack, &dump->dump, pzone);
*dump_ = &dump->up;
return 0;
}
static int
dpif_netdev_ct_exp_dump_next(struct dpif *dpif OVS_UNUSED,
struct ct_dpif_dump_state *dump_,
struct ct_dpif_exp *entry)
{
struct dp_netdev_ct_dump *dump;
INIT_CONTAINER(dump, dump_, up);
return conntrack_exp_dump_next(&dump->dump, entry);
}
static int
dpif_netdev_ct_exp_dump_done(struct dpif *dpif OVS_UNUSED,
struct ct_dpif_dump_state *dump_)
{
struct dp_netdev_ct_dump *dump;
int err;
INIT_CONTAINER(dump, dump_, up);
err = conntrack_exp_dump_done(&dump->dump);
free(dump);
return err;
}
static int static int
dpif_netdev_ct_flush(struct dpif *dpif, const uint16_t *zone, dpif_netdev_ct_flush(struct dpif *dpif, const uint16_t *zone,
const struct ct_dpif_tuple *tuple) const struct ct_dpif_tuple *tuple)
@@ -9679,6 +9726,9 @@ const struct dpif_class dpif_netdev_class = {
dpif_netdev_ct_dump_start, dpif_netdev_ct_dump_start,
dpif_netdev_ct_dump_next, dpif_netdev_ct_dump_next,
dpif_netdev_ct_dump_done, dpif_netdev_ct_dump_done,
dpif_netdev_ct_exp_dump_start,
dpif_netdev_ct_exp_dump_next,
dpif_netdev_ct_exp_dump_done,
dpif_netdev_ct_flush, dpif_netdev_ct_flush,
dpif_netdev_ct_set_maxconns, dpif_netdev_ct_set_maxconns,
dpif_netdev_ct_get_maxconns, dpif_netdev_ct_get_maxconns,

View File

@@ -4566,6 +4566,9 @@ const struct dpif_class dpif_netlink_class = {
dpif_netlink_ct_dump_start, dpif_netlink_ct_dump_start,
dpif_netlink_ct_dump_next, dpif_netlink_ct_dump_next,
dpif_netlink_ct_dump_done, dpif_netlink_ct_dump_done,
NULL, /* ct_exp_dump_start */
NULL, /* ct_exp_dump_next */
NULL, /* ct_exp_dump_done */
dpif_netlink_ct_flush, dpif_netlink_ct_flush,
NULL, /* ct_set_maxconns */ NULL, /* ct_set_maxconns */
NULL, /* ct_get_maxconns */ NULL, /* ct_get_maxconns */

View File

@@ -79,6 +79,7 @@ dpif_flow_dump_thread_init(struct dpif_flow_dump_thread *thread,
struct ct_dpif_dump_state; struct ct_dpif_dump_state;
struct ct_dpif_entry; struct ct_dpif_entry;
struct ct_dpif_exp;
struct ct_dpif_tuple; struct ct_dpif_tuple;
struct ct_dpif_timeout_policy; struct ct_dpif_timeout_policy;
enum ct_features; enum ct_features;
@@ -471,6 +472,16 @@ struct dpif_class {
struct ct_dpif_entry *entry); struct ct_dpif_entry *entry);
int (*ct_dump_done)(struct dpif *, struct ct_dpif_dump_state *state); int (*ct_dump_done)(struct dpif *, struct ct_dpif_dump_state *state);
/* Starts the dump initializing the structures involved and the zone
* filter. */
int (*ct_exp_dump_start)(struct dpif *, struct ct_dpif_dump_state **state,
const uint16_t *zone);
/* Fill the expectation 'entry' with the related information. */
int (*ct_exp_dump_next)(struct dpif *, struct ct_dpif_dump_state *state,
struct ct_dpif_exp *entry);
/* Ends the dump cleaning up any potential pending state, if any. */
int (*ct_exp_dump_done)(struct dpif *, struct ct_dpif_dump_state *state);
/* Flushes the connection tracking tables. The arguments have the /* Flushes the connection tracking tables. The arguments have the
* following behavior: * following behavior:
* *

View File

@@ -123,6 +123,15 @@ m4_define([CHECK_CONNTRACK_TIMEOUT],
on_exit 'modprobe -r nfnetlink_cttimeout' on_exit 'modprobe -r nfnetlink_cttimeout'
]) ])
# CHECK_CONNTRACK_DUMP_EXPECTATIONS()
#
# Perform requirements checks for dumping conntrack expectations.
#
m4_define([CHECK_CONNTRACK_DUMP_EXPECTATIONS],
[
AT_SKIP_IF([:])
])
# CHECK_CT_DPIF_SET_GET_MAXCONNS() # CHECK_CT_DPIF_SET_GET_MAXCONNS()
# #
# Perform requirements checks for running ovs-dpctl ct-set-maxconns or # Perform requirements checks for running ovs-dpctl ct-set-maxconns or

View File

@@ -5195,6 +5195,50 @@ tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=
OVS_TRAFFIC_VSWITCHD_STOP OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP AT_CLEANUP
AT_SETUP([conntrack - FTP with expectation dump])
AT_SKIP_IF([test $HAVE_FTP = no])
CHECK_CONNTRACK()
CHECK_CONNTRACK_ALG()
CHECK_CONNTRACK_DUMP_EXPECTATIONS()
OVS_TRAFFIC_VSWITCHD_START()
ADD_NAMESPACES(at_ns0, at_ns1)
ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
AT_DATA([flows.txt], [dnl
table=0,priority=1,action=drop
table=0,priority=10,arp,action=normal
table=0,priority=10,icmp,action=normal
table=0,priority=100,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,priority=100,in_port=2,tcp,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=1
])
AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows.txt])
OVS_START_L7([at_ns1], [ftp])
dnl FTP requests from p0->p1 should work fine.
NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.2 --no-passive-ftp -t 3 -T 1 --retry-connrefused -v -o wget0.log])
AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>),helper=ftp
])
dnl Verify that a dump with zero entries in a zone doesn't return any entry.
AT_CHECK([ovs-appctl dpctl/dump-conntrack-exp zone=42], [0], [dnl
])
AT_CHECK([ovs-appctl dpctl/dump-conntrack-exp | FORMAT_CT(10.1.1.2)], [0], [dnl
tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),parent=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>)
])
OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP
AT_SETUP([conntrack - FTP over IPv6]) AT_SETUP([conntrack - FTP over IPv6])
AT_SKIP_IF([test $HAVE_FTP = no]) AT_SKIP_IF([test $HAVE_FTP = no])
CHECK_CONNTRACK() CHECK_CONNTRACK()

View File

@@ -112,6 +112,12 @@ m4_define([CHECK_CONNTRACK_ZEROIP_SNAT])
# #
m4_define([CHECK_CONNTRACK_TIMEOUT]) m4_define([CHECK_CONNTRACK_TIMEOUT])
# CHECK_CONNTRACK_DUMP_EXPECTATIONS()
#
# Perform requirements checks for dumping conntrack expectations.
#
m4_define([CHECK_CONNTRACK_DUMP_EXPECTATIONS])
# CHECK_CT_DPIF_SET_GET_MAXCONNS() # CHECK_CT_DPIF_SET_GET_MAXCONNS()
# #
# Perform requirements checks for running ovs-dpctl ct-set-maxconns or # Perform requirements checks for running ovs-dpctl ct-set-maxconns or