2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-05 00:35:33 +00:00

Add ability to restrict flow mods and flow stats requests to cookies.

With this commit, it is possible to limit flow deletions and
modifications to specific cookies.  It also provides the ability to
dump flows based on their cookies.

Signed-off-by: Justin Pettit <jpettit@nicira.com>
This commit is contained in:
Justin Pettit
2011-12-23 12:23:24 -08:00
parent a8600e1a65
commit e729e7935e
12 changed files with 285 additions and 46 deletions

3
NEWS
View File

@@ -1,5 +1,8 @@
port-v1.4.0
------------------------
- OpenFlow:
- Added support for querying, modifying, and deleting flows
based on flow cookie when using NXM.
v1.4.0 - xx xxx xxxx

View File

@@ -1641,6 +1641,22 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
* Masking: Not maskable. */
#define NXM_NX_IP_TTL NXM_HEADER (0x0001, 29, 1)
/* Flow cookie.
*
* This may be used to gain the OpenFlow 1.1-like ability to restrict
* certain NXM-based Flow Mod and Flow Stats Request messages to flows
* with specific cookies. See the "nx_flow_mod" and "nx_flow_stats_request"
* structure definitions for more details. This match is otherwise not
* allowed.
*
* Prereqs: None.
*
* Format: 64-bit integer in network byte order.
*
* Masking: Arbitrary masks. */
#define NXM_NX_COOKIE NXM_HEADER (0x0001, 30, 8)
#define NXM_NX_COOKIE_W NXM_HEADER_W(0x0001, 30, 8)
/* ## --------------------- ## */
/* ## Requests and replies. ## */
/* ## --------------------- ## */
@@ -1659,7 +1675,14 @@ struct nxt_set_flow_format {
};
OFP_ASSERT(sizeof(struct nxt_set_flow_format) == 20);
/* NXT_FLOW_MOD (analogous to OFPT_FLOW_MOD). */
/* NXT_FLOW_MOD (analogous to OFPT_FLOW_MOD).
*
* It is possible to limit flow deletions and modifications to certain
* cookies by using the NXM_NX_COOKIE and NXM_NX_COOKIE_W matches. For
* these commands, the "cookie" field is always ignored. Flow additions
* make use of the "cookie" field and ignore any NXM_NX_COOKIE*
* definitions.
*/
struct nx_flow_mod {
struct nicira_header nxh;
ovs_be64 cookie; /* Opaque controller-issued identifier. */
@@ -1708,7 +1731,11 @@ struct nx_flow_removed {
OFP_ASSERT(sizeof(struct nx_flow_removed) == 56);
/* Nicira vendor stats request of type NXST_FLOW (analogous to OFPST_FLOW
* request). */
* request).
*
* It is possible to limit matches to certain cookies by using the
* NXM_NX_COOKIE and NXM_NX_COOKIE_W matches.
*/
struct nx_flow_stats_request {
struct nicira_stats_msg nsm;
ovs_be16 out_port; /* Require matching entries to include this

View File

@@ -96,13 +96,24 @@ nx_entry_ok(const void *p, unsigned int match_len)
return header;
}
/* Parses the nx_match formatted match description in 'b' with length
* 'match_len'. The results are stored in 'rule', which is initialized
* with 'priority'. If 'cookie' and 'cookie_mask' contain valid
* pointers, then the cookie and mask will be stored in them if a
* "NXM_NX_COOKIE*" match is defined. Otherwise, 0 is stored in both.
*
* Returns 0 if successful, otherwise an OpenFlow error code.
*/
int
nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
struct cls_rule *rule)
struct cls_rule *rule,
ovs_be64 *cookie, ovs_be64 *cookie_mask)
{
uint32_t header;
uint8_t *p;
assert((cookie != NULL) == (cookie_mask != NULL));
p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
if (!p) {
VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a "
@@ -112,6 +123,9 @@ nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
}
cls_rule_init_catchall(rule, priority);
if (cookie) {
*cookie = *cookie_mask = htonll(0);
}
while ((header = nx_entry_ok(p, match_len)) != 0) {
unsigned length = NXM_LENGTH(header);
const struct mf_field *mf;
@@ -147,6 +161,23 @@ nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
}
}
/* Check if the match is for a cookie rather than a classifier rule. */
if ((header == NXM_NX_COOKIE || header == NXM_NX_COOKIE_W) && cookie) {
if (*cookie_mask) {
error = NXM_DUP_TYPE;
} else {
unsigned int width = sizeof *cookie;
memcpy(cookie, p + 4, width);
if (NXM_HASMASK(header)) {
memcpy(cookie_mask, p + 4 + width, width);
} else {
*cookie_mask = htonll(UINT64_MAX);
}
error = 0;
}
}
if (error) {
char *msg = ofputil_error_to_string(error);
VLOG_DBG_RL(&rl, "bad nxm_entry %#08"PRIx32" (vendor=%"PRIu32", "
@@ -367,7 +398,9 @@ nxm_put_frag(struct ofpbuf *b, const struct cls_rule *cr)
/* Appends to 'b' the nx_match format that expresses 'cr' (except for
* 'cr->priority', because priority is not part of nx_match), plus enough
* zero bytes to pad the nx_match out to a multiple of 8.
* zero bytes to pad the nx_match out to a multiple of 8. For Flow Mod
* and Flow Stats Requests messages, a 'cookie' and 'cookie_mask' may be
* supplied. Otherwise, 'cookie_mask' should be zero.
*
* This function can cause 'b''s data to be reallocated.
*
@@ -376,7 +409,8 @@ nxm_put_frag(struct ofpbuf *b, const struct cls_rule *cr)
* If 'cr' is a catch-all rule that matches every packet, then this function
* appends nothing to 'b' and returns 0. */
int
nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
ovs_be64 cookie, ovs_be64 cookie_mask)
{
const flow_wildcards_t wc = cr->wc.wildcards;
const struct flow *flow = &cr->flow;
@@ -556,6 +590,9 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
htonl(flow->regs[i]), htonl(cr->wc.reg_masks[i]));
}
/* Cookie. */
nxm_put_64m(b, NXM_NX_COOKIE, cookie, cookie_mask);
match_len = b->size - start_len;
ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
return match_len;
@@ -625,6 +662,10 @@ format_nxm_field_name(struct ds *s, uint32_t header)
if (NXM_HASMASK(header)) {
ds_put_cstr(s, "_W");
}
} else if (header == NXM_NX_COOKIE) {
ds_put_cstr(s, "NXM_NX_COOKIE");
} else if (header == NXM_NX_COOKIE_W) {
ds_put_cstr(s, "NXM_NX_COOKIE_W");
} else {
ds_put_format(s, "%d:%d", NXM_VENDOR(header), NXM_FIELD(header));
}
@@ -641,6 +682,7 @@ parse_nxm_field_name(const char *name, int name_len)
if (wild) {
name_len -= 2;
}
for (i = 0; i < MFF_N_IDS; i++) {
const struct mf_field *mf = mf_from_id(i);
@@ -655,6 +697,15 @@ parse_nxm_field_name(const char *name, int name_len)
}
}
if (!strncmp("NXM_NX_COOKIE", name, name_len)
&& (name_len == strlen("NXM_NX_COOKIE"))) {
if (!wild) {
return NXM_NX_COOKIE;
} else {
return NXM_NX_COOKIE_W;
}
}
/* Check whether it's a 32-bit field header value as hex.
* (This isn't ordinarily useful except for testing error behavior.) */
if (name_len == 8) {

View File

@@ -35,8 +35,9 @@ struct nx_action_reg_move;
*/
int nx_pull_match(struct ofpbuf *, unsigned int match_len, uint16_t priority,
struct cls_rule *);
int nx_put_match(struct ofpbuf *, const struct cls_rule *);
struct cls_rule *, ovs_be64 *cookie, ovs_be64 *cookie_mask);
int nx_put_match(struct ofpbuf *, const struct cls_rule *,
ovs_be64 cookie, ovs_be64 cookie_mask);
char *nx_match_to_string(const uint8_t *, unsigned int match_len);
int nx_match_from_string(const char *, struct ofpbuf *);

View File

@@ -488,7 +488,6 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
enum {
F_OUT_PORT = 1 << 0,
F_ACTIONS = 1 << 1,
F_COOKIE = 1 << 2,
F_TIMEOUT = 1 << 3,
F_PRIORITY = 1 << 4
} fields;
@@ -503,7 +502,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
break;
case OFPFC_ADD:
fields = F_ACTIONS | F_COOKIE | F_TIMEOUT | F_PRIORITY;
fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY;
break;
case OFPFC_DELETE:
@@ -515,11 +514,11 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
break;
case OFPFC_MODIFY:
fields = F_ACTIONS | F_COOKIE;
fields = F_ACTIONS;
break;
case OFPFC_MODIFY_STRICT:
fields = F_ACTIONS | F_COOKIE | F_PRIORITY;
fields = F_ACTIONS | F_PRIORITY;
break;
default:
@@ -528,6 +527,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
fm->cookie = htonll(0);
fm->cookie_mask = htonll(0);
fm->table_id = 0xff;
fm->command = command;
fm->idle_timeout = OFP_FLOW_PERMANENT;
@@ -576,7 +576,18 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
fm->idle_timeout = str_to_u16(value, name);
} else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) {
fm->hard_timeout = str_to_u16(value, name);
} else if (fields & F_COOKIE && !strcmp(name, "cookie")) {
} else if (!strcmp(name, "cookie")) {
char *mask = strchr(value, '/');
if (mask) {
if (command == OFPFC_ADD) {
ofp_fatal(str_, verbose, "flow additions cannot use "
"a cookie mask");
}
*mask = '\0';
fm->cookie_mask = htonll(str_to_u64(mask+1));
} else {
fm->cookie_mask = htonll(UINT64_MAX);
}
fm->cookie = htonll(str_to_u64(value));
} else if (mf_from_name(name)) {
parse_field(mf_from_name(name), value, &fm->cr);
@@ -625,6 +636,9 @@ parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format,
parse_ofp_str(&fm, command, string, verbose);
min_format = ofputil_min_flow_format(&fm.cr);
if (command != OFPFC_ADD && fm.cookie_mask != htonll(0)) {
min_format = NXFF_NXM;
}
next_format = MAX(*cur_format, min_format);
if (next_format != *cur_format) {
struct ofpbuf *sff = ofputil_make_set_flow_format(next_format);
@@ -678,6 +692,8 @@ parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
parse_ofp_str(&fm, -1, string, false);
fsr->aggregate = aggregate;
fsr->cookie = fm.cookie;
fsr->cookie_mask = fm.cookie_mask;
fsr->match = fm.cr;
fsr->out_port = fm.out_port;
fsr->table_id = fm.table_id;

View File

@@ -987,6 +987,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
/* Translate the message. */
fm->cookie = ofm->cookie;
fm->cookie_mask = htonll(UINT64_MAX);
command = ntohs(ofm->command);
fm->idle_timeout = ntohs(ofm->idle_timeout);
fm->hard_timeout = ntohs(ofm->hard_timeout);
@@ -1001,7 +1002,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
/* Dissect the message. */
nfm = ofpbuf_pull(&b, sizeof *nfm);
error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
&fm->cr);
&fm->cr, &fm->cookie, &fm->cookie_mask);
if (error) {
return error;
}
@@ -1011,8 +1012,18 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
}
/* Translate the message. */
fm->cookie = nfm->cookie;
command = ntohs(nfm->command);
if (command == OFPFC_ADD) {
if (fm->cookie_mask) {
/* The "NXM_NX_COOKIE*" matches are not valid for flow
* additions. Additions must use the "cookie" field of
* the "nx_flow_mod" structure. */
return ofp_mkerr(OFPET_BAD_REQUEST, NXBRC_NXM_INVALID);
} else {
fm->cookie = nfm->cookie;
fm->cookie_mask = htonll(UINT64_MAX);
}
}
fm->idle_timeout = ntohs(nfm->idle_timeout);
fm->hard_timeout = ntohs(nfm->hard_timeout);
fm->buffer_id = ntohl(nfm->buffer_id);
@@ -1071,11 +1082,16 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len);
put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg);
match_len = nx_put_match(msg, &fm->cr);
nfm = msg->data;
nfm->cookie = fm->cookie;
nfm->command = htons(command);
if (command == OFPFC_ADD) {
nfm->cookie = fm->cookie;
match_len = nx_put_match(msg, &fm->cr, 0, 0);
} else {
nfm->cookie = 0;
match_len = nx_put_match(msg, &fm->cr,
fm->cookie, fm->cookie_mask);
}
nfm->idle_timeout = htons(fm->idle_timeout);
nfm->hard_timeout = htons(fm->hard_timeout);
nfm->priority = htons(fm->cr.priority);
@@ -1104,6 +1120,7 @@ ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
ofputil_cls_rule_from_match(&ofsr->match, 0, &fsr->match);
fsr->out_port = ntohs(ofsr->out_port);
fsr->table_id = ofsr->table_id;
fsr->cookie = fsr->cookie_mask = htonll(0);
return 0;
}
@@ -1120,7 +1137,8 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
ofpbuf_use_const(&b, oh, ntohs(oh->length));
nfsr = ofpbuf_pull(&b, sizeof *nfsr);
error = nx_pull_match(&b, ntohs(nfsr->match_len), 0, &fsr->match);
error = nx_pull_match(&b, ntohs(nfsr->match_len), 0, &fsr->match,
&fsr->cookie, &fsr->cookie_mask);
if (error) {
return error;
}
@@ -1194,7 +1212,8 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
subtype = fsr->aggregate ? NXST_AGGREGATE : NXST_FLOW;
ofputil_make_stats_request(sizeof *nfsr, OFPST_VENDOR, subtype, &msg);
match_len = nx_put_match(msg, &fsr->match);
match_len = nx_put_match(msg, &fsr->match,
fsr->cookie, fsr->cookie_mask);
nfsr = msg->data;
nfsr->out_port = htons(fsr->out_port);
@@ -1290,7 +1309,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
"claims invalid length %zu", match_len, length);
return EINVAL;
}
if (nx_pull_match(msg, match_len, ntohs(nfs->priority), &fs->rule)) {
if (nx_pull_match(msg, match_len, ntohs(nfs->priority), &fs->rule,
NULL, NULL)) {
return EINVAL;
}
@@ -1374,7 +1394,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
nfs->priority = htons(fs->rule.priority);
nfs->idle_timeout = htons(fs->idle_timeout);
nfs->hard_timeout = htons(fs->hard_timeout);
nfs->match_len = htons(nx_put_match(msg, &fs->rule));
nfs->match_len = htons(nx_put_match(msg, &fs->rule, 0, 0));
memset(nfs->pad2, 0, sizeof nfs->pad2);
nfs->cookie = fs->cookie;
nfs->packet_count = htonll(fs->packet_count);
@@ -1453,7 +1473,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
nfr = ofpbuf_pull(&b, sizeof *nfr);
error = nx_pull_match(&b, ntohs(nfr->match_len), ntohs(nfr->priority),
&fr->rule);
&fr->rule, NULL, NULL);
if (error) {
return error;
}
@@ -1503,7 +1523,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
int match_len;
make_nxmsg_xid(sizeof *nfr, NXT_FLOW_REMOVED, htonl(0), &msg);
match_len = nx_put_match(msg, &fr->rule);
match_len = nx_put_match(msg, &fr->rule, 0, 0);
nfr = msg->data;
nfr->cookie = fr->cookie;

View File

@@ -131,6 +131,7 @@ struct ofpbuf *ofputil_make_flow_mod_table_id(bool flow_mod_table_id);
struct ofputil_flow_mod {
struct cls_rule cr;
ovs_be64 cookie;
ovs_be64 cookie_mask;
uint8_t table_id;
uint16_t command;
uint16_t idle_timeout;
@@ -152,6 +153,8 @@ struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
struct ofputil_flow_stats_request {
bool aggregate; /* Aggregate results? */
struct cls_rule match;
ovs_be64 cookie;
ovs_be64 cookie_mask;
uint16_t out_port;
uint8_t table_id;
};

View File

@@ -2081,8 +2081,9 @@ next_matching_table(struct ofproto *ofproto,
* Returns 0 on success, otherwise an OpenFlow error code. */
static int
collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
const struct cls_rule *match, uint16_t out_port,
struct list *rules)
const struct cls_rule *match,
ovs_be64 cookie, ovs_be64 cookie_mask,
uint16_t out_port, struct list *rules)
{
struct classifier *cls;
int error;
@@ -2102,7 +2103,8 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
if (rule->pending) {
return OFPROTO_POSTPONE;
}
if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)) {
if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)
&& !((rule->flow_cookie ^ cookie) & cookie_mask)) {
list_push_back(rules, &rule->ofproto_node);
}
}
@@ -2123,8 +2125,9 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
* Returns 0 on success, otherwise an OpenFlow error code. */
static int
collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
const struct cls_rule *match, uint16_t out_port,
struct list *rules)
const struct cls_rule *match,
ovs_be64 cookie, ovs_be64 cookie_mask,
uint16_t out_port, struct list *rules)
{
struct classifier *cls;
int error;
@@ -2143,7 +2146,8 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
if (rule->pending) {
return OFPROTO_POSTPONE;
}
if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)) {
if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)
&& !((rule->flow_cookie ^ cookie) & cookie_mask)) {
list_push_back(rules, &rule->ofproto_node);
}
}
@@ -2168,6 +2172,7 @@ handle_flow_stats_request(struct ofconn *ofconn,
}
error = collect_rules_loose(ofproto, fsr.table_id, &fsr.match,
fsr.cookie, fsr.cookie_mask,
fsr.out_port, &rules);
if (error) {
return error;
@@ -2298,6 +2303,7 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
}
error = collect_rules_loose(ofproto, request.table_id, &request.match,
request.cookie, request.cookie_mask,
request.out_port, &rules);
if (error) {
return error;
@@ -2593,8 +2599,9 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
struct list rules;
int error;
error = collect_rules_loose(ofproto, fm->table_id, &fm->cr, OFPP_NONE,
&rules);
error = collect_rules_loose(ofproto, fm->table_id, &fm->cr,
fm->cookie, fm->cookie_mask,
OFPP_NONE, &rules);
return (error ? error
: list_is_empty(&rules) ? add_flow(ofproto, ofconn, fm, request)
: modify_flows__(ofproto, ofconn, fm, request, &rules));
@@ -2613,8 +2620,9 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
struct list rules;
int error;
error = collect_rules_strict(ofproto, fm->table_id, &fm->cr, OFPP_NONE,
&rules);
error = collect_rules_strict(ofproto, fm->table_id, &fm->cr,
fm->cookie, fm->cookie_mask,
OFPP_NONE, &rules);
return (error ? error
: list_is_empty(&rules) ? add_flow(ofproto, ofconn, fm, request)
: list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn,
@@ -2656,8 +2664,9 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
struct list rules;
int error;
error = collect_rules_loose(ofproto, fm->table_id, &fm->cr, fm->out_port,
&rules);
error = collect_rules_loose(ofproto, fm->table_id, &fm->cr,
fm->cookie, fm->cookie_mask,
fm->out_port, &rules);
return (error ? error
: !list_is_empty(&rules) ? delete_flows__(ofproto, ofconn, request,
&rules)
@@ -2673,8 +2682,9 @@ delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
struct list rules;
int error;
error = collect_rules_strict(ofproto, fm->table_id, &fm->cr, fm->out_port,
&rules);
error = collect_rules_strict(ofproto, fm->table_id, &fm->cr,
fm->cookie, fm->cookie_mask,
fm->out_port, &rules);
return (error ? error
: list_is_singleton(&rules) ? delete_flows__(ofproto, ofconn,
request, &rules)

View File

@@ -113,3 +113,91 @@ AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | STRIP_XIDS], [0], [OFPST_FLOW
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_SETUP([ofproto - dump flows with cookie])
OVS_VSWITCHD_START
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=0])
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=0])
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x3,in_port=3,actions=0])
AT_CHECK([ovs-ofctl dump-flows br0 | STRIP_XIDS | STRIP_DURATION | sort], [0], [dnl
cookie=0x1, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=1 actions=output:0
cookie=0x2, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=2 actions=output:0
cookie=0x3, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=3 actions=output:0
NXST_FLOW reply:
])
AT_CHECK([ovs-ofctl dump-aggregate br0 table=0 | STRIP_XIDS], [0], [dnl
NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=3
])
AT_CHECK([ovs-ofctl dump-flows br0 cookie=0x3 | STRIP_XIDS | STRIP_DURATION | sort], [0], [dnl
cookie=0x3, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=3 actions=output:0
NXST_FLOW reply:
])
AT_CHECK([ovs-ofctl dump-aggregate br0 cookie=0x3 | STRIP_XIDS], [0], [dnl
NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=1
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_SETUP([ofproto - dump flows with cookie mask])
OVS_VSWITCHD_START
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=0])
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=0])
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x3,in_port=3,actions=0])
AT_CHECK([ovs-ofctl dump-flows br0 | STRIP_XIDS | STRIP_DURATION | sort], [0], [dnl
cookie=0x1, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=1 actions=output:0
cookie=0x2, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=2 actions=output:0
cookie=0x3, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=3 actions=output:0
NXST_FLOW reply:
])
AT_CHECK([ovs-ofctl dump-aggregate br0 table=0 | STRIP_XIDS], [0], [dnl
NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=3
])
AT_CHECK([ovs-ofctl dump-flows br0 cookie=0x3/0x1 | STRIP_XIDS | STRIP_DURATION | sort], [0], [dnl
cookie=0x1, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=1 actions=output:0
cookie=0x3, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=3 actions=output:0
NXST_FLOW reply:
])
AT_CHECK([ovs-ofctl dump-aggregate br0 cookie=0x3/0x1 | STRIP_XIDS], [0], [dnl
NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=2
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_SETUP([ofproto - del flows with cookie])
OVS_VSWITCHD_START
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=0])
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=0])
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x3,in_port=3,actions=0])
AT_CHECK([ovs-ofctl dump-flows br0 | STRIP_XIDS | STRIP_DURATION | sort], [0], [dnl
cookie=0x1, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=1 actions=output:0
cookie=0x2, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=2 actions=output:0
cookie=0x3, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=3 actions=output:0
NXST_FLOW reply:
])
AT_CHECK([ovs-ofctl del-flows br0 cookie=0x3])
AT_CHECK([ovs-ofctl dump-flows br0 | STRIP_XIDS | STRIP_DURATION | sort], [0], [dnl
cookie=0x1, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=1 actions=output:0
cookie=0x2, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=2 actions=output:0
NXST_FLOW reply:
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_SETUP([ofproto - del flows with cookie mask])
OVS_VSWITCHD_START
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=0])
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=0])
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x3,in_port=3,actions=0])
AT_CHECK([ovs-ofctl dump-flows br0 | STRIP_XIDS | STRIP_DURATION | sort], [0], [dnl
cookie=0x1, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=1 actions=output:0
cookie=0x2, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=2 actions=output:0
cookie=0x3, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=3 actions=output:0
NXST_FLOW reply:
])
AT_CHECK([ovs-ofctl del-flows br0 cookie=0x3/0x1])
AT_CHECK([ovs-ofctl dump-flows br0 | STRIP_XIDS | STRIP_DURATION | sort], [0], [dnl
cookie=0x2, duration=?s, table=0, n_packets=0, n_bytes=0, in_port=2 actions=output:0
NXST_FLOW reply:
])
OVS_VSWITCHD_STOP
AT_CLEANUP

View File

@@ -346,6 +346,10 @@ NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(02/02)
NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(03/03)
NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG(f3)
# Flow cookie.
NXM_NX_COOKIE(00000000abcdef01)
NXM_NX_COOKIE_W(84200000abcdef01/84200000FFFFFFFF)
# Tunnel ID.
NXM_NX_TUN_ID(00000000abcdef01)
NXM_NX_TUN_ID_W(84200000abcdef01/84200000FFFFFFFF)
@@ -535,6 +539,10 @@ NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(02/02)
NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(03)
nx_pull_match() returned error 44010102 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_VALUE)
# Flow cookie.
NXM_NX_COOKIE(00000000abcdef01)
NXM_NX_COOKIE_W(84200000abcdef01/84200000ffffffff)
# Tunnel ID.
NXM_NX_TUN_ID(00000000abcdef01)
NXM_NX_TUN_ID_W(84200000abcdef01/84200000ffffffff)

View File

@@ -933,15 +933,22 @@ further actions, including those which may be in other tables, or different
levels of the \fBresubmit\fR call stack, are ignored.
.
.PP
The \fBadd\-flow\fR, \fBadd\-flows\fR, and \fBmod\-flows\fR commands
support an additional optional field:
An opaque identifier called a cookie can be used as a handle to identify
a set of flows:
.
.IP \fBcookie=\fIvalue\fR
.IP \fBcookie=\fIvalue\fR[\fB/\fImask\fR]
.
A cookie is an opaque identifier that can be associated with the flow.
\fIvalue\fR can be any 64-bit number and need not be unique among
flows. If this field is omitted, these commands set a default cookie
value of 0.
A cookie can be associated with a flow using the \fBadd-flow\fR and
\fBadd-flows\fR commands. \fIvalue\fR can be any 64-bit number and need
not be unique among flows. If this field is omitted, a default cookie
value of 0 is used.
.IP
When using NXM, the cookie can be used as a handle for querying,
modifying, and deleting flows. In addition to \fIvalue\fR, an optional
\fImask\fR may be supplied for the \fBdel-flows\fR, \fBmod-flows\fR,
\fBdump-flows\fR, and \fBdump-aggregate\fR commands to limit matching
cookies. A 1-bit in \fImask\fR indicates that the corresponding bit in
\fIcookie\fR must match exactly, and a 0-bit wildcards that bit.
.
.PP
The following additional field sets the priority for flows added by

View File

@@ -586,6 +586,9 @@ do_dump_flows__(int argc, char *argv[], bool aggregate)
open_vconn(argv[1], &vconn);
min_flow_format = ofputil_min_flow_format(&fsr.match);
if (fsr.cookie_mask != htonll(0)) {
min_flow_format = NXFF_NXM;
}
flow_format = negotiate_highest_flow_format(vconn, min_flow_format);
request = ofputil_encode_flow_stats_request(&fsr, flow_format);
dump_stats_transaction(argv[1], request);
@@ -1455,6 +1458,7 @@ do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
while (!ds_get_line(&in, stdin)) {
struct ofpbuf nx_match;
struct cls_rule rule;
ovs_be64 cookie, cookie_mask;
int match_len;
int error;
char *s;
@@ -1478,14 +1482,15 @@ do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
match_len = nx_match_from_string(ds_cstr(&in), &nx_match);
/* Convert nx_match to cls_rule. */
error = nx_pull_match(&nx_match, match_len, 0, &rule);
error = nx_pull_match(&nx_match, match_len, 0, &rule,
&cookie, &cookie_mask);
if (!error) {
char *out;
/* Convert cls_rule back to nx_match. */
ofpbuf_uninit(&nx_match);
ofpbuf_init(&nx_match, 0);
match_len = nx_put_match(&nx_match, &rule);
match_len = nx_put_match(&nx_match, &rule, cookie, cookie_mask);
/* Convert nx_match to string. */
out = nx_match_to_string(nx_match.data, match_len);