From: Justin Pettit Date: Fri, 23 Dec 2011 20:23:24 +0000 (-0800) Subject: Add ability to restrict flow mods and flow stats requests to cookies. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e729e7935e5c77eae1ca4a8040d05626f61cf9a2;p=openvswitch 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 --- diff --git a/NEWS b/NEWS index eae3a970..675e4c4f 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index f449329f..1dcd32b5 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -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 diff --git a/lib/nx-match.c b/lib/nx-match.c index 9b3c1e05..a93a6939 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -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) { diff --git a/lib/nx-match.h b/lib/nx-match.h index faeacd6c..c7ee0f8d 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 40215511..38c3dabd 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 11b0f159..42dab87f 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(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; diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 909467f8..8fa729e9 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 521533b2..b6f92077 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,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) diff --git a/tests/ofproto.at b/tests/ofproto.at index e430800b..b54d1dd1 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 2a458c8d..d89d3981 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 5fcc60b5..45574b4d 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -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 diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index cf77300d..edeadfb4 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);