@@ -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 conntrack by full
+ or partial 5-tuple.
v3.0.0 - 15 Aug 2022
@@ -1064,4 +1064,21 @@ 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 direction; /* The CT direction specified by enum
+ * ofputil_ct_direction. */
+ ovs_be16 zone_id; /* CT zone id. */
+ ovs_be32 src[4]; /* CT source IPv6 or mapped IPv4 address. */
+ ovs_be32 dst[4]; /* CT destination IPv6 or mapped IPv4
+ * address. */
+ ovs_be16 src_port; /* CT source port or ICMP id. */
+ ovs_be16 dst_port; /* CT destination port or ICMP type and ICMP
+ * code. */
+};
+OFP_ASSERT(sizeof(struct nx_ct_flush) == 40);
+
#endif /* openflow/nicira-ext.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.
@@ -19,6 +19,9 @@
#include <stdbool.h>
#include <stdint.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
#include "openvswitch/ofp-protocol.h"
struct ofp_header;
@@ -27,6 +30,31 @@ struct ofp_header;
extern "C" {
#endif
+enum ofputil_ct_direction {
+ OFPUTIL_CT_DIRECTION_ORIG = 1,
+ OFPUTIL_CT_DIRECTION_REPLY,
+};
+
+struct ofputil_ct_tuple {
+ uint8_t ip_proto;
+ uint8_t direction;
+
+ struct in6_addr src;
+ struct in6_addr dst;
+
+ union {
+ ovs_be16 src_port;
+ ovs_be16 icmp_id;
+ };
+ union {
+ ovs_be16 dst_port;
+ struct {
+ uint8_t icmp_type;
+ uint8_t icmp_code;
+ };
+ };
+};
+
bool ofputil_decode_hello(const struct ofp_header *,
uint32_t *allowed_versions);
struct ofpbuf *ofputil_encode_hello(uint32_t version_bitmap);
@@ -37,6 +65,14 @@ struct ofpbuf *ofputil_encode_echo_reply(const struct ofp_header *);
struct ofpbuf *ofputil_encode_barrier_request(enum ofp_version);
+struct ofpbuf *ofp_ct_tuple_encode(struct ofputil_ct_tuple *tuple,
+ uint16_t zone_id,
+ enum ofputil_ct_direction dir,
+ enum ofp_version version);
+enum ofperr ofp_ct_tuple_decode(struct ofputil_ct_tuple *tuple,
+ uint16_t *zone_id,
+ const struct ofp_header *oh);
+
#ifdef __cplusplus
}
#endif
@@ -23,6 +23,7 @@
#include "ct-dpif.h"
#include "openvswitch/ofp-parse.h"
+#include "openvswitch/ofp-util.h"
#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(ct_dpif);
@@ -176,7 +177,8 @@ ct_dpif_tuple_cmp_partial(const struct ct_dpif_tuple *partial,
static int
ct_dpif_flush_tuple(struct dpif *dpif, const uint16_t *zone,
- const struct ct_dpif_tuple *tuple) {
+ const struct ct_dpif_tuple *tuple,
+ enum ofputil_ct_direction direction) {
struct ct_dpif_dump_state *dump;
struct ct_dpif_entry cte;
int error;
@@ -204,7 +206,22 @@ ct_dpif_flush_tuple(struct dpif *dpif, const uint16_t *zone,
continue;
}
- if (ct_dpif_tuple_cmp_partial(tuple, &cte.tuple_orig)) {
+ struct ct_dpif_tuple *ct_tuple;
+ switch (direction) {
+ case OFPUTIL_CT_DIRECTION_ORIG:
+ ct_tuple = &cte.tuple_orig;
+ break;
+ case OFPUTIL_CT_DIRECTION_REPLY:
+ ct_tuple = &cte.tuple_reply;
+ break;
+ default:
+ error = EOPNOTSUPP;
+ }
+ if (error) {
+ break;
+ }
+
+ if (ct_dpif_tuple_cmp_partial(tuple, ct_tuple)) {
error = dpif->dpif_class->ct_flush(dpif, &cte.zone,
&cte.tuple_orig);
if (error) {
@@ -228,10 +245,11 @@ ct_dpif_flush_tuple(struct dpif *dpif, const uint16_t *zone,
* in '*zone'. If 'zone' is NULL, use the default zone (zone 0). */
int
ct_dpif_flush(struct dpif *dpif, const uint16_t *zone,
- const struct ct_dpif_tuple *tuple)
+ const struct ct_dpif_tuple *tuple,
+ enum ofputil_ct_direction direction)
{
if (tuple) {
- return ct_dpif_flush_tuple(dpif, zone, tuple);
+ return ct_dpif_flush_tuple(dpif, zone, tuple, direction);
} else if (zone) {
VLOG_DBG("%s: ct_flush: zone %"PRIu16, dpif_name(dpif), *zone);
} else {
@@ -17,6 +17,7 @@
#ifndef CT_DPIF_H
#define CT_DPIF_H
+#include "openvswitch/ofp-util.h"
#include "openvswitch/types.h"
#include "packets.h"
@@ -281,7 +282,8 @@ int ct_dpif_dump_start(struct dpif *, struct ct_dpif_dump_state **,
int ct_dpif_dump_next(struct ct_dpif_dump_state *, struct ct_dpif_entry *);
int ct_dpif_dump_done(struct ct_dpif_dump_state *);
int ct_dpif_flush(struct dpif *, const uint16_t *zone,
- const struct ct_dpif_tuple *);
+ const struct ct_dpif_tuple *,
+ enum ofputil_ct_direction direction);
int ct_dpif_set_maxconns(struct dpif *dpif, uint32_t maxconns);
int ct_dpif_get_maxconns(struct dpif *dpif, uint32_t *maxconns);
int ct_dpif_get_nconns(struct dpif *dpif, uint32_t *nconns);
@@ -35,6 +35,7 @@
#include "dpif.h"
#include "dpif-provider.h"
#include "openvswitch/dynamic-string.h"
+#include "openvswitch/ofp-util.h"
#include "flow.h"
#include "openvswitch/match.h"
#include "netdev.h"
@@ -1737,7 +1738,7 @@ dpctl_flush_conntrack(int argc, const char *argv[],
return error;
}
- error = ct_dpif_flush(dpif, pzone, ptuple);
+ error = ct_dpif_flush(dpif, pzone, ptuple, OFPUTIL_CT_DIRECTION_ORIG);
if (!error) {
dpif_close(dpif);
return 0;
@@ -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;
}
@@ -949,6 +949,44 @@ 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)
+{
+ struct ofputil_ct_tuple tuple;
+ uint16_t zone_id;
+ enum ofperr err = ofp_ct_tuple_decode(&tuple, &zone_id, oh);
+ if (err) {
+ return err;
+ }
+
+ switch (tuple.direction) {
+ case OFPUTIL_CT_DIRECTION_ORIG:
+ ds_put_cstr(string, " direction=orig,");
+ break;
+ case OFPUTIL_CT_DIRECTION_REPLY:
+ ds_put_cstr(string, " direction=reply,");
+ break;
+ }
+
+ ds_put_format(string, "proto=%"PRIu8",zone_id=%"PRIu16,
+ tuple.ip_proto, zone_id);
+ ds_put_cstr(string, ",src=");
+ ipv6_format_mapped(&tuple.src, string);
+ ds_put_cstr(string, ",dst=");
+ ipv6_format_mapped(&tuple.dst, string);
+
+ if (tuple.ip_proto == IPPROTO_ICMP ||
+ tuple.ip_proto == IPPROTO_ICMPV6) {
+ ds_put_format(string, ",id=%"PRIu16",type=%"PRIu8",code=%"PRIu8,
+ ntohs(tuple.icmp_id), tuple.icmp_type, tuple.icmp_code);
+ } else {
+ ds_put_format(string, ",src_port=%"PRIu16",dst_port=%"PRIu16,
+ ntohs(tuple.src_port), ntohs(tuple.dst_port));
+ }
+
+ return 0;
+}
+
static enum ofperr
ofp_to_string__(const struct ofp_header *oh,
const struct ofputil_port_map *port_map,
@@ -1184,6 +1222,9 @@ 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;
@@ -237,3 +237,62 @@ ofputil_encode_barrier_request(enum ofp_version ofp_version)
return ofpraw_alloc(type, ofp_version, 0);
}
+
+struct ofpbuf *
+ofp_ct_tuple_encode(struct ofputil_ct_tuple *tuple, uint16_t zone_id,
+ enum ofputil_ct_direction dir, 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);
+
+ memcpy(&nx_flush->src, &tuple->src, sizeof tuple->src);
+ memcpy(&nx_flush->dst, &tuple->dst, sizeof tuple->dst);
+ nx_flush->ip_proto = tuple->ip_proto;
+ nx_flush->direction = dir;
+ nx_flush->zone_id = htons(zone_id);
+ nx_flush->src_port = tuple->src_port;
+
+ if (tuple->ip_proto == IPPROTO_ICMP || tuple->ip_proto == IPPROTO_ICMPV6) {
+ nx_flush->dst_port = htons(tuple->icmp_type << 8 | tuple->icmp_code);
+ } else {
+ nx_flush->dst_port = tuple->dst_port;
+ }
+
+ return msg;
+}
+
+enum ofperr
+ofp_ct_tuple_decode(struct ofputil_ct_tuple *tuple, uint16_t *zone_id,
+ const struct ofp_header *oh)
+{
+
+ const struct nx_ct_flush *nx_flush = ofpmsg_body(oh);
+
+ switch (nx_flush->direction) {
+ case OFPUTIL_CT_DIRECTION_ORIG:
+ case OFPUTIL_CT_DIRECTION_REPLY:
+ break;
+ default:
+ return EOPNOTSUPP;
+ }
+
+ *zone_id = ntohs(nx_flush->zone_id);
+
+ tuple->ip_proto = nx_flush->ip_proto;
+ tuple->direction = nx_flush->direction;
+
+ memcpy(&tuple->src, &nx_flush->src, sizeof tuple->src);
+ memcpy(&tuple->dst, &nx_flush->dst, sizeof tuple->dst);
+
+ tuple->src_port = nx_flush->src_port;
+
+ if (tuple->ip_proto == IPPROTO_ICMP || tuple->ip_proto == IPPROTO_ICMPV6) {
+ uint16_t icmp = ntohs(nx_flush->dst_port);
+ tuple->icmp_type = icmp >> 8 & 0xff;
+ tuple->icmp_code = icmp & 0xff;
+ } else {
+ tuple->dst_port = nx_flush->dst_port;
+ }
+
+ return 0;
+}
@@ -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;
}
@@ -5358,11 +5358,65 @@ type_set_config(const char *type, const struct smap *other_config)
}
static void
-ct_flush(const struct ofproto *ofproto_, const uint16_t *zone)
+ofp_ct_mapped_ipv6_to_ct_dpif_inet_addr(const struct in6_addr *ipv6,
+ union ct_dpif_inet_addr *addr)
{
+ memset(addr, 0, sizeof *addr);
+
+ if (ipv6_is_zero(ipv6)) {
+ return;
+ }
+
+ if (IN6_IS_ADDR_V4MAPPED(ipv6)) {
+ addr->ip = in6_addr_get_mapped_ipv4(ipv6);
+ } else {
+ addr->in6 = *ipv6;
+ }
+}
+
+static int
+ofputil_ct_tuple_to_ct_dpif_tuple(const struct ofputil_ct_tuple *ofp_tuple,
+ struct ct_dpif_tuple *tuple)
+{
+ tuple->l3_type = AF_INET;
+ if (!ipv6_is_zero(&ofp_tuple->src) &&
+ !IN6_IS_ADDR_V4MAPPED(&ofp_tuple->src)) {
+ tuple->l3_type = AF_INET6;
+ }
+
+
+ ofp_ct_mapped_ipv6_to_ct_dpif_inet_addr(&ofp_tuple->src, &tuple->src);
+ ofp_ct_mapped_ipv6_to_ct_dpif_inet_addr(&ofp_tuple->dst, &tuple->dst);
+
+ tuple->ip_proto = ofp_tuple->ip_proto;
+ tuple->src_port = ofp_tuple->src_port;
+
+ if (tuple->ip_proto == IPPROTO_ICMP ||
+ tuple->ip_proto == IPPROTO_ICMPV6) {
+ tuple->icmp_code = ofp_tuple->icmp_code;
+ tuple->icmp_type = ofp_tuple->icmp_type;
+ } else {
+ tuple->dst_port = ofp_tuple->dst_port;
+ }
+
+ return 0;
+}
+
+static void
+ct_flush(const struct ofproto *ofproto_, const uint16_t *zone,
+ const struct ofputil_ct_tuple *ofp_tuple)
+{
+ struct ct_dpif_tuple tuple;
+ enum ofputil_ct_direction direction =
+ ofp_tuple ? ofp_tuple->direction : OFPUTIL_CT_DIRECTION_ORIG;
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- ct_dpif_flush(ofproto->backer->dpif, zone, NULL);
+ if (ofp_tuple) {
+ ofputil_ct_tuple_to_ct_dpif_tuple(ofp_tuple, &tuple);
+ }
+
+ ct_dpif_flush(ofproto->backer->dpif, zone, ofp_tuple ? &tuple : NULL,
+ direction);
}
static struct ct_timeout_policy *
@@ -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 'tuple' is not NULL,
+ * deletes connections specified by the tuple. */
+ void (*ct_flush)(const struct ofproto *, const uint16_t *zone,
+ const struct ofputil_ct_tuple *tuple);
/* Sets conntrack timeout policy specified by 'timeout_policy' to 'zone'
* in datapath type 'dp_type'. */
@@ -934,7 +934,28 @@ 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_tuple tuple;
+ uint16_t zone_id;
+
+ int err = ofp_ct_tuple_decode(&tuple, &zone_id, oh);
+ if (err) {
+ return err;
+ }
+
+ if (ofproto->ofproto_class->ct_flush) {
+ ofproto->ofproto_class->ct_flush(ofproto, &zone_id, &tuple);
} else {
return EOPNOTSUPP;
}
@@ -8787,6 +8808,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:
@@ -4073,3 +4073,58 @@ 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 38 00 00 00 03 00 00 23 20 00 00 00 20 \
+06 \
+01 \
+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 \
+"], [0], [dnl
+NXT_CT_FLUSH (xid=0x3): direction=orig,proto=6,zone_id=13,src=10.10.0.1,dst=10.10.0.2,src_port=80,dst_port=8080
+])
+
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 38 00 00 00 03 00 00 23 20 00 00 00 20 \
+06 \
+01 \
+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 \
+"], [0], [dnl
+NXT_CT_FLUSH (xid=0x3): direction=orig,proto=6,zone_id=13,src=fd18::ffff:abcd:1,dst=fd18::ffff:abcd:2,src_port=80,dst_port=8080
+])
+
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 38 00 00 00 03 00 00 23 20 00 00 00 20 \
+01 \
+01 \
+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 \
+00 0a \
+"], [0], [dnl
+NXT_CT_FLUSH (xid=0x3): direction=orig,proto=1,zone_id=13,src=10.10.0.1,dst=10.10.0.2,id=1,type=0,code=10
+])
+
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 38 00 00 00 03 00 00 23 20 00 00 00 20 \
+01 \
+01 \
+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 \
+0a 00 \
+"], [0], [dnl
+NXT_CT_FLUSH (xid=0x3): direction=orig,proto=1,zone_id=13,src=fd18::ffff:abcd:1,dst=fd18::ffff:abcd:2,id=1,type=10,code=0
+])
+AT_CLEANUP
Curently there was only extension to flush CT by zone. Add another extension that will allow CT flush by full or partial 5-tuple. The extension can also specify the direction orig or reply. Reported-at: https://bugzilla.redhat.com/2120546 Signed-off-by: Ales Musil <amusil@redhat.com> --- I'm mainly unsure about the overlap between struct ct_dpif_tuple and struct ofputil_ct_tuple, however I was not able to find a good way how to "merge" them because the usage is sligthly different. That's the main reason for RFC, when we come to an agreement what is the best way I'll make it an official series. NEWS | 3 ++ include/openflow/nicira-ext.h | 17 ++++++++++ include/openvswitch/ofp-msgs.h | 4 +++ include/openvswitch/ofp-util.h | 36 +++++++++++++++++++++ lib/ct-dpif.c | 26 ++++++++++++--- lib/ct-dpif.h | 4 ++- lib/dpctl.c | 3 +- lib/ofp-bundle.c | 1 + lib/ofp-print.c | 41 +++++++++++++++++++++++ lib/ofp-util.c | 59 ++++++++++++++++++++++++++++++++++ lib/rconn.c | 1 + ofproto/ofproto-dpif.c | 58 +++++++++++++++++++++++++++++++-- ofproto/ofproto-provider.h | 7 ++-- ofproto/ofproto.c | 26 ++++++++++++++- tests/ofp-print.at | 55 +++++++++++++++++++++++++++++++ 15 files changed, 330 insertions(+), 11 deletions(-)