ofp-actions: Give ovs_instruction funcs names that start with that prefix.
[sliver-openvswitch.git] / lib / ofp-parse.c
index 1d71370..2c1fb5d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@
 #include <errno.h>
 #include <stdlib.h>
 
-#include "autopath.h"
 #include "bundle.h"
 #include "byte-order.h"
 #include "dynamic-string.h"
@@ -46,14 +45,14 @@ static void ofp_fatal(const char *flow, bool verbose, const char *format, ...)
     NO_RETURN;
 
 static uint8_t
-str_to_table_id(const char *str)
+str_to_u8(const char *str, const char *name)
 {
-    int table_id;
+    int value;
 
-    if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) {
-        ovs_fatal(0, "invalid table \"%s\"", str);
+    if (!str_to_int(str, 10, &value) || value < 0 || value > 255) {
+        ovs_fatal(0, "invalid %s \"%s\"", name, str);
     }
-    return table_id;
+    return value;
 }
 
 static uint16_t
@@ -136,7 +135,7 @@ parse_enqueue(char *arg, struct ofpbuf *ofpacts)
     }
 
     enqueue = ofpact_put_ENQUEUE(ofpacts);
-    enqueue->port = str_to_u32(port);
+    enqueue->port = u16_to_ofp(str_to_u32(port));
     enqueue->queue = str_to_u32(queue);
 }
 
@@ -153,7 +152,7 @@ parse_output(char *arg, struct ofpbuf *ofpacts)
         struct ofpact_output *output;
 
         output = ofpact_put_OUTPUT(ofpacts);
-        output->port = str_to_u32(arg);
+        output->port = u16_to_ofp(str_to_u32(arg));
         output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
     }
 }
@@ -318,6 +317,18 @@ parse_dec_ttl(struct ofpbuf *b, char *arg)
     }
 }
 
+static void
+parse_set_mpls_ttl(struct ofpbuf *b, const char *arg)
+{
+    struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(b);
+
+    if (*arg == '\0') {
+        ovs_fatal(0, "parse_set_mpls_ttl: expected ttl.");
+    }
+
+    mpls_ttl->ttl = atoi(arg);
+}
+
 static void
 set_field_parse(const char *arg, struct ofpbuf *ofpacts)
 {
@@ -379,7 +390,35 @@ parse_metadata(struct ofpbuf *b, char *arg)
 }
 
 static void
-parse_named_action(enum ofputil_action_code code, const struct flow *flow,
+parse_sample(struct ofpbuf *b, char *arg)
+{
+    struct ofpact_sample *os = ofpact_put_SAMPLE(b);
+    char *key, *value;
+
+    while (ofputil_parse_key_value(&arg, &key, &value)) {
+        if (!strcmp(key, "probability")) {
+            os->probability = str_to_u16(value, "probability");
+            if (os->probability == 0) {
+                ovs_fatal(0, "invalid probability value \"%s\"", value);
+            }
+        } else if (!strcmp(key, "collector_set_id")) {
+            os->collector_set_id = str_to_u32(value);
+        } else if (!strcmp(key, "obs_domain_id")) {
+            os->obs_domain_id = str_to_u32(value);
+        } else if (!strcmp(key, "obs_point_id")) {
+            os->obs_point_id = str_to_u32(value);
+        } else {
+            ovs_fatal(0, "invalid key \"%s\" in \"sample\" argument",
+                      key);
+        }
+    }
+    if (os->probability == 0) {
+        ovs_fatal(0, "non-zero \"probability\" must be specified on sample");
+    }
+}
+
+static void
+parse_named_action(enum ofputil_action_code code,
                    char *arg, struct ofpbuf *ofpacts)
 {
     struct ofpact_tunnel *tunnel;
@@ -428,12 +467,17 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
     case OFPUTIL_OFPAT11_PUSH_VLAN:
         ethertype = str_to_u16(arg, "ethertype");
         if (ethertype != ETH_TYPE_VLAN_8021Q) {
-            /* TODO:XXXX ETH_TYPE_VLAN_8021AD case isn't supported */
+            /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */
             ovs_fatal(0, "%s: not a valid VLAN ethertype", arg);
         }
         ofpact_put_PUSH_VLAN(ofpacts);
         break;
 
+    case OFPUTIL_OFPAT11_SET_QUEUE:
+        ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
+        break;
+
+
     case OFPUTIL_OFPAT10_SET_DL_SRC:
     case OFPUTIL_OFPAT11_SET_DL_SRC:
         str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
@@ -521,10 +565,6 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
         break;
 
-    case OFPUTIL_NXAST_AUTOPATH__DEPRECATED:
-        autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg);
-        break;
-
     case OFPUTIL_NXAST_BUNDLE:
         bundle_parse(arg, ofpacts);
         break;
@@ -539,7 +579,7 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         NOT_REACHED();
 
     case OFPUTIL_NXAST_LEARN:
-        learn_parse(arg, flow, ofpacts);
+        learn_parse(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_EXIT:
@@ -550,6 +590,16 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         parse_dec_ttl(ofpacts, arg);
         break;
 
+    case OFPUTIL_NXAST_SET_MPLS_TTL:
+    case OFPUTIL_OFPAT11_SET_MPLS_TTL:
+        parse_set_mpls_ttl(ofpacts, arg);
+        break;
+
+    case OFPUTIL_OFPAT11_DEC_MPLS_TTL:
+    case OFPUTIL_NXAST_DEC_MPLS_TTL:
+        ofpact_put_DEC_MPLS_TTL(ofpacts);
+        break;
+
     case OFPUTIL_NXAST_FIN_TIMEOUT:
         parse_fin_timeout(ofpacts, arg);
         break;
@@ -557,16 +607,39 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
     case OFPUTIL_NXAST_CONTROLLER:
         parse_controller(ofpacts, arg);
         break;
+
+    case OFPUTIL_OFPAT11_PUSH_MPLS:
+    case OFPUTIL_NXAST_PUSH_MPLS:
+        ofpact_put_PUSH_MPLS(ofpacts)->ethertype =
+            htons(str_to_u16(arg, "push_mpls"));
+        break;
+
+    case OFPUTIL_OFPAT11_POP_MPLS:
+    case OFPUTIL_NXAST_POP_MPLS:
+        ofpact_put_POP_MPLS(ofpacts)->ethertype =
+            htons(str_to_u16(arg, "pop_mpls"));
+        break;
+
+    case OFPUTIL_NXAST_STACK_PUSH:
+        nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
+        break;
+    case OFPUTIL_NXAST_STACK_POP:
+        nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg);
+        break;
+
+    case OFPUTIL_NXAST_SAMPLE:
+        parse_sample(ofpacts, arg);
+        break;
     }
 }
 
 static bool
-str_to_ofpact__(const struct flow *flow, char *pos, char *act, char *arg,
+str_to_ofpact__(char *pos, char *act, char *arg,
                 struct ofpbuf *ofpacts, int n_actions)
 {
     int code = ofputil_action_code_from_name(act);
     if (code >= 0) {
-        parse_named_action(code, flow, arg, ofpacts);
+        parse_named_action(code, arg, ofpacts);
     } else if (!strcasecmp(act, "drop")) {
         if (n_actions) {
             ovs_fatal(0, "Drop actions must not be preceded by other "
@@ -577,7 +650,7 @@ str_to_ofpact__(const struct flow *flow, char *pos, char *act, char *arg,
         }
         return false;
     } else {
-        uint16_t port;
+        ofp_port_t port;
         if (ofputil_port_from_string(act, &port)) {
             ofpact_put_OUTPUT(ofpacts)->port = port;
         } else {
@@ -589,7 +662,7 @@ str_to_ofpact__(const struct flow *flow, char *pos, char *act, char *arg,
 }
 
 static void
-str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
+str_to_ofpacts(char *str, struct ofpbuf *ofpacts)
 {
     char *pos, *act, *arg;
     enum ofperr error;
@@ -598,7 +671,7 @@ str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
     pos = str;
     n_actions = 0;
     while (ofputil_parse_key_value(&pos, &act, &arg)) {
-        if (!str_to_ofpact__(flow, pos, act, arg, ofpacts, n_actions)) {
+        if (!str_to_ofpact__(pos, act, arg, ofpacts, n_actions)) {
             break;
         }
         n_actions++;
@@ -624,7 +697,7 @@ parse_named_instruction(enum ovs_instruction_type type,
         break;
 
     case OVSINST_OFPIT11_WRITE_ACTIONS:
-        /* TODO:XXX */
+        /* XXX */
         ovs_fatal(0, "instruction write-actions is not supported yet");
         break;
 
@@ -632,6 +705,10 @@ parse_named_instruction(enum ovs_instruction_type type,
         ofpact_put_CLEAR_ACTIONS(ofpacts);
         break;
 
+    case OVSINST_OFPIT13_METER:
+        ofpact_put_METER(ofpacts)->meter_id = str_to_u32(arg);
+        break;
+
     case OVSINST_OFPIT11_WRITE_METADATA:
         parse_metadata(ofpacts, arg);
         break;
@@ -642,7 +719,7 @@ parse_named_instruction(enum ovs_instruction_type type,
         if (!table_s || !table_s[0]) {
             ovs_fatal(0, "instruction goto-table needs table id");
         }
-        ogt->table_id = str_to_table_id(table_s);
+        ogt->table_id = str_to_u8(table_s, "table");
         break;
     }
     }
@@ -656,7 +733,7 @@ parse_named_instruction(enum ovs_instruction_type type,
 }
 
 static void
-str_to_inst_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
+str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
 {
     char *pos, *inst, *arg;
     int type;
@@ -666,9 +743,9 @@ str_to_inst_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
 
     pos = str;
     while (ofputil_parse_key_value(&pos, &inst, &arg)) {
-        type = ofpact_instruction_type_from_name(inst);
+        type = ovs_instruction_type_from_name(inst);
         if (type < 0) {
-            if (!str_to_ofpact__(flow, pos, inst, arg, ofpacts, n_actions)) {
+            if (!str_to_ofpact__(pos, inst, arg, ofpacts, n_actions)) {
                 break;
             }
 
@@ -721,7 +798,9 @@ parse_protocol(const char *name, const struct protocol **p_out)
         { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP },
         { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP },
         { "rarp", ETH_TYPE_RARP, 0},
-};
+        { "mpls", ETH_TYPE_MPLS, 0 },
+        { "mplsm", ETH_TYPE_MPLS_MCAST, 0 },
+    };
     const struct protocol *p;
 
     for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
@@ -858,6 +937,12 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             fm->flags |= OFPFF_SEND_FLOW_REM;
         } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) {
             fm->flags |= OFPFF_CHECK_OVERLAP;
+        } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) {
+            fm->flags |= OFPFF12_RESET_COUNTS;
+        } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) {
+            fm->flags |= OFPFF13_NO_PKT_COUNTS;
+        } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) {
+            fm->flags |= OFPFF13_NO_BYT_COUNTS;
         } else {
             char *value;
 
@@ -867,9 +952,9 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             }
 
             if (!strcmp(name, "table")) {
-                fm->table_id = str_to_table_id(value);
+                fm->table_id = str_to_u8(value, name);
             } else if (!strcmp(name, "out_port")) {
-                if (!ofputil_port_from_string(name, &fm->out_port)) {
+                if (!ofputil_port_from_string(value, &fm->out_port)) {
                     ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port",
                               name);
                 }
@@ -903,7 +988,9 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
                 parse_field(mf_from_name(name), value, &fm->match);
             } else if (!strcmp(name, "duration")
                        || !strcmp(name, "n_packets")
-                       || !strcmp(name, "n_bytes")) {
+                       || !strcmp(name, "n_bytes")
+                       || !strcmp(name, "idle_age")
+                       || !strcmp(name, "hard_age")) {
                 /* Ignore these, so that users can feed the output of
                  * "ovs-ofctl dump-flows" back into commands that parse
                  * flows. */
@@ -921,11 +1008,19 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     }
     if (fields & F_ACTIONS) {
         struct ofpbuf ofpacts;
+        enum ofperr err;
 
         ofpbuf_init(&ofpacts, 32);
-        str_to_inst_ofpacts(&fm->match.flow, act_str, &ofpacts);
+        str_to_inst_ofpacts(act_str, &ofpacts);
         fm->ofpacts_len = ofpacts.size;
         fm->ofpacts = ofpbuf_steal_data(&ofpacts);
+
+        err = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
+                            OFPP_MAX);
+        if (err) {
+            exit(EXIT_FAILURE);
+        }
+
     } else {
         fm->ofpacts_len = 0;
         fm->ofpacts = NULL;
@@ -934,6 +1029,198 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     free(string);
 }
 
+/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
+ * page) into 'mm' for sending the specified meter_mod 'command' to a switch.
+ */
+void
+parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
+                        int command, bool verbose)
+{
+    enum {
+        F_METER = 1 << 0,
+        F_FLAGS = 1 << 1,
+        F_BANDS = 1 << 2,
+    } fields;
+    char *string = xstrdup(str_);
+    char *save_ptr = NULL;
+    char *band_str = NULL;
+    char *name;
+
+    switch (command) {
+    case -1:
+        fields = F_METER;
+        break;
+
+    case OFPMC13_ADD:
+        fields = F_METER | F_FLAGS | F_BANDS;
+        break;
+
+    case OFPMC13_DELETE:
+        fields = F_METER;
+        break;
+
+    case OFPMC13_MODIFY:
+        fields = F_METER | F_FLAGS | F_BANDS;
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    mm->command = command;
+    mm->meter.meter_id = 0;
+    mm->meter.flags = 0;
+    if (fields & F_BANDS) {
+        band_str = strstr(string, "band");
+        if (!band_str) {
+            ofp_fatal(str_, verbose, "must specify bands");
+        }
+        *band_str = '\0';
+
+        band_str = strchr(band_str + 1, '=');
+        if (!band_str) {
+            ofp_fatal(str_, verbose, "must specify bands");
+        }
+
+        band_str++;
+    }
+    for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+         name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+
+        if (fields & F_FLAGS && !strcmp(name, "kbps")) {
+            mm->meter.flags |= OFPMF13_KBPS;
+        } else if (fields & F_FLAGS && !strcmp(name, "pktps")) {
+            mm->meter.flags |= OFPMF13_PKTPS;
+        } else if (fields & F_FLAGS && !strcmp(name, "burst")) {
+            mm->meter.flags |= OFPMF13_BURST;
+        } else if (fields & F_FLAGS && !strcmp(name, "stats")) {
+            mm->meter.flags |= OFPMF13_STATS;
+        } else {
+            char *value;
+
+            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+            if (!value) {
+                ofp_fatal(str_, verbose, "field %s missing value", name);
+            }
+
+            if (!strcmp(name, "meter")) {
+                if (!strcmp(value, "all")) {
+                    mm->meter.meter_id = OFPM13_ALL;
+                } else if (!strcmp(value, "controller")) {
+                    mm->meter.meter_id = OFPM13_CONTROLLER;
+                } else if (!strcmp(value, "slowpath")) {
+                    mm->meter.meter_id = OFPM13_SLOWPATH;
+                } else {
+                    mm->meter.meter_id = str_to_u32(value);
+                    if (mm->meter.meter_id > OFPM13_MAX) {
+                        ofp_fatal(str_, verbose, "invalid value for %s", name);
+                    }
+                }
+            } else {
+                ofp_fatal(str_, verbose, "unknown keyword %s", name);
+            }
+        }
+    }
+    if (fields & F_METER && !mm->meter.meter_id) {
+        ofp_fatal(str_, verbose, "must specify 'meter'");
+    }
+    if (fields & F_FLAGS && !mm->meter.flags) {
+        ofp_fatal(str_, verbose,
+                  "meter must specify either 'kbps' or 'pktps'");
+    }
+
+    if (fields & F_BANDS) {
+        struct ofpbuf bands;
+        uint16_t n_bands = 0;
+        struct ofputil_meter_band *band = NULL;
+        int i;
+
+        ofpbuf_init(&bands, 64);
+
+        for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name;
+             name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+
+            char *value;
+
+            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+            if (!value) {
+                ofp_fatal(str_, verbose, "field %s missing value", name);
+            }
+
+            if (!strcmp(name, "type")) {
+                /* Start a new band */
+                band = ofpbuf_put_zeros(&bands, sizeof *band);
+                n_bands++;
+
+                if (!strcmp(value, "drop")) {
+                    band->type = OFPMBT13_DROP;
+                } else if (!strcmp(value, "dscp_remark")) {
+                    band->type = OFPMBT13_DSCP_REMARK;
+                } else {
+                    ofp_fatal(str_, verbose, "field %s unknown value %s", name,
+                              value);
+                }
+            } else if (!band || !band->type) {
+                ofp_fatal(str_, verbose,
+                          "band must start with the 'type' keyword");
+            } else if (!strcmp(name, "rate")) {
+                band->rate = str_to_u32(value);
+            } else if (!strcmp(name, "burst_size")) {
+                band->burst_size = str_to_u32(value);
+            } else if (!strcmp(name, "prec_level")) {
+                band->prec_level = str_to_u8(value, name);
+            } else {
+                ofp_fatal(str_, verbose, "unknown keyword %s", name);
+            }
+        }
+        /* validate bands */
+        if (!n_bands) {
+            ofp_fatal(str_, verbose, "meter must have bands");
+        }
+
+        mm->meter.n_bands = n_bands;
+        mm->meter.bands = ofpbuf_steal_data(&bands);
+
+        for (i = 0; i < n_bands; ++i) {
+            band = &mm->meter.bands[i];
+
+            if (!band->type) {
+                ofp_fatal(str_, verbose, "band must have 'type'");
+            }
+            if (band->type == OFPMBT13_DSCP_REMARK) {
+                if (!band->prec_level) {
+                    ofp_fatal(str_, verbose, "'dscp_remark' band must have"
+                              " 'prec_level'");
+                }
+            } else {
+                if (band->prec_level) {
+                    ofp_fatal(str_, verbose, "Only 'dscp_remark' band may have"
+                              " 'prec_level'");
+                }
+            }
+            if (!band->rate) {
+                ofp_fatal(str_, verbose, "band must have 'rate'");
+            }
+            if (mm->meter.flags & OFPMF13_BURST) {
+                if (!band->burst_size) {
+                    ofp_fatal(str_, verbose, "band must have 'burst_size' "
+                              "when 'burst' flag is set");
+                }
+            } else {
+                if (band->burst_size) {
+                    ofp_fatal(str_, verbose, "band may have 'burst_size' only "
+                              "when 'burst' flag is set");
+                }
+            }
+        }
+    } else {
+        mm->meter.n_bands = 0;
+        mm->meter.bands = NULL;
+    }
+
+    free(string);
+}
+
 /* Convert 'str_' (as described in the documentation for the "monitor" command
  * in the ovs-ofctl man page) into 'fmr'. */
 void
@@ -983,9 +1270,9 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
             }
 
             if (!strcmp(name, "table")) {
-                fmr->table_id = str_to_table_id(value);
+                fmr->table_id = str_to_u8(value, name);
             } else if (!strcmp(name, "out_port")) {
-                fmr->out_port = atoi(value);
+                fmr->out_port = u16_to_ofp(atoi(value));
             } else if (mf_from_name(name)) {
                 parse_field(mf_from_name(name), value, &fmr->match);
             } else {
@@ -1005,7 +1292,7 @@ void
 parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
 {
     char *s = xstrdup(s_);
-    str_to_ofpacts(NULL, s, ofpacts);
+    str_to_ofpacts(s, ofpacts);
     free(s);
 }
 
@@ -1138,6 +1425,10 @@ parse_ofp_exact_flow(struct flow *flow, const char *s)
         }
     }
 
+    if (!flow->in_port.ofp_port) {
+        flow->in_port.ofp_port = OFPP_NONE;
+    }
+
 exit:
     free(copy);