@@ -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
@@ -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)
{
@@ -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);
@@ -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)
@@ -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);
@@ -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 },
@@ -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.
@@ -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,
@@ -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 */
@@ -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 informations. */
+ 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:
*
@@ -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
@@ -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()
@@ -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
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> --- NEWS | 2 + lib/conntrack.c | 66 +++++++++++++++++++++++++++++ lib/conntrack.h | 10 ++++ lib/ct-dpif.c | 87 ++++++++++++++++++++++++++++++++++++++ lib/ct-dpif.h | 15 +++++++ lib/dpctl.c | 49 +++++++++++++++++++++ lib/dpctl.man | 6 +++ lib/dpif-netdev.c | 50 ++++++++++++++++++++++ lib/dpif-netlink.c | 3 + lib/dpif-provider.h | 11 +++++ tests/system-kmod-macros.at | 9 ++++ tests/system-traffic.at | 44 +++++++++++++++++++ tests/system-userspace-macros.at | 6 +++ 13 files changed, 357 insertions(+), 1 deletion(-)