On Thu, Oct 20, 2022 at 12:53 PM Ales Musil <[email protected]> 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.
>
> Reported-at: https://bugzilla.redhat.com/2120546
> Signed-off-by: Ales Musil <[email protected]>
> ---
>  NEWS                           |  3 ++
>  include/openflow/nicira-ext.h  | 26 +++++++++++++
>  include/openvswitch/ofp-msgs.h |  4 ++
>  include/openvswitch/ofp-util.h |  4 ++
>  lib/ofp-bundle.c               |  1 +
>  lib/ofp-ct-util.c              | 40 +++++++++++++++++++
>  lib/ofp-ct-util.h              |  3 ++
>  lib/ofp-print.c                | 16 ++++++++
>  lib/ofp-util.c                 | 36 +++++++++++++++++
>  lib/rconn.c                    |  1 +
>  ofproto/ofproto-dpif.c         |  8 +++-
>  ofproto/ofproto-provider.h     |  7 +++-
>  ofproto/ofproto.c              | 24 +++++++++++-
>  tests/ofp-print.at             | 71 ++++++++++++++++++++++++++++++++++
>  14 files changed, 239 insertions(+), 5 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 81909812e..20ffd0a2a 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -25,6 +25,9 @@ Post-v3.0.0
>         DPDK 21.11.2.
>     - 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.
>
>
>  v3.0.0 - 15 Aug 2022
> diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
> index b68804991..90013bc36 100644
> --- a/include/openflow/nicira-ext.h
> +++ b/include/openflow/nicira-ext.h
> @@ -1064,4 +1064,30 @@ struct nx_zone_id {
>  };
>  OFP_ASSERT(sizeof(struct nx_zone_id) == 8);
>
> +/* NXT_CT_FLUSH.
> + *
> + * Flushes the connection tracking specified by 5-tuple. */
> +struct nx_ct_flush {
> +    uint8_t ip_proto;          /* IP protocol. */
> +    uint8_t family;            /* L3 address family. */
> +    ovs_be16 zone_id;          /* CT zone id. */
> +
> +    /* The orig direction section. */
> +    ovs_be32 orig_src[4];      /* CT source IPv6 or mapped IPv4 address.
> */
> +    ovs_be32 orig_dst[4];      /* CT destination IPv6 or mapped IPv4
> +                                * address. */
> +    ovs_be16 orig_src_port;    /* CT source port or ICMP id. */
> +    ovs_be16 orig_dst_port;    /* CT destination port or ICMP type and
> ICMP
> +                                * code. */
> +
> +    /* The reply direction section. */
> +    ovs_be32 reply_src[4];      /* CT source IPv6 or mapped IPv4 address.
> */
> +    ovs_be32 reply_dst[4];      /* CT destination IPv6 or mapped IPv4
> +                                * address. */
> +    ovs_be16 reply_src_port;    /* CT source port or ICMP id. */
> +    ovs_be16 reply_dst_port;    /* CT destination port or ICMP type and
> ICMP
> +                                * code. */
> +};
> +OFP_ASSERT(sizeof(struct nx_ct_flush) == 76);
> +
>  #endif /* openflow/nicira-ext.h */
> diff --git a/include/openvswitch/ofp-msgs.h
> b/include/openvswitch/ofp-msgs.h
> index 921a937e5..80f12481c 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. */
> +    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..2e533fa4f 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 *,
> +                                       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 9112305cc..2e7f7ffc1 100644
> --- a/lib/ofp-ct-util.c
> +++ b/lib/ofp-ct-util.c
> @@ -23,7 +23,9 @@
>
>  #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-util.h"
>  #include "openvswitch/packets.h"
> @@ -309,3 +311,41 @@ error:
>      free(copy);
>      return false;
>  }
> +
> +void
> +ofputil_ct_match_decode(struct ofputil_ct_match *match, uint16_t *zone_id,
> +                        const struct ofp_header *oh)
> +{
> +    const struct nx_ct_flush *nx_flush = ofpmsg_body(oh);
> +
> +    struct ofputil_ct_tuple *orig = &match->tuple_orig;
> +    struct ofputil_ct_tuple *reply = &match->tuple_reply;
> +
> +    *zone_id = ntohs(nx_flush->zone_id);
> +
> +    match->l3_type = nx_flush->family;
> +    match->ip_proto = nx_flush->ip_proto;
> +
> +    memcpy(&orig->src, &nx_flush->orig_src, sizeof orig->src);
> +    memcpy(&orig->dst, &nx_flush->orig_dst, sizeof orig->dst);
> +
> +    memcpy(&reply->src, &nx_flush->reply_src, sizeof reply->src);
> +    memcpy(&reply->dst, &nx_flush->reply_dst, sizeof reply->dst);
> +
> +    orig->src_port = nx_flush->orig_src_port;
> +    reply->src_port = nx_flush->reply_src_port;
> +
> +    if (match->ip_proto == IPPROTO_ICMP || match->ip_proto ==
> IPPROTO_ICMPV6) {
> +        uint16_t icmp = ntohs(nx_flush->orig_dst_port);
> +        orig->icmp_type = icmp >> 8 & 0xff;
> +        orig->icmp_code = icmp & 0xff;
> +
> +        icmp = ntohs(nx_flush->reply_dst_port);
> +        reply->icmp_type = icmp >> 8 & 0xff;
> +        reply->icmp_code = icmp & 0xff;
> +    } else {
> +        orig->dst_port = nx_flush->orig_dst_port;
> +        reply->dst_port = nx_flush->reply_dst_port;
> +    }
> +}
> +
> diff --git a/lib/ofp-ct-util.h b/lib/ofp-ct-util.h
> index 6e8f0f68a..4c6e61e2d 100644
> --- a/lib/ofp-ct-util.h
> +++ b/lib/ofp-ct-util.h
> @@ -31,4 +31,7 @@ void ofputil_ct_match_format(struct ds *ds,
>  bool ofputil_ct_match_parse(struct ofputil_ct_match *match, const char *s,
>                              struct ds *ds);
>
> +void ofputil_ct_match_decode(struct ofputil_ct_match *match, uint16_t
> *zone_id,
> +                             const struct ofp_header *oh);
> +
>  #endif // lib/ofp-ct-util.h
> diff --git a/lib/ofp-print.c b/lib/ofp-print.c
> index bd37fa17a..fd4e982b6 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,19 @@ 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;
> +    struct ofputil_ct_match match = {0};
> +
> +    ofputil_ct_match_decode(&match, &zone_id, oh);
> +    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 +1198,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..51c42357f 100644
> --- a/lib/ofp-util.c
> +++ b/lib/ofp-util.c
> @@ -237,3 +237,39 @@ 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;
> +    nx_flush->zone_id = htons(zone_id);
> +
> +    memcpy(&nx_flush->orig_src, &orig->src, sizeof nx_flush->orig_src);
> +    memcpy(&nx_flush->orig_dst, &orig->dst, sizeof nx_flush->orig_dst);
> +    memcpy(&nx_flush->reply_src, &reply->src, sizeof nx_flush->reply_src);
> +    memcpy(&nx_flush->reply_dst, &reply->dst, sizeof nx_flush->reply_dst);
> +
> +    nx_flush->orig_src_port = orig->src_port;
> +    nx_flush->reply_src_port = reply->src_port;
> +
> +    if (nx_flush->ip_proto == IPPROTO_ICMP
> +        || nx_flush->ip_proto == IPPROTO_ICMPV6) {
> +
> +        nx_flush->orig_dst_port =
> +            htons(orig->icmp_type << 8 | orig->icmp_code);
> +        nx_flush->reply_dst_port =
> +            htons(reply->icmp_type << 8 | reply->icmp_code);
> +    } else {
> +        nx_flush->orig_dst_port = orig->dst_port;
> +        nx_flush->reply_dst_port = reply->dst_port;
> +    }
> +
> +    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..1aee6b327 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,25 @@ 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};
> +    uint16_t zone_id;
> +
> +    ofputil_ct_match_decode(&match, &zone_id, oh);
> +
> +    if (ofproto->ofproto_class->ct_flush) {
> +        ofproto->ofproto_class->ct_flush(ofproto, &zone_id, &match);
>

I've realized that v1 doesn't allow zone_id being NULL. However I'm not
sure how to put that information into the extension struct.
I'm open to any suggestion, I was thinking about flags field, which would
grow the whole struct by 4 bytes.


>      } else {
>          return EOPNOTSUPP;
>      }
> @@ -8787,6 +8806,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..418e98559 100644
> --- a/tests/ofp-print.at
> +++ b/tests/ofp-print.at
> @@ -4073,3 +4073,74 @@ 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 5c 00 00 00 03 00 00 23 20 00 00 00 20 \
> +06 \
> +02 \
> +00 0d \
> +00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 \
> +00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 \
> +00 50 \
> +1f 90 \
> +00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 \
> +00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 \
> +1f 90 \
> +00 50 \
> +"], [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 5c 00 00 00 03 00 00 23 20 00 00 00 20 \
> +06 \
> +0a \
> +00 0d \
> +fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 01 \
> +fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 02 \
> +00 50 \
> +1f 90 \
> +fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 02 \
> +fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 01 \
> +1f 90 \
> +00 50 \
> +"], [0], [dnl
> +NXT_CT_FLUSH (xid=0x3):
> l3_type=10,ip_proto=6,orig=(src=fd18::ffff:abcd:1,dst=fd18::ffff:abcd:2,src_port=80,dst_port=8080),reply=(src=fd18::ffff:abcd:2,dst=fd18::ffff:abcd:1,src_port=8080,dst_port=80),zone_id=13
> +])
> +
> +AT_CHECK([ovs-ofctl ofp-print "\
> +01 04 00 5c 00 00 00 03 00 00 23 20 00 00 00 20 \
> +01 \
> +02 \
> +00 0d \
> +00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 \
> +00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 \
> +00 01 \
> +08 00 \
> +00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 \
> +00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 \
> +00 01 \
> +00 00 \
> +"], [0], [dnl
> +NXT_CT_FLUSH (xid=0x3):
> l3_type=2,ip_proto=1,orig=(src=10.10.0.1,dst=10.10.0.2,icmp_id=1,icmp_type=8,icmp_code=0),reply=(src=10.10.0.2,dst=10.10.0.1,icmp_id=1,icmp_type=0,icmp_code=0),zone_id=13
> +])
> +
> +AT_CHECK([ovs-ofctl ofp-print "\
> +01 04 00 5c 00 00 00 03 00 00 23 20 00 00 00 20 \
> +01 \
> +0a \
> +00 0d \
> +fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 01 \
> +fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 02 \
> +00 01 \
> +08 00 \
> +fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 02 \
> +fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 01 \
> +00 01 \
> +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=1,icmp_type=8,icmp_code=0),reply=(src=fd18::ffff:abcd:2,dst=fd18::ffff:abcd:1,icmp_id=1,icmp_type=0,icmp_code=0),zone_id=13
> +])
> +AT_CLEANUP
> --
> 2.37.3
>
>

-- 

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