Add ability to restrict flow mods and flow stats requests to cookies.
authorJustin Pettit <jpettit@nicira.com>
Fri, 23 Dec 2011 20:23:24 +0000 (12:23 -0800)
committerJustin Pettit <jpettit@nicira.com>
Wed, 28 Dec 2011 05:09:51 +0000 (21:09 -0800)
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>
12 files changed:
NEWS
include/openflow/nicira-ext.h
lib/nx-match.c
lib/nx-match.h
lib/ofp-parse.c
lib/ofp-util.c
lib/ofp-util.h
ofproto/ofproto.c
tests/ofproto.at
tests/ovs-ofctl.at
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c

diff --git a/NEWS b/NEWS
index eae3a9708653e60a2d14b5d7d647660c8deb2918..675e4c4fa94bbc968693a35af3bf9ec30141ad1a 100644 (file)
--- 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
index f449329f7b3be6084d54a91c2dcdfaaacbac44e7..1dcd32b5318f58c9b841523e86531e02705911d5 100644 (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
index 9b3c1e0539f6600116cdfc49337deb4ff6505fcf..a93a6939b0a8bded5df53301d31f29744b5da6b3 100644 (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) {
index faeacd6c859c0fbb717de7bb7031d7005e2039ad..c7ee0f8d22430a239c6c0e1f4c56d1c5bc120db2 100644 (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 *);
index 40215511dad0729fc2a5d2070e60e0c616d71a37..38c3dabd4dc06f96ac6878560d02cf6fd75db302 100644 (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;
index 11b0f159ec805a053d8208ed79019f0aefa39576..42dab87f4714125dd1b90e08087a505db34a237b 100644 (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;
index 909467f87ba0bc2d3d97b7fa0767d7cc5c8be89b..8fa729e93359fc01eebc48feca6cdb4ef04de65f 100644 (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;
 };
index 521533b2c07033f8d914b16026af4d3cecec8841..b6f920772f75f67d1a450aee910b5b5995d4ee8a 100644 (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)
index e430800b4db4219f79fe57c283e19feb0bc82aa8..b54d1dd1ba227342641bdf2f029071aae901aa4f 100644 (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
index 2a458c8df3e13c740bccffd3e738b95b1437d0ae..d89d3981bc7c4e047d5a6c4a6e9c25a179b7fc56 100644 (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)
index 5fcc60b5794d486097c7bbd5976188cc05766d04..45574b4df95bc01a1aaf40e7fa689c5f1bbbf19b 100644 (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
index cf77300df68dbc0ffdd1513c6de5bade36ea09c7..edeadfb4fe718f48898f27906e1aff790238e37d 100644 (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);