ofproto: New action TTL decrement.
[sliver-openvswitch.git] / lib / ofp-parse.c
index a80d45f..5321364 100644 (file)
@@ -26,6 +26,7 @@
 #include "bundle.h"
 #include "byte-order.h"
 #include "dynamic-string.h"
+#include "learn.h"
 #include "meta-flow.h"
 #include "netdev.h"
 #include "multipath.h"
 
 VLOG_DEFINE_THIS_MODULE(ofp_parse);
 
+static uint8_t
+str_to_table_id(const char *str)
+{
+    int table_id;
+
+    if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) {
+        ovs_fatal(0, "invalid table \"%s\"", str);
+    }
+    return table_id;
+}
+
+static uint16_t
+str_to_u16(const char *str, const char *name)
+{
+    int value;
+
+    if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) {
+        ovs_fatal(0, "invalid %s \"%s\"", name, str);
+    }
+    return value;
+}
+
 static uint32_t
 str_to_u32(const char *str)
 {
@@ -228,7 +251,8 @@ parse_note(struct ofpbuf *b, const char *arg)
 }
 
 static void
-parse_named_action(enum ofputil_action_code code, struct ofpbuf *b, char *arg)
+parse_named_action(enum ofputil_action_code code, const struct flow *flow,
+                   struct ofpbuf *b, char *arg)
 {
     struct ofp_action_dl_addr *oada;
     struct ofp_action_vlan_pcp *oavp;
@@ -332,11 +356,23 @@ parse_named_action(enum ofputil_action_code code, struct ofpbuf *b, char *arg)
     case OFPUTIL_NXAST_RESUBMIT_TABLE:
     case OFPUTIL_NXAST_OUTPUT_REG:
         NOT_REACHED();
+
+    case OFPUTIL_NXAST_LEARN:
+        learn_parse(b, arg, flow);
+        break;
+
+    case OFPUTIL_NXAST_EXIT:
+        ofputil_put_NXAST_EXIT(b);
+        break;
+
+    case OFPUTIL_NXAST_DEC_TTL:
+        ofputil_put_NXAST_DEC_TTL(b);
+        break;
     }
 }
 
 static void
-str_to_action(char *str, struct ofpbuf *b)
+str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
 {
     char *pos, *act, *arg;
     int n_actions;
@@ -349,7 +385,7 @@ str_to_action(char *str, struct ofpbuf *b)
 
         code = ofputil_action_code_from_name(act);
         if (code >= 0) {
-            parse_named_action(code, b, arg);
+            parse_named_action(code, flow, b, arg);
         } else if (!strcasecmp(act, "drop")) {
             /* A drop action in OpenFlow occurs by just not setting
              * an action. */
@@ -456,12 +492,12 @@ 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;
     char *string = xstrdup(str_);
     char *save_ptr = NULL;
+    char *act_str = NULL;
     char *name;
 
     switch (command) {
@@ -470,7 +506,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:
@@ -482,11 +518,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:
@@ -495,6 +531,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;
@@ -503,9 +540,6 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     fm->out_port = OFPP_NONE;
     fm->flags = 0;
     if (fields & F_ACTIONS) {
-        struct ofpbuf actions;
-        char *act_str;
-
         act_str = strstr(string, "action");
         if (!act_str) {
             ofp_fatal(str_, verbose, "must specify an action");
@@ -518,14 +552,6 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         }
 
         act_str++;
-
-        ofpbuf_init(&actions, sizeof(union ofp_action));
-        str_to_action(act_str, &actions);
-        fm->actions = ofpbuf_steal_data(&actions);
-        fm->n_actions = actions.size / sizeof(union ofp_action);
-    } else {
-        fm->actions = NULL;
-        fm->n_actions = 0;
     }
     for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
          name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
@@ -545,16 +571,27 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             }
 
             if (!strcmp(name, "table")) {
-                fm->table_id = atoi(value);
+                fm->table_id = str_to_table_id(value);
             } else if (!strcmp(name, "out_port")) {
                 fm->out_port = atoi(value);
             } else if (fields & F_PRIORITY && !strcmp(name, "priority")) {
-                fm->cr.priority = atoi(value);
+                fm->cr.priority = str_to_u16(value, name);
             } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) {
-                fm->idle_timeout = atoi(value);
+                fm->idle_timeout = str_to_u16(value, name);
             } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) {
-                fm->hard_timeout = atoi(value);
-            } else if (fields & F_COOKIE && !strcmp(name, "cookie")) {
+                fm->hard_timeout = str_to_u16(value, name);
+            } 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);
@@ -569,6 +606,17 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             }
         }
     }
+    if (fields & F_ACTIONS) {
+        struct ofpbuf actions;
+
+        ofpbuf_init(&actions, sizeof(union ofp_action));
+        str_to_action(&fm->cr.flow, act_str, &actions);
+        fm->actions = ofpbuf_steal_data(&actions);
+        fm->n_actions = actions.size / sizeof(union ofp_action);
+    } else {
+        fm->actions = NULL;
+        fm->n_actions = 0;
+    }
 
     free(string);
 }
@@ -586,14 +634,15 @@ parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format,
 {
     enum nx_flow_format min_format, next_format;
     struct cls_rule rule_copy;
-    struct ofpbuf actions;
     struct ofpbuf *ofm;
     struct ofputil_flow_mod fm;
 
-    ofpbuf_init(&actions, 64);
     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);
@@ -615,8 +664,6 @@ parse_ofp_flow_mod_str(struct list *packets, enum nx_flow_format *cur_format,
 
     ofm = ofputil_encode_flow_mod(&fm, *cur_format, *flow_mod_table_id);
     list_push_back(packets, &ofm->list_node);
-
-    ofpbuf_uninit(&actions);
 }
 
 /* Similar to parse_ofp_flow_mod_str(), except that the string is read from
@@ -649,6 +696,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;