On 12/16/22 13:30, Ales Musil wrote: > Add extension that allows to flush connections from CT > by specifying fields that the connections should be > matched against. This allows to match only some fields > of the connection e.g. source address for orig direrction.
*direction > > Reported-at: https://bugzilla.redhat.com/2120546 > Signed-off-by: Ales Musil <[email protected]> > --- > v5: Add missing usage and man for ovs-ofctl command. > v4: Allow ovs-ofctl flush/conntrack without any zone/tuple. > v3: Rebase on top of master. > v2: Rebase on top of master. > Use suggestion from Ilya. > --- > NEWS | 6 ++ > include/openflow/nicira-ext.h | 30 +++++++ > include/openvswitch/ofp-msgs.h | 4 + > include/openvswitch/ofp-util.h | 4 + > lib/ofp-bundle.c | 1 + > lib/ofp-ct-util.c | 146 +++++++++++++++++++++++++++++++++ > lib/ofp-ct-util.h | 9 ++ > lib/ofp-print.c | 20 +++++ > lib/ofp-util.c | 25 ++++++ > lib/rconn.c | 1 + > ofproto/ofproto-dpif.c | 8 +- > ofproto/ofproto-provider.h | 7 +- > ofproto/ofproto.c | 30 ++++++- > tests/ofp-print.at | 93 +++++++++++++++++++++ > tests/ovs-ofctl.at | 26 ++++++ > tests/system-traffic.at | 116 ++++++++++++++------------ > utilities/ovs-ofctl.8.in | 20 +++++ > utilities/ovs-ofctl.c | 40 +++++++++ > 18 files changed, 528 insertions(+), 58 deletions(-) > > diff --git a/NEWS b/NEWS > index ff8904b02..ac73d6293 100644 > --- a/NEWS > +++ b/NEWS > @@ -16,6 +16,12 @@ Post-v3.0.0 > by specifying 'max-rate' or '[r]stp-path-cost' accordingly. > - ovs-dpctl and related ovs-appctl commands: > * "flush-conntrack" is capable of handling partial 5-tuple. > + - OpenFlow: > + * New OpenFlow extension NXT_CT_FLUSH to flush connections matching > + the specified fields. > + - ovs-ofctl: > + * New command "flush-conntrack" that accepts zone and 5-tuple or partial > + 5-tuple. ovs-ofctl commands supposed to mimic actual OF command names, so, this should be 'ovs-ofctl ct-flush'. > > > v3.0.0 - 15 Aug 2022 > diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h > index b68804991..32ce56d31 100644 > --- a/include/openflow/nicira-ext.h > +++ b/include/openflow/nicira-ext.h > @@ -1064,4 +1064,34 @@ struct nx_zone_id { > }; > OFP_ASSERT(sizeof(struct nx_zone_id) == 8); > > +/* CT flush available TLVs. */ > +enum nx_ct_flush_tlv_type { > + /* Outer types. */ > + NXT_CT_ORIG_DIRECTION, /* CT orig direction outer type. */ > + NXT_CT_REPLY_DIRECTION, /* CT reply direction outer type. */ Do we need both? See my comments on the patch 1/2. > + > + /* Nested types. */ > + NXT_CT_SRC, /* CT source IPv6 or mapped IPv4 address. */ > + NXT_CT_DST, /* CT destination IPv6 or mapped IPv4 address. > */ > + NXT_CT_SRC_PORT, /* CT source port. */ > + NXT_CT_DST_PORT, /* CT destination port. */ > + NXT_CT_ICMP_ID, /* CT ICMP id. */ > + NXT_CT_ICMP_TYPE, /* CT ICMP type. */ > + NXT_CT_ICMP_CODE, /* CT ICMP code. */ I'm not sure about nested types being part of the same enum. If we will need to add one new nested type, we will not be able to add it into the same block here, because we'll have to preserve a value of NXT_CT_ZONE_ID. > + > + /* Primitive types. */ > + NXT_CT_ZONE_ID, /* CT zone id. */ > +}; > + > +/* NXT_CT_FLUSH. > + * > + * Flushes the connection tracking specified by 5-tuple. > + * The struct should be followed by TLVs specifying the matching parameters. > */ > +struct nx_ct_flush { > + uint8_t ip_proto; /* IP protocol. */ > + uint8_t family; /* L3 address family. */ AFAICT, you're expecting users to put a numerical value of AF_INET or AF_INET6 in this field. However, these values are non-standard. And they are different on different systems. For example, winsdk defines AF_INET6 as 23, while it's 10 on linux. Other systems use different values as well. So, it cannot be used as part of OpenFlow. You need a separate enum for them. Alternatively, we could drop that field from the protocol, because it can be determined by looking at supplied ip addresses, right? > + uint8_t zero[6]; /* Must be zero. */ Name it 'pad' instead. Please, add the 'Followed by' comment here. > +}; > +OFP_ASSERT(sizeof(struct nx_ct_flush) == 8); > + > #endif /* openflow/nicira-ext.h */ > diff --git a/include/openvswitch/ofp-msgs.h b/include/openvswitch/ofp-msgs.h > index 921a937e5..659b0a3e7 100644 > --- a/include/openvswitch/ofp-msgs.h > +++ b/include/openvswitch/ofp-msgs.h > @@ -526,6 +526,9 @@ enum ofpraw { > > /* NXST 1.0+ (4): struct nx_ipfix_stats_reply[]. */ > OFPRAW_NXST_IPFIX_FLOW_REPLY, > + > + /* NXT 1.0+ (32): struct nx_ct_flush, uint8_t[8][]. */ Are you sure these are 8-byte aligned and padded (I didnt' check)? > + OFPRAW_NXT_CT_FLUSH, Order in this enum should not matter. Please, move the definition to the OFPTYPE_CT_FLUSH_ZONE. > }; > > /* Decoding messages into OFPRAW_* values. */ > @@ -772,6 +775,7 @@ enum ofptype { > OFPTYPE_IPFIX_FLOW_STATS_REQUEST, /* OFPRAW_NXST_IPFIX_FLOW_REQUEST */ > OFPTYPE_IPFIX_FLOW_STATS_REPLY, /* OFPRAW_NXST_IPFIX_FLOW_REPLY */ > OFPTYPE_CT_FLUSH_ZONE, /* OFPRAW_NXT_CT_FLUSH_ZONE. */ > + OFPTYPE_CT_FLUSH, /* OFPRAW_NXT_CT_FLUSH. */ Comment might look better if aligned. > > /* Flow monitor extension. */ > OFPTYPE_FLOW_MONITOR_CANCEL, /* OFPRAW_NXT_FLOW_MONITOR_CANCEL. > diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-util.h > index 84937ae26..e10d90b9f 100644 > --- a/include/openvswitch/ofp-util.h > +++ b/include/openvswitch/ofp-util.h > @@ -65,6 +65,10 @@ struct ofpbuf *ofputil_encode_echo_reply(const struct > ofp_header *); > > struct ofpbuf *ofputil_encode_barrier_request(enum ofp_version); > > +struct ofpbuf *ofputil_ct_match_encode(const struct ofputil_ct_match *match, > + uint16_t *zone_id, > + enum ofp_version version); > + > #ifdef __cplusplus > } > #endif > diff --git a/lib/ofp-bundle.c b/lib/ofp-bundle.c > index 0161c2bc6..941a8370e 100644 > --- a/lib/ofp-bundle.c > +++ b/lib/ofp-bundle.c > @@ -292,6 +292,7 @@ ofputil_is_bundlable(enum ofptype type) > case OFPTYPE_IPFIX_FLOW_STATS_REQUEST: > case OFPTYPE_IPFIX_FLOW_STATS_REPLY: > case OFPTYPE_CT_FLUSH_ZONE: > + case OFPTYPE_CT_FLUSH: > break; > } > > diff --git a/lib/ofp-ct-util.c b/lib/ofp-ct-util.c > index a10607590..a46798582 100644 > --- a/lib/ofp-ct-util.c > +++ b/lib/ofp-ct-util.c > @@ -23,8 +23,12 @@ > > #include "ct-dpif.h" > #include "ofp-ct-util.h" > +#include "openflow/nicira-ext.h" > #include "openvswitch/dynamic-string.h" > +#include "openvswitch/ofp-msgs.h" > #include "openvswitch/ofp-parse.h" > +#include "openvswitch/ofp-errors.h" > +#include "openvswitch/ofp-prop.h" > #include "openvswitch/ofp-util.h" > #include "openvswitch/packets.h" > > @@ -324,3 +328,145 @@ error: > free(copy); > return false; > } > + > +static enum ofperr > +ofpprop_pull_value(struct ofpbuf *property, void *value, size_t len) > +{ > + if (ofpbuf_msgsize(property) < len) { > + return OFPERR_OFPBPC_BAD_LEN; > + } > + > + memcpy(value, property->msg, len); > + > + return 0; > +} > + > +static enum ofperr > +ofputil_ct_tuple_decode_nested(struct ofpbuf *property, > + struct ofputil_ct_tuple *tuple) > +{ > + struct ofpbuf nested; > + enum ofperr error = ofpprop_parse_nested(property, &nested); > + if (error) { > + return error; > + } > + > + while (nested.size) { > + struct ofpbuf inner; > + uint64_t type; > + > + error = ofpprop_pull(&nested, &inner, &type); > + if (error) { > + return error; > + } > + switch (type) { > + case NXT_CT_SRC: > + ofpprop_pull_value(&inner, &tuple->src, sizeof tuple->src); > + break; Case should be on the same level as a switch. And there should be an empty line after the break. See the coding style document for example. > + case NXT_CT_DST: > + ofpprop_pull_value(&inner, &tuple->dst, sizeof tuple->dst); > + break; > + case NXT_CT_SRC_PORT: > + ofpprop_parse_be16(&inner, &tuple->src_port); > + break; > + case NXT_CT_DST_PORT: > + ofpprop_parse_be16(&inner, &tuple->dst_port); > + break; > + case NXT_CT_ICMP_ID: > + ofpprop_parse_be16(&inner, &tuple->icmp_id); > + break; > + case NXT_CT_ICMP_TYPE: > + ofpprop_parse_u8(&inner, &tuple->icmp_type); > + break; > + case NXT_CT_ICMP_CODE: > + ofpprop_parse_u8(&inner, &tuple->icmp_code); > + } Should we check that tuple src and dst are from the same family? Do we need to check for duplicates? > + } > + > + return 0; > +} > + > +enum ofperr > +ofputil_ct_match_decode(struct ofputil_ct_match *match, bool *with_zone, > + uint16_t *zone_id, const struct ofp_header *oh) > +{ > + struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length)); > + ofpraw_pull_assert(&msg); > + > + const struct nx_ct_flush *nx_flush = ofpbuf_pull(&msg, sizeof *nx_flush); > + > + if (!is_all_zeros(nx_flush->zero, sizeof nx_flush->zero)) { > + return OFPERR_NXBRC_MUST_BE_ZERO; > + } > + > + match->l3_type = nx_flush->family; > + match->ip_proto = nx_flush->ip_proto; > + > + struct ofputil_ct_tuple *orig = &match->tuple_orig; > + struct ofputil_ct_tuple *reply = &match->tuple_reply; > + > + while (msg.size) { > + struct ofpbuf property; > + uint64_t type; > + > + enum ofperr error = ofpprop_pull(&msg, &property, &type); > + if (error) { > + return error; > + } > + > + switch (type) { > + case NXT_CT_ORIG_DIRECTION: > + ofputil_ct_tuple_decode_nested(&property, orig); > + break; Same here. > + case NXT_CT_REPLY_DIRECTION: > + ofputil_ct_tuple_decode_nested(&property, reply); > + break; > + case NXT_CT_ZONE_ID: > + if (with_zone) { > + *with_zone = true; > + } > + ofpprop_parse_u16(&property, zone_id); > + break; > + } > + } > + > + return 0; > +} > + > +void > +ofputil_ct_tuple_encode(const struct ofputil_ct_tuple *tuple, > + struct ofpbuf *buf, enum nx_ct_flush_tlv_type type, > + uint8_t ip_proto) > +{ > + /* 128 B is enough to hold the whole tuple. */ Maybe use sizeof instead? > + uint8_t stub[128]; > + struct ofpbuf nested = OFPBUF_STUB_INITIALIZER(stub); > + > + if (!ipv6_is_zero(&tuple->src)) { > + ofpprop_put(&nested, NXT_CT_SRC, &tuple->src, sizeof tuple->src); > + } > + > + if (!ipv6_is_zero(&tuple->dst)) { > + ofpprop_put(&nested, NXT_CT_DST, &tuple->dst, sizeof tuple->dst); > + } > + > + if (ip_proto == IPPROTO_ICMP || ip_proto == IPPROTO_ICMPV6) { > + ofpprop_put_be16(&nested, NXT_CT_ICMP_ID, tuple->icmp_id); > + ofpprop_put_u8(&nested, NXT_CT_ICMP_TYPE, tuple->icmp_type); > + ofpprop_put_u8(&nested, NXT_CT_ICMP_CODE, tuple->icmp_code); > + } else { > + if (tuple->src_port) { > + ofpprop_put_be16(&nested, NXT_CT_SRC_PORT, tuple->src_port); > + } > + > + if (tuple->dst_port) { > + ofpprop_put_be16(&nested, NXT_CT_DST_PORT, tuple->dst_port); > + } > + } > + > + if (nested.size) { > + ofpprop_put_nested(buf, type, &nested); > + } > + > + ofpbuf_uninit(&nested); > +} > diff --git a/lib/ofp-ct-util.h b/lib/ofp-ct-util.h > index 5a5eaf920..f4e4f812e 100644 > --- a/lib/ofp-ct-util.h > +++ b/lib/ofp-ct-util.h > @@ -17,6 +17,7 @@ > #define OVS_OFP_CT_UTIL_H > > #include "ct-dpif.h" > +#include "openflow/nicira-ext.h" > #include "openvswitch/ofp-util.h" > > bool ofputil_ct_match_cmp(const struct ofputil_ct_match *match, > @@ -33,4 +34,12 @@ bool ofputil_ct_match_parse(struct ofputil_ct_match > *match, const char *s, > > bool ofputil_is_ct_match_zero(const struct ofputil_ct_match *match); > > +enum ofperr ofputil_ct_match_decode(struct ofputil_ct_match *match, > + bool *with_zone, uint16_t *zone_id, > + const struct ofp_header *oh); > + > +void ofputil_ct_tuple_encode(const struct ofputil_ct_tuple *tuple, > + struct ofpbuf *buf, > + enum nx_ct_flush_tlv_type type, uint8_t > ip_proto); > + > #endif /* lib/ofp-ct-util.h */ > diff --git a/lib/ofp-print.c b/lib/ofp-print.c > index bd37fa17a..94bfa7070 100644 > --- a/lib/ofp-print.c > +++ b/lib/ofp-print.c > @@ -36,6 +36,7 @@ > #include "learn.h" > #include "multipath.h" > #include "netdev.h" > +#include "ofp-ct-util.h" > #include "nx-match.h" > #include "odp-util.h" > #include "openflow/nicira-ext.h" > @@ -949,6 +950,23 @@ ofp_print_nxt_ct_flush_zone(struct ds *string, const > struct nx_zone_id *nzi) > return 0; > } > > +static enum ofperr > +ofp_print_nxt_ct_flush(struct ds *string, const struct ofp_header *oh) > +{ > + uint16_t zone_id = 0; > + struct ofputil_ct_match match = {0}; > + > + enum ofperr error = ofputil_ct_match_decode(&match, NULL, &zone_id, oh); > + if (error) { > + return error; > + } > + > + ofputil_ct_match_format(string, &match); > + ds_put_format(string, ",zone_id=%"PRIu16, zone_id); > + > + return 0; > +} > + > static enum ofperr > ofp_to_string__(const struct ofp_header *oh, > const struct ofputil_port_map *port_map, > @@ -1184,6 +1202,8 @@ ofp_to_string__(const struct ofp_header *oh, > > case OFPTYPE_CT_FLUSH_ZONE: > return ofp_print_nxt_ct_flush_zone(string, ofpmsg_body(oh)); > + case OFPTYPE_CT_FLUSH: > + return ofp_print_nxt_ct_flush(string, oh); > } > > return 0; > diff --git a/lib/ofp-util.c b/lib/ofp-util.c > index a324ceeea..a14cb6860 100644 > --- a/lib/ofp-util.c > +++ b/lib/ofp-util.c > @@ -31,6 +31,7 @@ > #include "multipath.h" > #include "netdev.h" > #include "nx-match.h" > +#include "ofp-ct-util.h" > #include "id-pool.h" > #include "openflow/netronome-ext.h" > #include "openvswitch/dynamic-string.h" > @@ -237,3 +238,27 @@ ofputil_encode_barrier_request(enum ofp_version > ofp_version) > > return ofpraw_alloc(type, ofp_version, 0); > } > + > +struct ofpbuf * > +ofputil_ct_match_encode(const struct ofputil_ct_match *match, > + uint16_t *zone_id, enum ofp_version version) Why this function is in internal header? Surely OVN will want to use it. > +{ > + struct ofpbuf *msg = ofpraw_alloc(OFPRAW_NXT_CT_FLUSH, version, 0); > + struct nx_ct_flush *nx_flush = ofpbuf_put_zeros(msg, sizeof *nx_flush); > + const struct ofputil_ct_tuple *orig = &match->tuple_orig; > + const struct ofputil_ct_tuple *reply = &match->tuple_reply; > + > + nx_flush->ip_proto = match->ip_proto; > + nx_flush->family = match->l3_type; > + > + ofputil_ct_tuple_encode(orig, msg, NXT_CT_ORIG_DIRECTION, > + match->ip_proto); > + ofputil_ct_tuple_encode(reply, msg, NXT_CT_REPLY_DIRECTION, > + match->ip_proto); > + > + if (zone_id) { > + ofpprop_put_u16(msg, NXT_CT_ZONE_ID, *zone_id); > + } > + > + return msg; > +} > diff --git a/lib/rconn.c b/lib/rconn.c > index a96b2eb8b..4afa21515 100644 > --- a/lib/rconn.c > +++ b/lib/rconn.c > @@ -1426,6 +1426,7 @@ is_admitted_msg(const struct ofpbuf *b) > case OFPTYPE_IPFIX_FLOW_STATS_REQUEST: > case OFPTYPE_IPFIX_FLOW_STATS_REPLY: > case OFPTYPE_CT_FLUSH_ZONE: > + case OFPTYPE_CT_FLUSH: > default: > return true; > } > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > index f9562dee8..29174a585 100644 > --- a/ofproto/ofproto-dpif.c > +++ b/ofproto/ofproto-dpif.c > @@ -5358,11 +5358,12 @@ type_set_config(const char *type, const struct smap > *other_config) > } > > static void > -ct_flush(const struct ofproto *ofproto_, const uint16_t *zone) > +ct_flush(const struct ofproto *ofproto_, const uint16_t *zone, > + const struct ofputil_ct_match *match) > { > struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); > > - ct_dpif_flush(ofproto->backer->dpif, zone, NULL); > + ct_dpif_flush(ofproto->backer->dpif, zone, match); > } > > static struct ct_timeout_policy * > @@ -5674,6 +5675,9 @@ get_datapath_cap(const char *datapath_type, struct smap > *cap) > smap_add(cap, "lb_output_action", s.lb_output_action ? "true" : "false"); > smap_add(cap, "ct_zero_snat", s.ct_zero_snat ? "true" : "false"); > smap_add(cap, "add_mpls", s.add_mpls ? "true" : "false"); > + /* The ct_tuple_flush is implemented on dpif level, so it is supported > + * for all backers. */ > + smap_add(cap, "ct_flush", "true"); > } > > /* Gets timeout policy name in 'backer' based on 'zone', 'dl_type' and > diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h > index 7e3fb6698..5e39234f9 100644 > --- a/ofproto/ofproto-provider.h > +++ b/ofproto/ofproto-provider.h > @@ -49,6 +49,7 @@ > #include "openvswitch/ofp-port.h" > #include "openvswitch/ofp-switch.h" > #include "openvswitch/ofp-table.h" > +#include "openvswitch/ofp-util.h" > #include "ovs-atomic.h" > #include "ovs-rcu.h" > #include "ovs-thread.h" > @@ -1902,8 +1903,10 @@ struct ofproto_class { > /* ## Connection tracking ## */ > /* ## ------------------- ## */ > /* Flushes the connection tracking tables. If 'zone' is not NULL, > - * only deletes connections in '*zone'. */ > - void (*ct_flush)(const struct ofproto *, const uint16_t *zone); > + * only deletes connections in '*zone'. If 'match' is not NULL, > + * deletes connections specified by the match. */ > + void (*ct_flush)(const struct ofproto *, const uint16_t *zone, > + const struct ofputil_ct_match *match); > > /* Sets conntrack timeout policy specified by 'timeout_policy' to 'zone' > * in datapath type 'dp_type'. */ > diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c > index 3a527683c..c9b222994 100644 > --- a/ofproto/ofproto.c > +++ b/ofproto/ofproto.c > @@ -34,6 +34,7 @@ > #include "openvswitch/hmap.h" > #include "netdev.h" > #include "nx-match.h" > +#include "ofp-ct-util.h" > #include "ofproto.h" > #include "ofproto-provider.h" > #include "openflow/nicira-ext.h" > @@ -934,7 +935,31 @@ handle_nxt_ct_flush_zone(struct ofconn *ofconn, const > struct ofp_header *oh) > > uint16_t zone = ntohs(nzi->zone_id); > if (ofproto->ofproto_class->ct_flush) { > - ofproto->ofproto_class->ct_flush(ofproto, &zone); > + ofproto->ofproto_class->ct_flush(ofproto, &zone, NULL); > + } else { > + return EOPNOTSUPP; > + } > + > + return 0; > +} > + > +static enum ofperr > +handle_nxt_ct_flush(struct ofconn *ofconn, const struct ofp_header *oh) > +{ > + struct ofproto *ofproto = ofconn_get_ofproto(ofconn); > + struct ofputil_ct_match match = {0}; > + bool with_zone = false; > + uint16_t zone_id = 0; > + > + enum ofperr error = > + ofputil_ct_match_decode(&match, &with_zone, &zone_id, oh); > + if (error) { > + return error; > + } > + > + if (ofproto->ofproto_class->ct_flush) { > + ofproto->ofproto_class->ct_flush(ofproto, with_zone ? &zone_id : > NULL, > + &match); > } else { > return EOPNOTSUPP; > } > @@ -8787,6 +8812,9 @@ handle_single_part_openflow(struct ofconn *ofconn, > const struct ofp_header *oh, > case OFPTYPE_CT_FLUSH_ZONE: > return handle_nxt_ct_flush_zone(ofconn, oh); > > + case OFPTYPE_CT_FLUSH: > + return handle_nxt_ct_flush(ofconn, oh); > + > case OFPTYPE_HELLO: > case OFPTYPE_ERROR: > case OFPTYPE_FEATURES_REPLY: > diff --git a/tests/ofp-print.at b/tests/ofp-print.at > index fe41cc42c..7c6a86133 100644 > --- a/tests/ofp-print.at > +++ b/tests/ofp-print.at > @@ -4073,3 +4073,96 @@ AT_CHECK([ovs-ofctl ofp-print "\ > NXT_CT_FLUSH_ZONE (xid=0x3): zone_id=13 > ]) > AT_CLEANUP > + > +AT_SETUP([NXT_CT_FLUSH]) > +AT_KEYWORDS([ofp-print]) > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 18 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +02 \ > +00 00 00 00 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): > l3_type=2,ip_proto=6,orig=(src=::,dst=::,src_port=0,dst_port=0),reply=(src=::,dst=::,src_port=0,dst_port=0),zone_id=0 > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 20 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +02 \ > +00 00 00 00 00 00 \ > +00 09 00 08 00 0d 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): > l3_type=2,ip_proto=6,orig=(src=::,dst=::,src_port=0,dst_port=0),reply=(src=::,dst=::,src_port=0,dst_port=0),zone_id=13 > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 68 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +02 \ > +00 00 00 00 00 00 \ > +00 09 00 08 00 0d 00 00 \ > +00 00 00 48 00 00 00 00 \ > +00 02 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ > +00 03 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ > +00 04 00 08 00 50 00 00 \ > +00 05 00 08 1f 90 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): > l3_type=2,ip_proto=6,orig=(src=10.10.0.1,dst=10.10.0.2,src_port=80,dst_port=8080),reply=(src=::,dst=::,src_port=0,dst_port=0),zone_id=13 > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 68 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +02 \ > +00 00 00 00 00 00 \ > +00 09 00 08 00 0d 00 00 \ > +00 01 00 48 00 00 00 00 \ > +00 03 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ > +00 02 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ > +00 05 00 08 00 50 00 00 \ > +00 04 00 08 1f 90 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): > l3_type=2,ip_proto=6,orig=(src=::,dst=::,src_port=0,dst_port=0),reply=(src=10.10.0.2,dst=10.10.0.1,src_port=8080,dst_port=80),zone_id=13 > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 b0 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +02 \ > +00 00 00 00 00 00 \ > +00 09 00 08 00 0d 00 00 \ > +00 00 00 48 00 00 00 00 \ > +00 02 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ > +00 03 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ > +00 04 00 08 00 50 00 00 \ > +00 05 00 08 1f 90 00 00 \ > +00 01 00 48 00 00 00 00 \ > +00 03 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ > +00 02 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ > +00 05 00 08 00 50 00 00 \ > +00 04 00 08 1f 90 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): > l3_type=2,ip_proto=6,orig=(src=10.10.0.1,dst=10.10.0.2,src_port=80,dst_port=8080),reply=(src=10.10.0.2,dst=10.10.0.1,src_port=8080,dst_port=80),zone_id=13 > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 b8 00 00 00 03 00 00 23 20 00 00 00 20 \ > +01 \ > +0a \ > +00 00 00 00 00 00 \ > +00 00 00 50 00 00 00 00 \ > +00 02 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 01 00 00 00 00 \ > +00 03 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 02 00 00 00 00 \ > +00 06 00 08 00 0a 00 00 \ > +00 07 00 05 01 00 00 00 \ > +00 08 00 05 02 00 00 00 \ > +00 01 00 50 00 00 00 00 \ > +00 02 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 02 00 00 00 00 \ > +00 02 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 01 00 00 00 00 \ > +00 06 00 08 00 0a 00 00 \ > +00 07 00 05 03 00 00 00 \ > +00 08 00 05 04 00 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): > l3_type=10,ip_proto=1,orig=(src=fd18::ffff:abcd:1,dst=fd18::ffff:abcd:2,icmp_id=10,icmp_type=1,icmp_code=2),reply=(src=fd18::ffff:abcd:1,dst=::,icmp_id=10,icmp_type=3,icmp_code=4),zone_id=0 > +]) > +AT_CLEANUP > diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at > index a8934051e..a14912768 100644 > --- a/tests/ovs-ofctl.at > +++ b/tests/ovs-ofctl.at > @@ -3271,3 +3271,29 @@ AT_CHECK([ovs-ofctl -O OpenFlow15 dump-flows br0 | > ofctl_strip | sed '/OFPST_FLO > > OVS_VSWITCHD_STOP(["/Flow exceeded the maximum flow statistics reply size > and was excluded from the response set/d"]) > AT_CLEANUP > + > +AT_SETUP([ovs-ofctl ct - flush-conntrack]) > +OVS_VSWITCHD_START > + > +AT_CHECK([ovs-appctl vlog/set ct_dpif:dbg]) > + > +# Check flush conntrack with both zone and tuple > +AT_CHECK([ovs-ofctl flush-conntrack br0 zone=5 > 'ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1']) > + > +OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) > -eq 1]) > +AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: > l3_type=2,ip_proto=17,orig=(src=10.1.1.1,dst=10.1.1.2,src_port=1,dst_port=0),reply=(src=::,dst=::,src_port=0,dst_port=0) > in zone 5" ovs-vswitchd.log]) > + > +# Check flush-conntrack just with tuple > +AT_CHECK([ovs-ofctl flush-conntrack br0 > 'ct_nw_src=10.1.1.3,ct_nw_dst=10.1.1.4,ct_nw_proto=17,ct_tp_src=1']) > + > +OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) > -eq 2]) > +AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: > l3_type=2,ip_proto=17,orig=(src=10.1.1.3,dst=10.1.1.4,src_port=1,dst_port=0),reply=(src=::,dst=::,src_port=0,dst_port=0) > in zone 0" ovs-vswitchd.log]) > + > +# Check flush-conntrack without any tuple and zone > +AT_CHECK([ovs-ofctl flush-conntrack br0]) > + > +OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" ovs-vswitchd.log) > -eq 3]) > +AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: <all>" ovs-vswitchd.log]) > + > +OVS_VSWITCHD_STOP > +AT_CLEANUP > diff --git a/tests/system-traffic.at b/tests/system-traffic.at > index 51903a658..396d81ad9 100644 > --- a/tests/system-traffic.at > +++ b/tests/system-traffic.at > @@ -2250,126 +2250,136 @@ > priority=100,in_port=2,icmp,action=ct(zone=5,commit),1 > > AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) > > -dnl Test UDP from port 1 > -AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > +flush_conntrack() { > + if [[ "$1" == "dpctl" ]]; then > + AT_CHECK([ovs-appctl dpctl/flush-conntrack ${@:2}]) I'm not sure if '${@:2}' syntax is portable. > + else > + AT_CHECK([ovs-ofctl flush-conntrack br0 ${@:2}]) > + fi > +} > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.1,"], > [], [dnl > +for type in dpctl ofctl; do Might be better to do somethign like: m4_foreach([FLUSH_CMD], [[ovs-appctl dpctl/flush-conntrack], [ovs-ofctl ct-flush br0]], [ Some tests here. ... AT_CHECK([FLUSH_CMD | grep "orig=.src=10\.1\.1\.1,"], [1]) ... ]) > + AS_BOX([Testing with $type command]) > + > + dnl Test UDP from port 1 > + AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > + > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep > "orig=.src=10\.1\.1\.1,"], [], [dnl > > udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1) > ]) > > -AT_CHECK([ovs-appctl dpctl/flush-conntrack > 'ct_nw_src=10.1.1.2,ct_nw_dst=10.1.1.1,ct_nw_proto=17,ct_tp_src=2,ct_tp_dst=1']) > + AT_CHECK([flush_conntrack $type > 'ct_nw_src=10.1.1.2,ct_nw_dst=10.1.1.1,ct_nw_proto=17,ct_tp_src=2,ct_tp_dst=1']) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.1,"], > [1], [dnl > -]) > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep > "orig=.src=10\.1\.1\.1,"], [1]) > > -dnl Test UDP from port 2 > -AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.2,"], > [0], [dnl > + dnl Test UDP from port 2 > + AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > + > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep > "orig=.src=10\.1\.1\.2,"], [0], [dnl > > udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 > ]) > > -AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=5 > 'ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2']) > + AT_CHECK([flush_conntrack $type zone=5 > 'ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2']) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl > -]) > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0]) > > -dnl Test ICMP traffic > -NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -w 2 10.1.1.1 | FORMAT_PING], > [0], [dnl > + dnl Test ICMP traffic > + NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -w 2 10.1.1.1 | > FORMAT_PING], [0], [dnl > 3 packets transmitted, 3 received, 0% packet loss, time 0ms > ]) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.2,"], > [0], [stdout]) > -AT_CHECK([cat stdout | FORMAT_CT(10.1.1.1)], [0],[dnl > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep > "orig=.src=10\.1\.1\.2,"], [0], [stdout]) > + AT_CHECK([cat stdout | FORMAT_CT(10.1.1.1)], [0],[dnl > > icmp,orig=(src=10.1.1.2,dst=10.1.1.1,id=<cleared>,type=8,code=0),reply=(src=10.1.1.1,dst=10.1.1.2,id=<cleared>,type=0,code=0),zone=5 > ]) > > -ICMP_ID=`cat stdout | cut -d ',' -f4 | cut -d '=' -f2` > -ICMP_TUPLE=ct_nw_src=10.1.1.2,ct_nw_dst=10.1.1.1,ct_nw_proto=1,icmp_id=$ICMP_ID,icmp_type=8,icmp_code=0 > -AT_CHECK([ovs-appctl dpctl/flush-conntrack zone=5 $ICMP_TUPLE]) > + ICMP_ID=`cat stdout | cut -d ',' -f4 | cut -d '=' -f2` > + > ICMP_TUPLE=ct_nw_src=10.1.1.2,ct_nw_dst=10.1.1.1,ct_nw_proto=1,icmp_id=$ICMP_ID,icmp_type=8,icmp_code=0 > + AT_CHECK([flush_conntrack $type zone=5 $ICMP_TUPLE]) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.2,"], > [1], [dnl > -]) > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep > "orig=.src=10\.1\.1\.2,"], [1]) > > -dnl Test UDP from port 1 and 2, partial flush by src port > -AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > -AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > + dnl Test UDP from port 1 and 2, partial flush by src port > + AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > + AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sort], [0], > [dnl > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sort], > [0], [dnl > > udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1) > > udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 > ]) > > -AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_nw_proto=17,ct_tp_src=1']) > + AT_CHECK([flush_conntrack $type 'ct_nw_proto=17,ct_tp_src=1']) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [0], [dnl > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [0], > [dnl > > udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 > ]) > > -AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_nw_proto=17,ct_tp_src=2']) > + AT_CHECK([flush_conntrack $type 'ct_nw_proto=17,ct_tp_src=2']) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > > -dnl Test UDP from port 1 and 2, partial flush by dst port > -AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > -AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > + dnl Test UDP from port 1 and 2, partial flush by dst port > + AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > + AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sort], [0], > [dnl > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sort], > [0], [dnl > > udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1) > > udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 > ]) > > -AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_nw_proto=17,ct_tp_dst=2']) > + AT_CHECK([flush_conntrack $type 'ct_nw_proto=17,ct_tp_dst=2']) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [0], [dnl > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [0], > [dnl > > udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 > ]) > > -AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_nw_proto=17,ct_tp_dst=1']) > + AT_CHECK([flush_conntrack $type 'ct_nw_proto=17,ct_tp_dst=1']) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > > -dnl Test UDP from port 1 and 2, partial flush by src address > -AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > -AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > + dnl Test UDP from port 1 and 2, partial flush by src address > + AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > + AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sort], [0], > [dnl > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sort], > [0], [dnl > > udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1) > > udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 > ]) > > -AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_nw_src=10.1.1.1']) > + AT_CHECK([flush_conntrack $type 'ct_nw_src=10.1.1.1']) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [0], [dnl > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [0], > [dnl > > udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 > ]) > > -AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_nw_src=10.1.1.2']) > + AT_CHECK([flush_conntrack $type 'ct_nw_src=10.1.1.2']) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > > -dnl Test UDP from port 1 and 2, partial flush by dst address > -AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > -AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > + dnl Test UDP from port 1 and 2, partial flush by dst address > + AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > + AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sort], [0], > [dnl > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1" | sort], > [0], [dnl > > udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1) > > udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 > ]) > > -AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_nw_dst=10.1.1.2']) > + AT_CHECK([flush_conntrack $type 'ct_nw_dst=10.1.1.2']) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [0], [dnl > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [0], > [dnl > > udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),zone=5 > ]) > > -AT_CHECK([ovs-appctl dpctl/flush-conntrack 'ct_nw_dst=10.1.1.1']) > + AT_CHECK([flush_conntrack $type 'ct_nw_dst=10.1.1.1']) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > + AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > +done > > OVS_TRAFFIC_VSWITCHD_STOP > AT_CLEANUP > diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in > index 10a6a64de..35dd5c8b0 100644 > --- a/utilities/ovs-ofctl.8.in > +++ b/utilities/ovs-ofctl.8.in > @@ -296,6 +296,26 @@ Flushes the connection tracking entries in \fIzone\fR on > \fIswitch\fR. > This command uses an Open vSwitch extension that is only in Open > vSwitch 2.6 and later. > . > +.IP "\fBflush\-conntrack \fIswitch [[zone=N] ct-tuple]\fR > +Flushes the connection entries in the tracker \fIzone\fR and > +connection tracking tuple \fIct-tuple\fR. > +If \fIct-tuple\fR is not provided, flushes all the connection entries. > +If \fIzone\fR is specified, only flushes the connections in > +\fIzone\fR. > +.IP > +If \fIct-tuple\fR is provided, flushes the connection entry specified by > +\fIct-tuple\fR in \fIzone\fR. The zone defaults to 0 if it is not provided. > +The userspace connection tracker requires flushing with the original > pre-NATed > +tuple and a warning log will be otherwise generated. The tuple can be partial > +and will remove all connections that are matching on the specified fields. > +An example of an IPv4 ICMP \fIct-tuple\fR: > +.IP > +"ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=1,icmp_type=8,icmp_code=0,icmp_id=10" > +.IP > +An example of an IPv6 TCP \fIct-tuple\fR: > +.IP > +"ct_ipv6_src=fc00::1,ct_ipv6_dst=fc00::2,ct_nw_proto=6,ct_tp_src=1,ct_tp_dst=2" > +. > .SS "OpenFlow Switch Flow Table Commands" > . > These commands manage the flow table in an OpenFlow switch. In each > diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c > index fe9114580..b0fac6e03 100644 > --- a/utilities/ovs-ofctl.c > +++ b/utilities/ovs-ofctl.c > @@ -40,6 +40,7 @@ > #include "fatal-signal.h" > #include "nx-match.h" > #include "odp-util.h" > +#include "ofp-ct-util.h" > #include "ofp-version-opt.h" > #include "ofproto/ofproto.h" > #include "openflow/nicira-ext.h" > @@ -485,6 +486,8 @@ usage(void) > " dump-ipfix-bridge SWITCH print ipfix stats of bridge\n" > " dump-ipfix-flow SWITCH print flow ipfix of a bridge\n" > " ct-flush-zone SWITCH ZONE flush conntrack entries in ZONE\n" > + " flush-conntrack SWITCH [[ZONE=N] CT_TUPLE] flush conntrack" > + " entries specified by CT_TUPLE and ZONE\n" > "\nFor OpenFlow switches and controllers:\n" > " probe TARGET probe whether TARGET is up\n" > " ping TARGET [N] latency of N-byte echos\n" > @@ -3050,6 +3053,40 @@ ofctl_ct_flush_zone(struct ovs_cmdl_context *ctx) > vconn_close(vconn); > } > > +static void > +ofctl_ct_flush_conntrack(struct ovs_cmdl_context *ctx) ofctl_ct_flush() > +{ > + struct vconn *vconn; > + struct ofputil_ct_match match = {0}; > + struct ds ds = DS_EMPTY_INITIALIZER; > + uint16_t zone; > + bool with_zone = false; > + int args = ctx->argc - 2; > + > + /* Parse ct tuple */ > + if (args) { > + if (!ofputil_ct_match_parse(&match, ctx->argv[ctx->argc - 1], &ds)) { > + ovs_fatal(0, "Failed to parse ct-tuple: %s", ds_cstr(&ds)); > + } > + args--; > + } > + > + /* Parse zone */ > + if (args && ovs_scan(ctx->argv[ctx->argc - 2], "zone=%"SCNu16, &zone)) { > + with_zone = true; > + } > + > + open_vconn(ctx->argv[1], &vconn); > + enum ofp_version version = vconn_get_version(vconn); > + > + struct ofpbuf *msg = > + ofputil_ct_match_encode(&match, with_zone ? &zone : NULL, version); > + > + ds_destroy(&ds); > + transact_noreply(vconn, msg); > + vconn_close(vconn); > +} > + > static void > ofctl_dump_ipfix_flow(struct ovs_cmdl_context *ctx) > { > @@ -5063,6 +5100,9 @@ static const struct ovs_cmdl_command all_commands[] = { > { "ct-flush-zone", "switch zone", > 2, 2, ofctl_ct_flush_zone, OVS_RO }, > > + { "flush-conntrack", "switch [[zone=N] ct-tuple]", > + 1, 3, ofctl_ct_flush_conntrack, OVS_RO }, > + > { "ofp-parse", "file", > 1, 1, ofctl_ofp_parse, OVS_RW }, > { "ofp-parse-pcap", "pcap", _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
