2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

openflow: Add extension to flush CT by generic match.

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 <amusil@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
Ales Musil 2023-01-16 12:45:08 +01:00 committed by Ilya Maximets
parent a9ae73b916
commit 08146bf7d9
16 changed files with 562 additions and 22 deletions

6
NEWS
View File

@ -21,6 +21,9 @@ Post-v3.0.0
10 Gbps link speed by default in case the actual link speed cannot be
determined. Previously it was 10 Mbps. Values can still be overridden
by specifying 'max-rate' or '[r]stp-path-cost' accordingly.
- OpenFlow:
* New OpenFlow extension NXT_CT_FLUSH to flush connections matching
the specified fields.
- ovs-ctl:
* New option '--dump-hugepages' to include hugepages in core dumps. This
can assist with postmortem analysis involving DPDK, but may also produce
@ -28,6 +31,9 @@ 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.
- ovs-ofctl:
* New command 'flush-conntrack' that accepts zone and 5-tuple (or partial
5-tuple) for both directions.
- Support for travis-ci.org based continuous integration builds has been
dropped.
- Userspace datapath:

View File

@ -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.
* Nested TLVs are specified
* by 'enum nx_ct_flush_tuple_tlv_type'. */
NXT_CT_REPLY_TUPLE = 1, /* Outer type for reply tuple TLV. *
* Nested TLVs 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 entries 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]; /* Align to 64 bits (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 */

View File

@ -22,6 +22,8 @@
#include <sys/types.h>
#include <netinet/in.h>
#include "openflow/nicira-ext.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -59,6 +61,12 @@ void ofp_ct_match_format(struct ds *, const struct ofp_ct_match *);
bool ofp_ct_tuple_parse(struct ofp_ct_tuple *, const char *,
struct ds *, uint8_t *ip_proto, uint16_t *l3_type);
enum ofperr ofp_ct_match_decode(struct ofp_ct_match *, bool *with_zone,
uint16_t *zone_id, const struct ofp_header *);
struct ofpbuf *ofp_ct_match_encode(const struct ofp_ct_match *,
uint16_t *zone_id,
enum ofp_version version);
#ifdef __cplusplus
}
#endif

View File

@ -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.

View File

@ -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;
}

View File

@ -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"
@ -211,3 +215,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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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'. */

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -296,6 +296,37 @@ 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-orig-tuple [ct-reply-tuple]]\fR
Flushes the connection entries on \fIswitch\fR based on \fIzone\fR and
connection tracking tuples \fIct-[orig|reply]-tuple\fR.
.IP
If \fIct-[orig|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-[orig|reply]-tuple\fR is provided, flushes the connection entry
specified by \fIct-[orig|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-orig-tuple\fR.
.IP
Note: Currently there is limitation for matching on ICMP, in order to partially
match on ICMP parameters the \fIct-[orig|reply]-tuple\fR has to include
either source or destination IP.
.IP
An example of an IPv4 ICMP \fIct-[orig|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-[orig|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"
.IP
This command uses an Open vSwitch extension that is only in Open vSwitch 3.1
and later.
.
.SS "OpenFlow Switch Flow Table Commands"
.
These commands manage the flow table in an OpenFlow switch. In each

View File

@ -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",