2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +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
allow to get and set, for the userspace datapath, the sweep interval
for the conntrack garbage collector.
* New commands "dpctl/dump-conntrack-exp" that allows to dump
conntrack's expectations for the userspace datapath.
- ovs-ctl:
* Added new options --[ovsdb-server|ovs-vswitchd]-umask=MODE to set umask
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;
}
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
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 *ct;
unsigned bucket;
struct cmap_position cm_pos;
union {
struct cmap_position cm_pos;
struct hmap_position hmap_pos;
};
bool filter_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_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_tuple(struct conntrack *, const struct ct_dpif_tuple *,
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)
: 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. */
@ -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
ct_dpif_format_entry(const struct ct_dpif_entry *entry, struct ds *ds,
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)
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 {
/* Const members. */
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 *);
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_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,
const struct ofp_ct_match *);
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_format_entry(const struct ct_dpif_entry *, struct ds *,
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_tuple(struct ds *, const struct ct_dpif_tuple *);
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;
}
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
dpctl_flush_conntrack(int argc, const char *argv[],
struct dpctl_params *dpctl_p)
@ -2951,6 +2998,8 @@ static const struct dpctl_command all_commands[] = {
0, 1, dpctl_offload_stats_show, DP_RO },
{ "dump-conntrack", "[-m] [-s] [dp] [zone=N]",
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]",
0, 4, dpctl_flush_conntrack, DP_RW },
{ "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.
.
.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]]
Flushes the connection entries in the tracker used by \fIdp\fR based on
\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;
}
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
dpif_netdev_ct_flush(struct dpif *dpif, const uint16_t *zone,
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_next,
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_set_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_next,
dpif_netlink_ct_dump_done,
NULL, /* ct_exp_dump_start */
NULL, /* ct_exp_dump_next */
NULL, /* ct_exp_dump_done */
dpif_netlink_ct_flush,
NULL, /* ct_set_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_entry;
struct ct_dpif_exp;
struct ct_dpif_tuple;
struct ct_dpif_timeout_policy;
enum ct_features;
@ -471,6 +472,16 @@ struct dpif_class {
struct ct_dpif_entry *entry);
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
* following behavior:
*

View File

@ -123,6 +123,15 @@ m4_define([CHECK_CONNTRACK_TIMEOUT],
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()
#
# 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
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_SKIP_IF([test $HAVE_FTP = no])
CHECK_CONNTRACK()

View File

@ -112,6 +112,12 @@ m4_define([CHECK_CONNTRACK_ZEROIP_SNAT])
#
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()
#
# Perform requirements checks for running ovs-dpctl ct-set-maxconns or