On Mon, Jan 16, 2023 at 12:45 PM Ales Musil <amu...@redhat.com> 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 direction. > > Reported-at: https://bugzilla.redhat.com/2120546 > Signed-off-by: Ales Musil <amu...@redhat.com> > --- > v7: Rebase on top of current master. > Address comments from Ilya: > * Use the public header for encode/decode functions. > * Adjust the code accordingly to the new header file. > * Address some style related comments. > * Rename the nested TLV, so they do not overlap with > the outer types. > * Document the ICMP partial match limitation. > > v6: Rebase on top of current master. > Address comments from Ilya. > 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 | 37 +++++++ > include/openvswitch/ofp-ct.h | 10 ++ > include/openvswitch/ofp-msgs.h | 4 + > lib/ofp-bundle.c | 1 + > lib/ofp-ct.c | 196 +++++++++++++++++++++++++++++++++ > lib/ofp-print.c | 20 ++++ > lib/rconn.c | 1 + > ofproto/ofproto-dpif.c | 9 +- > ofproto/ofproto-provider.h | 7 +- > ofproto/ofproto.c | 29 ++++- > tests/ofp-print.at | 108 ++++++++++++++++++ > tests/ovs-ofctl.at | 38 +++++++ > tests/system-traffic.at | 38 ++++--- > utilities/ovs-ofctl.8.in | 27 +++++ > utilities/ovs-ofctl.c | 51 +++++++++ > 16 files changed, 560 insertions(+), 22 deletions(-) > > diff --git a/NEWS b/NEWS > index 035fcb0ee..22840402c 100644 > --- a/NEWS > +++ b/NEWS > @@ -36,6 +36,12 @@ Post-v3.0.0 > - ovs-dpctl and 'ovs-appctl dpctl/' commands: > * "flush-conntrack" is now capable of handling partial 5-tuple, > with additional optional parameter to specify the reply direction. > + - 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 for both directions. > > > v3.0.0 - 15 Aug 2022 > diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h > index b68804991..35f66a76d 100644 > --- a/include/openflow/nicira-ext.h > +++ b/include/openflow/nicira-ext.h > @@ -1064,4 +1064,41 @@ 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_TUPLE = 0, /* Outer type for original tuple TLV. > + * The nested TLV are specified > + * by 'enum nx_ct_flush_tuple_tlv_type'. */ > + NXT_CT_REPLY_TUPLE = 1, /* Outer type for reply tuple TLC. * > + * The nested TLV are specified > + * by 'enum nx_ct_flush_tuple_tlv_type'*/ > + /* Primitive types. */ > + NXT_CT_ZONE_ID = 2, /* be16 zone id. */ > +}; > + > +/* CT flush nested TLVs. */ > +enum nx_ct_flush_tuple_tlv_type { > + NXT_CT_TUPLE_SRC = 0, /* IPv6 or mapped IPv4 address. */ > + NXT_CT_TUPLE_DST = 1, /* IPv6 or mapped IPv4 address. */ > + NXT_CT_TUPLE_SRC_PORT = 2, /* be16 source port. */ > + NXT_CT_TUPLE_DST_PORT = 3, /* be16 destination port. */ > + NXT_CT_TUPLE_ICMP_ID = 4, /* be16 ICMP id. */ > + NXT_CT_TUPLE_ICMP_TYPE = 5, /* u8 ICMP type. */ > + NXT_CT_TUPLE_ICMP_CODE = 6, /* u8 ICMP code. */ > +}; > + > +/* NXT_CT_FLUSH. > + * > + * Flushes the connection tracking specified by 5-tuple. > + * The struct should be followed by TLVs specifying the matching > parameters. > + * Currently there is a limitation for ICMP, in order to partially match > on > + * ICMP parameters the tuple should include at least SRC/DST. */ > +struct nx_ct_flush { > + uint8_t ip_proto; /* IP protocol. */ > + uint8_t pad[7]; /* Must be zero. */ > + /* Followed by optional TLVs of type 'enum nx_ct_flush_tlv_type'. */ > +}; > +OFP_ASSERT(sizeof(struct nx_ct_flush) == 8); > + > #endif /* openflow/nicira-ext.h */ > diff --git a/include/openvswitch/ofp-ct.h b/include/openvswitch/ofp-ct.h > index e7af45337..094da0a97 100644 > --- a/include/openvswitch/ofp-ct.h > +++ b/include/openvswitch/ofp-ct.h > @@ -21,6 +21,8 @@ > #include <sys/types.h> > #include <netinet/in.h> > > +#include "openflow/nicira-ext.h" > + > #ifdef __cplusplus > extern "C" { > #endif > @@ -63,6 +65,14 @@ bool ofp_ct_tuple_parse(struct ofp_ct_tuple *tuple, > const char *s, > > bool ofp_ct_is_match_zero(const struct ofp_ct_match *match); > > +enum ofperr ofp_ct_match_decode(struct ofp_ct_match *match, bool > *with_zone, > + uint16_t *zone_id, > + const struct ofp_header *oh); > + > +struct ofpbuf * ofp_ct_match_encode(const struct ofp_ct_match *match, > + uint16_t *zone_id, > + enum ofp_version version); > + > #ifdef __cplusplus > } > #endif > diff --git a/include/openvswitch/ofp-msgs.h > b/include/openvswitch/ofp-msgs.h > index 921a937e5..708427fc0 100644 > --- a/include/openvswitch/ofp-msgs.h > +++ b/include/openvswitch/ofp-msgs.h > @@ -515,6 +515,9 @@ enum ofpraw { > /* NXT 1.0+ (29): struct nx_zone_id. */ > OFPRAW_NXT_CT_FLUSH_ZONE, > > + /* NXT 1.0+ (32): struct nx_ct_flush, uint8_t[8][]. */ > + OFPRAW_NXT_CT_FLUSH, > + > /* NXST 1.0+ (3): void. */ > OFPRAW_NXST_IPFIX_BRIDGE_REQUEST, > > @@ -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/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.c b/lib/ofp-ct.c > index 61f0be953..a81d9f0bc 100644 > --- a/lib/ofp-ct.c > +++ b/lib/ofp-ct.c > @@ -23,8 +23,12 @@ > > #include "ct-dpif.h" > #include "openvswitch/ofp-ct.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" > > @@ -212,3 +216,195 @@ error: > free(copy); > return false; > } > + > +static enum ofperr > +ofpprop_pull_ipv6(struct ofpbuf *property, struct in6_addr *addr, > + uint16_t *l3_type) > +{ > + if (ofpbuf_msgsize(property) < sizeof *addr) { > + return OFPERR_OFPBPC_BAD_LEN; > + } > + > + memcpy(addr, property->msg, sizeof *addr); > + > + uint16_t l3 = 0; > + if (!ipv6_is_zero(addr)) { > + l3 = IN6_IS_ADDR_V4MAPPED(addr) ? AF_INET : AF_INET6; > + } > + > + if (*l3_type && l3 && *l3_type != l3) { > + return OFPERR_OFPBPC_BAD_VALUE; > + } > + > + *l3_type = l3; > + > + return 0; > +} > + > +static enum ofperr > +ofp_ct_tuple_decode_nested(struct ofpbuf *property, struct ofp_ct_tuple > *tuple, > + uint16_t *l3_type) > +{ > + 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_TUPLE_SRC: > + error = ofpprop_pull_ipv6(&inner, &tuple->src, l3_type); > + break; > + > + case NXT_CT_TUPLE_DST: > + error = ofpprop_pull_ipv6(&inner, &tuple->dst, l3_type); > + break; > + > + case NXT_CT_TUPLE_SRC_PORT: > + error = ofpprop_parse_be16(&inner, &tuple->src_port); > + break; > + > + case NXT_CT_TUPLE_DST_PORT: > + error = ofpprop_parse_be16(&inner, &tuple->dst_port); > + break; > + > + case NXT_CT_TUPLE_ICMP_ID: > + error = ofpprop_parse_be16(&inner, &tuple->icmp_id); > + break; > + > + case NXT_CT_TUPLE_ICMP_TYPE: > + error = ofpprop_parse_u8(&inner, &tuple->icmp_type); > + break; > + > + case NXT_CT_TUPLE_ICMP_CODE: > + error = ofpprop_parse_u8(&inner, &tuple->icmp_code); > + break; > + } > + > + if (error) { > + return error; > + } > + } > + > + return 0; > +} > + > +static void > +ofp_ct_tuple_encode(const struct ofp_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_TUPLE_SRC, &tuple->src, sizeof > tuple->src); > + } > + > + if (!ipv6_is_zero(&tuple->dst)) { > + ofpprop_put(&nested, NXT_CT_TUPLE_DST, &tuple->dst, sizeof > tuple->dst); > + } > + > + if (ip_proto == IPPROTO_ICMP || ip_proto == IPPROTO_ICMPV6) { > + ofpprop_put_be16(&nested, NXT_CT_TUPLE_ICMP_ID, tuple->icmp_id); > + ofpprop_put_u8(&nested, NXT_CT_TUPLE_ICMP_TYPE, tuple->icmp_type); > + ofpprop_put_u8(&nested, NXT_CT_TUPLE_ICMP_CODE, tuple->icmp_code); > + } else { > + if (tuple->src_port) { > + ofpprop_put_be16(&nested, NXT_CT_TUPLE_SRC_PORT, > tuple->src_port); > + } > + > + if (tuple->dst_port) { > + ofpprop_put_be16(&nested, NXT_CT_TUPLE_DST_PORT, > tuple->dst_port); > + } > + } > + > + if (nested.size) { > + ofpprop_put_nested(buf, type, &nested); > + } > + > + ofpbuf_uninit(&nested); > +} > + > +enum ofperr > +ofp_ct_match_decode(struct ofp_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->pad, sizeof nx_flush->pad)) { > + return OFPERR_NXBRC_MUST_BE_ZERO; > + } > + > + match->ip_proto = nx_flush->ip_proto; > + > + struct ofp_ct_tuple *orig = &match->tuple_orig; > + struct ofp_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_TUPLE: > + error = ofp_ct_tuple_decode_nested(&property, orig, > + &match->l3_type); > + break; > + > + case NXT_CT_REPLY_TUPLE: > + error = ofp_ct_tuple_decode_nested(&property, reply, > + &match->l3_type); > + break; > + > + case NXT_CT_ZONE_ID: > + if (with_zone) { > + *with_zone = true; > + } > + error = ofpprop_parse_u16(&property, zone_id); > + break; > + } > + > + if (error) { > + return error; > + } > + } > + > + return 0; > +} > + > +struct ofpbuf * > +ofp_ct_match_encode(const struct ofp_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 ofp_ct_tuple *orig = &match->tuple_orig; > + const struct ofp_ct_tuple *reply = &match->tuple_reply; > + > + nx_flush->ip_proto = match->ip_proto; > + > + ofp_ct_tuple_encode(orig, msg, NXT_CT_ORIG_TUPLE,match->ip_proto); > + ofp_ct_tuple_encode(reply, msg, NXT_CT_REPLY_TUPLE, match->ip_proto); > + > + if (zone_id) { > + ofpprop_put_u16(msg, NXT_CT_ZONE_ID, *zone_id); > + } > + > + return msg; > +} > diff --git a/lib/ofp-print.c b/lib/ofp-print.c > index bd37fa17a..874079b84 100644 > --- a/lib/ofp-print.c > +++ b/lib/ofp-print.c > @@ -45,6 +45,7 @@ > #include "openvswitch/ofp-actions.h" > #include "openvswitch/ofp-bundle.h" > #include "openvswitch/ofp-connection.h" > +#include "openvswitch/ofp-ct.h" > #include "openvswitch/ofp-errors.h" > #include "openvswitch/ofp-group.h" > #include "openvswitch/ofp-ipfix.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 ofp_ct_match match = {0}; > + > + enum ofperr error = ofp_ct_match_decode(&match, NULL, &zone_id, oh); > + if (error) { > + return error; > + } > + > + ds_put_format(string, " zone=%"PRIu16" ", zone_id); > + ofp_ct_match_format(string, &match); > + > + 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/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..f87e27a8c 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 ofp_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,10 @@ 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..a84ddc1d0 100644 > --- a/ofproto/ofproto-provider.h > +++ b/ofproto/ofproto-provider.h > @@ -42,6 +42,7 @@ > #include "ofproto/ofproto.h" > #include "openvswitch/list.h" > #include "openvswitch/ofp-actions.h" > +#include "openvswitch/ofp-ct.h" > #include "openvswitch/ofp-errors.h" > #include "openvswitch/ofp-flow.h" > #include "openvswitch/ofp-group.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 ofp_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..17f636ed9 100644 > --- a/ofproto/ofproto.c > +++ b/ofproto/ofproto.c > @@ -42,6 +42,7 @@ > #include "openvswitch/meta-flow.h" > #include "openvswitch/ofp-actions.h" > #include "openvswitch/ofp-bundle.h" > +#include "openvswitch/ofp-ct.h" > #include "openvswitch/ofp-errors.h" > #include "openvswitch/ofp-match.h" > #include "openvswitch/ofp-msgs.h" > @@ -934,7 +935,30 @@ 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 ofp_ct_match match = {0}; > + bool with_zone = false; > + uint16_t zone_id = 0; > + > + enum ofperr error = ofp_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 +8811,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..14aa55416 100644 > --- a/tests/ofp-print.at > +++ b/tests/ofp-print.at > @@ -4073,3 +4073,111 @@ 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 \ > +00 00 00 00 00 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): zone=0 > 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' > 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 20 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +00 00 00 00 00 00 00 \ > +00 02 00 08 00 0d 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): zone=13 > 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' > 'ct_ipv6_src=::,ct_ipv6_dst=::,ct_tp_src=0,ct_tp_dst=0' > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 68 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +00 00 00 00 00 00 00 \ > +00 02 00 08 00 0d 00 00 \ > +00 00 00 48 00 00 00 00 \ > +00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ > +00 01 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ > +00 02 00 08 00 50 00 00 \ > +00 03 00 08 1f 90 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): zone=13 > 'ct_nw_src=10.10.0.1,ct_nw_dst=10.10.0.2,ct_tp_src=80,ct_tp_dst=8080,ct_nw_proto=6' > 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0' > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 68 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +00 00 00 00 00 00 00 \ > +00 02 00 08 00 0d 00 00 \ > +00 01 00 48 00 00 00 00 \ > +00 01 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ > +00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ > +00 03 00 08 00 50 00 00 \ > +00 02 00 08 1f 90 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): zone=13 > 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=6' > 'ct_nw_src=10.10.0.2,ct_nw_dst=10.10.0.1,ct_tp_src=8080,ct_tp_dst=80' > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 b0 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +00 00 00 00 00 00 00 \ > +00 02 00 08 00 0d 00 00 \ > +00 00 00 48 00 00 00 00 \ > +00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ > +00 01 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ > +00 02 00 08 00 50 00 00 \ > +00 03 00 08 1f 90 00 00 \ > +00 01 00 48 00 00 00 00 \ > +00 01 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ > +00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ > +00 03 00 08 00 50 00 00 \ > +00 02 00 08 1f 90 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): zone=13 > 'ct_nw_src=10.10.0.1,ct_nw_dst=10.10.0.2,ct_tp_src=80,ct_tp_dst=8080,ct_nw_proto=6' > 'ct_nw_src=10.10.0.2,ct_nw_dst=10.10.0.1,ct_tp_src=8080,ct_tp_dst=80' > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 b8 00 00 00 03 00 00 23 20 00 00 00 20 \ > +01 \ > +00 00 00 00 00 00 00 \ > +00 00 00 50 00 00 00 00 \ > +00 00 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 01 00 00 00 00 \ > +00 01 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 02 00 00 00 00 \ > +00 04 00 08 00 0a 00 00 \ > +00 05 00 05 01 00 00 00 \ > +00 06 00 05 02 00 00 00 \ > +00 01 00 50 00 00 00 00 \ > +00 01 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 02 00 00 00 00 \ > +00 00 00 14 fd 18 00 00 00 00 00 00 00 00 ff ff ab cd 00 01 00 00 00 00 \ > +00 04 00 08 00 0a 00 00 \ > +00 05 00 05 03 00 00 00 \ > +00 06 00 05 04 00 00 00 \ > +"], [0], [dnl > +NXT_CT_FLUSH (xid=0x3): zone=0 > 'ct_ipv6_src=fd18::ffff:abcd:1,ct_ipv6_dst=fd18::ffff:abcd:2,icmp_id=10,icmp_type=1,icmp_code=2,ct_nw_proto=1' > 'ct_ipv6_src=fd18::ffff:abcd:1,ct_ipv6_dst=fd18::ffff:abcd:2,icmp_id=10,icmp_type=3,icmp_code=4' > +]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 58 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +00 00 00 00 00 00 00 \ > +00 02 00 08 00 0d 00 00 \ > +00 00 00 38 00 00 00 00 \ > +00 00 00 14 00 0a 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ > +00 01 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ > +" | grep -q OFPBPC_BAD_VALUE], [0]) > + > +AT_CHECK([ovs-ofctl ofp-print "\ > +01 04 00 60 00 00 00 03 00 00 23 20 00 00 00 20 \ > +06 \ > +00 00 00 00 00 00 00 \ > +00 02 00 08 00 0d 00 00 \ > +00 00 00 20 00 00 00 00 \ > +00 00 00 14 00 0a 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 01 00 00 00 00 \ > +00 01 00 20 00 00 00 00 \ > +00 00 00 14 00 00 00 00 00 00 00 00 00 00 ff ff 0a 0a 00 02 00 00 00 00 \ > +" | grep -q OFPBPC_BAD_VALUE], [0]) > +AT_CLEANUP > diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at > index a8934051e..8531b2e2e 100644 > --- a/tests/ovs-ofctl.at > +++ b/tests/ovs-ofctl.at > @@ -3271,3 +3271,41 @@ 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]) > +OVS_VSWITCHD_START > + > +AT_CHECK([ovs-appctl vlog/set ct_dpif:dbg]) > + > +# Check flush conntrack with both zone and tuple > +AT_CHECK([ovs-ofctl ct-flush 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: zone=5 > 'ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_tp_src=1,ct_tp_dst=0,ct_nw_proto=17' > 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0'" ovs-vswitchd.log]) > + > +# Check flush-conntrack just with tuple > +AT_CHECK([ovs-ofctl ct-flush 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: zone=0 > 'ct_nw_src=10.1.1.3,ct_nw_dst=10.1.1.4,ct_tp_src=1,ct_tp_dst=0,ct_nw_proto=17' > 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0'" ovs-vswitchd.log]) > + > +# Check flush-conntrack with reply tuple > +AT_CHECK([ovs-ofctl ct-flush 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 3]) > +AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=0 > 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=17' > 'ct_nw_src=10.1.1.3,ct_nw_dst=10.1.1.4,ct_tp_src=1,ct_tp_dst=0'" > ovs-vswitchd.log]) > + > +# Check flush-conntrack with zone and reply tuple > +AT_CHECK([ovs-ofctl ct-flush br0 zone=5 '' > '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 4]) > +AT_CHECK([grep -q "ct_dpif|DBG|.*ct_flush: zone=5 > 'ct_nw_src=::,ct_nw_dst=::,ct_tp_src=0,ct_tp_dst=0,ct_nw_proto=17' > 'ct_nw_src=10.1.1.3,ct_nw_dst=10.1.1.4,ct_tp_src=1,ct_tp_dst=0'" > ovs-vswitchd.log]) > + > +# Check flush-conntrack without any tuple and zone > +AT_CHECK([ovs-ofctl ct-flush br0]) > + > +OVS_WAIT_UNTIL([test $(grep -c "|ct_dpif|DBG|.*ct_flush" > ovs-vswitchd.log) -eq 5]) > +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 e7ec1d96b..503455cc6 100644 > --- a/tests/system-traffic.at > +++ b/tests/system-traffic.at > @@ -2298,6 +2298,10 @@ > priority=100,in_port=2,icmp,action=ct(zone=5,commit),1 > > AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt]) > > +m4_foreach([FLUSH_CMD], [[ovs-appctl dpctl/flush-conntrack], > + [ovs-ofctl ct-flush br0]], [ > +AS_BOX([Testing with FLUSH_CMD]) > + > dnl Test UDP from port 1 > AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 > actions=resubmit(,0)"]) > > @@ -2305,10 +2309,10 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep > "orig=.src=10\.1\.1\.1,"], [], > > > 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_CMD > '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]) > > -AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep > "orig=.src=10\.1\.1\.1,"], [1], [dnl > -]) > > dnl Test UDP from port 2 > AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 > packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 > actions=resubmit(,0)"]) > @@ -2317,10 +2321,9 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep > "orig=.src=10\.1\.1\.2,"], [0], > > > 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_CMD 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 > @@ -2334,7 +2337,7 @@ > icmp,orig=(src=10.1.1.2,dst=10.1.1.1,id=<cleared>,type=8,code=0),reply=(src=10.1 > > 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]) > +AT_CHECK([FLUSH_CMD zone=5 $ICMP_TUPLE]) > > AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep > "orig=.src=10\.1\.1\.2,"], [1], [dnl > ]) > @@ -2349,13 +2352,13 @@ > udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10. > > > 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_CMD 'ct_nw_proto=17,ct_tp_src=1']) > > 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_CMD 'ct_nw_proto=17,ct_tp_src=2']) > > AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > > @@ -2369,13 +2372,13 @@ > udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10. > > > 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_CMD 'ct_nw_proto=17,ct_tp_dst=2']) > > 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_CMD 'ct_nw_proto=17,ct_tp_dst=1']) > > AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > > @@ -2389,13 +2392,13 @@ > udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10. > > > 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_CMD 'ct_nw_src=10.1.1.1']) > > 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_CMD 'ct_nw_src=10.1.1.2']) > > AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > > @@ -2409,13 +2412,13 @@ > udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10. > > > 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_CMD 'ct_nw_dst=10.1.1.2']) > > 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_CMD 'ct_nw_dst=10.1.1.1']) > > AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > > @@ -2429,15 +2432,16 @@ > udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2),reply=(src=10.1.1.2,dst=10. > > > 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_CMD '' 'ct_nw_src=10.1.1.2']) > > 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 zone=5 '' > 'ct_nw_src=10.1.1.1']) > +AT_CHECK([FLUSH_CMD zone=5 '' 'ct_nw_src=10.1.1.1']) > > AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "10\.1\.1\.1"], [1]) > +]) > > OVS_TRAFFIC_VSWITCHD_STOP > AT_CLEANUP > diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in > index 10a6a64de..6f02a9134 100644 > --- a/utilities/ovs-ofctl.8.in > +++ b/utilities/ovs-ofctl.8.in > @@ -296,6 +296,33 @@ 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 "\fBct\-flush \fIswitch [zone=N] [ct-origin-tuple [ct-reply-tuple]]\fR > +Flushes the connection entries in the tracker \fIzone\fR and > +connection tracking tuple \fIct-tuple\fR. > +If \fIct-[origin|reply]-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-[origin|reply]-tuple\fR is provided, flushes the connection entry > +specified by \fIct-[origin|reply]-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. In order to specify only > +\fIct-reply-tuple\fR provide empty string as \fIct-origin-tuple\fR. > +.IP > +Note: Currently there is limitation for matching on ICMP, in order to > partially > +match on ICMP parameters the \fIct-[origin|reply]-tuple\fR has to include > +either source or destination IP. > +.IP > +An example of an IPv4 ICMP \fIct-[origin|reply]-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-[origin|reply]-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..eabec18a3 100644 > --- a/utilities/ovs-ofctl.c > +++ b/utilities/ovs-ofctl.c > @@ -48,6 +48,7 @@ > #include "openvswitch/meta-flow.h" > #include "openvswitch/ofp-actions.h" > #include "openvswitch/ofp-bundle.h" > +#include "openvswitch/ofp-ct.h" > #include "openvswitch/ofp-errors.h" > #include "openvswitch/ofp-group.h" > #include "openvswitch/ofp-match.h" > @@ -485,6 +486,9 @@ 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" > + " ct-flush SWITCH [ZONE] [CT_ORIG_TUPLE [CT_REPLY_TUPLE]]\n" > + " flush conntrack entries > specified\n" > + " by CT_ORIG/REPLY_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 +3054,50 @@ ofctl_ct_flush_zone(struct ovs_cmdl_context *ctx) > vconn_close(vconn); > } > > +static void > +ofctl_ct_flush(struct ovs_cmdl_context *ctx) > +{ > + struct vconn *vconn; > + struct ofp_ct_match match = {0}; > + struct ds ds = DS_EMPTY_INITIALIZER; > + uint16_t zone, *pzone = NULL; > + int args = ctx->argc - 2; > + > + /* Parse zone. */ > + if (args && !strncmp(ctx->argv[2], "zone=", 5)) { > + if (!ovs_scan(ctx->argv[2], "zone=%"SCNu16, &zone)) { > + ovs_fatal(0, "Failed to parse zone"); > + } > + pzone = &zone; > + args--; > + } > + > + /* Parse ct tuples. */ > + for (int i = 0; i < 2; i++) { > + if (!args) { > + break; > + } > + > + struct ofp_ct_tuple *tuple = > + i ? &match.tuple_reply : &match.tuple_orig; > + const char *arg = ctx->argv[ctx->argc - args]; > + > + if (arg[0] && !ofp_ct_tuple_parse(tuple, arg, &ds, > &match.ip_proto, > + &match.l3_type)) { > + ovs_fatal(0, "Failed to parse ct-tuple: %s", ds_cstr(&ds)); > + } > + args--; > + } > + > + open_vconn(ctx->argv[1], &vconn); > + enum ofp_version version = vconn_get_version(vconn); > + struct ofpbuf *msg = ofp_ct_match_encode(&match, pzone, version); > + > + ds_destroy(&ds); > + transact_noreply(vconn, msg); > + vconn_close(vconn); > +} > + > static void > ofctl_dump_ipfix_flow(struct ovs_cmdl_context *ctx) > { > @@ -5063,6 +5111,9 @@ static const struct ovs_cmdl_command all_commands[] > = { > { "ct-flush-zone", "switch zone", > 2, 2, ofctl_ct_flush_zone, OVS_RO }, > > + { "ct-flush", "switch [zone=N] [ct-orig-tuple [ct-reply-tuple]]", > + 1, 4, ofctl_ct_flush, OVS_RO }, > + > { "ofp-parse", "file", > 1, 1, ofctl_ofp_parse, OVS_RW }, > { "ofp-parse-pcap", "pcap", > -- > 2.39.0 > > Argh I missed the comment alignment again... With that being said: diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index 35f66a76d..645ad656a 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -1083,9 +1083,9 @@ enum nx_ct_flush_tuple_tlv_type { NXT_CT_TUPLE_DST = 1, /* IPv6 or mapped IPv4 address. */ NXT_CT_TUPLE_SRC_PORT = 2, /* be16 source port. */ NXT_CT_TUPLE_DST_PORT = 3, /* be16 destination port. */ - NXT_CT_TUPLE_ICMP_ID = 4, /* be16 ICMP id. */ - NXT_CT_TUPLE_ICMP_TYPE = 5, /* u8 ICMP type. */ - NXT_CT_TUPLE_ICMP_CODE = 6, /* u8 ICMP code. */ + NXT_CT_TUPLE_ICMP_ID = 4, /* be16 ICMP id. */ + NXT_CT_TUPLE_ICMP_TYPE = 5, /* u8 ICMP type. */ + NXT_CT_TUPLE_ICMP_CODE = 6, /* u8 ICMP code. */ }; /* NXT_CT_FLUSH. Should be added on top of this patch. Thanks, Ales -- Ales Musil Senior Software Engineer - OVN Core Red Hat EMEA <https://www.redhat.com> amu...@redhat.com IM: amusil <https://red.ht/sig> _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev