On Fri, Dec 16, 2022 at 1:01 PM Paolo Valerio <[email protected]> wrote:

> Ales Musil <[email protected]> writes:
>
> > 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.
> >
> > Reported-at: https://bugzilla.redhat.com/2120546
> > Signed-off-by: Ales Musil <[email protected]>
> > ---
> > 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                           |   3 +
> >  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.c          |  38 +++++++++
> >  17 files changed, 503 insertions(+), 58 deletions(-)
> >
> > diff --git a/NEWS b/NEWS
> > index ff8904b02..46b8faa41 100644
> > --- a/NEWS
> > +++ b/NEWS
> > @@ -16,6 +16,9 @@ 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.
> >
>
> I guess we miss an entry for ovs-ofctl flush-conntrack
>
> >
> >  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. */
> > +
> > +    /* 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. */
> > +
> > +    /* 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. */
> > +    uint8_t zero[6];           /* Must be zero. */
> > +};
> > +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][]. */
> > +    OFPRAW_NXT_CT_FLUSH,
> >  };
> >
> >  /* 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. */
> >
> >      /* 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 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);
> > +        }
> > +    }
> > +
> > +    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;
> > +            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. */
> > +    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)
> > +{
> > +    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}])
> > +    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
> > +    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.c b/utilities/ovs-ofctl.c
> > index fe9114580..8cb0911a2 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"
> > @@ -3050,6 +3051,40 @@ ofctl_ct_flush_zone(struct ovs_cmdl_context *ctx)
> >      vconn_close(vconn);
> >  }
> >
> > +static void
> > +ofctl_ct_flush_conntrack(struct ovs_cmdl_context *ctx)
> > +{
> > +    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 +5098,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 },
> > +
>
> usage() for this is missing and the entry in utilities/ovs-ofctl.8.in
>
> >      { "ofp-parse", "file",
> >        1, 1, ofctl_ofp_parse, OVS_RW },
> >      { "ofp-parse-pcap", "pcap",
> > --
> > 2.38.1
>
>

Both addressed in v5, thanks.

-- 

Ales Musil

Senior Software Engineer - OVN Core

Red Hat EMEA <https://www.redhat.com>

[email protected]    IM: amusil
<https://red.ht/sig>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to