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 <[email protected]> --- 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 _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
