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 <[email protected]> --- 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(-) diff --git a/NEWS b/NEWS index 66d5a4ea3..16cdb6933 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/lib/conntrack.c b/lib/conntrack.c index f5ebfa05b..4375c03e2 100644 --- a/lib/conntrack.c +++ b/lib/conntrack.c @@ -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) { diff --git a/lib/conntrack.h b/lib/conntrack.h index 524ec0acb..57d5159b6 100644 --- a/lib/conntrack.h +++ b/lib/conntrack.h @@ -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); diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c index 0c4b2964f..f59c6e560 100644 --- a/lib/ct-dpif.c +++ b/lib/ct-dpif.c @@ -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) diff --git a/lib/ct-dpif.h b/lib/ct-dpif.h index 5579ac925..0b728b529 100644 --- a/lib/ct-dpif.h +++ b/lib/ct-dpif.h @@ -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); diff --git a/lib/dpctl.c b/lib/dpctl.c index 15950bd50..4394653ab 100644 --- a/lib/dpctl.c +++ b/lib/dpctl.c @@ -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 }, diff --git a/lib/dpctl.man b/lib/dpctl.man index d448596d3..66fc50903 100644 --- a/lib/dpctl.man +++ b/lib/dpctl.man @@ -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. diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index abe63412e..feab15d21 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -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, diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index 60bd39643..9194971d3 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -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 */ diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h index a33c6ec30..db1f063e0 100644 --- a/lib/dpif-provider.h +++ b/lib/dpif-provider.h @@ -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: * diff --git a/tests/system-kmod-macros.at b/tests/system-kmod-macros.at index 712925ded..81601390d 100644 --- a/tests/system-kmod-macros.at +++ b/tests/system-kmod-macros.at @@ -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 diff --git a/tests/system-traffic.at b/tests/system-traffic.at index 4c378e1d0..a05ca311c 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -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() diff --git a/tests/system-userspace-macros.at b/tests/system-userspace-macros.at index c1855cbc5..73e0e843b 100644 --- a/tests/system-userspace-macros.at +++ b/tests/system-userspace-macros.at @@ -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 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
