[ovs-dev] [PATCH 2/2] Add ability to restrict flow mods and flow stats requests to cookies.
Justin Pettit
jpettit at nicira.com
Fri Dec 23 21:53:14 UTC 2011
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 at nicira.com>
---
include/openflow/nicira-ext.h | 30 +++++++++++++-
lib/nx-match.c | 44 +++++++++++++++++++-
lib/nx-match.h | 5 +-
lib/ofp-parse.c | 26 ++++++++++--
lib/ofp-util.c | 38 ++++++++++++-----
lib/ofp-util.h | 3 +
ofproto/ofproto.c | 40 ++++++++++++------
tests/ofproto.at | 88 +++++++++++++++++++++++++++++++++++++++++
tests/ovs-ofctl.at | 8 ++++
utilities/ovs-ofctl.8.in | 20 ++++++---
utilities/ovs-ofctl.c | 9 +++-
11 files changed, 265 insertions(+), 46 deletions(-)
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index f449329..bccde56 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -1641,6 +1641,21 @@ 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 ignored.
+ *
+ * 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 +1674,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 +1730,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
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 9b3c1e0..86b3658 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -98,7 +98,8 @@ nx_entry_ok(const void *p, unsigned int match_len)
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;
@@ -112,6 +113,9 @@ nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
}
cls_rule_init_catchall(rule, priority);
+ if (cookie && cookie_mask) {
+ *cookie = *cookie_mask = 0;
+ }
while ((header = nx_entry_ok(p, match_len)) != 0) {
unsigned length = NXM_LENGTH(header);
const struct mf_field *mf;
@@ -147,6 +151,24 @@ 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 && cookie_mask) {
+ 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 +389,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 +400,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 +581,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 +653,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));
}
@@ -652,6 +684,12 @@ parse_nxm_field_name(const char *name, int name_len)
} else if (mf->maskable != MFM_NONE) {
return NXM_MAKE_WILD_HEADER(mf->nxm_header);
}
+ } else if (!strncmp("NXM_NX_COOKIE", name, name_len)) {
+ if (!wild) {
+ return NXM_NX_COOKIE;
+ } else {
+ return NXM_NX_COOKIE_W;
+ }
}
}
diff --git a/lib/nx-match.h b/lib/nx-match.h
index faeacd6..c7ee0f8 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -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 *);
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 4021551..38c3dab 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -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;
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 11b0f15..6b3e5f0 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -987,6 +987,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
/* Translate the message. */
fm->cookie = ofm->cookie;
+ fm->cookie_mask = htonll(0);
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,14 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
}
/* Translate the message. */
- fm->cookie = nfm->cookie;
command = ntohs(nfm->command);
+ if (command == OFPFC_ADD) {
+ /* In NXM, the "cookie" field from the Flow Mod is only
+ * valid for flow additions. In that case, we don't use the
+ * NXM_NX_COOKIE definition. */
+ 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 +1078,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 +1116,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 +1133,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 +1208,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 +1305,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 +1390,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 +1469,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 +1519,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;
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 909467f..8fa729e 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -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;
};
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 521533b..cb06a4d 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -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,9 @@ 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_mask)
+ == (cookie & cookie_mask)) {
list_push_back(rules, &rule->ofproto_node);
}
}
@@ -2123,8 +2126,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 +2147,9 @@ 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_mask)
+ == (cookie & cookie_mask)) {
list_push_back(rules, &rule->ofproto_node);
}
}
@@ -2168,6 +2174,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 +2305,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 +2601,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 +2622,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 +2666,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 +2684,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)
diff --git a/tests/ofproto.at b/tests/ofproto.at
index e430800..b54d1dd 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -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
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 2a458c8..d89d398 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -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)
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 5fcc60b..12207f8 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -933,15 +933,21 @@ 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 associated with a flow:
+.
+.IP \fBcookie=\fIvalue\fR[\fB/\fImask\fR]
.
-.IP \fBcookie=\fIvalue\fR
+A cookie can be associated with a flow with the \fBadd-flow\fR and
+\fBadd-flows\fR commands. When using NXM, the cookie can be used as a
+handle for querying, modifying, and deleting flows. \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.
.
-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.
+When using NXM, 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
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index cf77300..edeadfb 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -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);
--
1.7.4.1
More information about the dev
mailing list