OF 1.1 pop vlan compatibility.
[sliver-openvswitch.git] / lib / ofp-parse.c
index 0e0d2f8..1ada870 100644 (file)
@@ -599,10 +599,12 @@ parse_named_action(enum ofputil_action_code code,
 {
     size_t orig_size = ofpacts->size;
     struct ofpact_tunnel *tunnel;
+    struct ofpact_vlan_vid *vlan_vid;
+    struct ofpact_vlan_pcp *vlan_pcp;
     char *error = NULL;
     uint16_t ethertype = 0;
     uint16_t vid = 0;
-    uint8_t tos = 0;
+    uint8_t tos = 0, ecn, ttl;
     uint8_t pcp = 0;
 
     switch (code) {
@@ -624,7 +626,10 @@ parse_named_action(enum ofputil_action_code code,
         if (vid & ~VLAN_VID_MASK) {
             return xasprintf("%s: not a valid VLAN VID", arg);
         }
-        ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid;
+        vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts);
+        vlan_vid->vlan_vid = vid;
+        vlan_vid->ofpact.compat = code;
+        vlan_vid->push_vlan_if_needed = code == OFPUTIL_OFPAT10_SET_VLAN_VID;
         break;
 
     case OFPUTIL_OFPAT10_SET_VLAN_PCP:
@@ -637,7 +642,10 @@ parse_named_action(enum ofputil_action_code code,
         if (pcp & ~7) {
             return xasprintf("%s: not a valid VLAN PCP", arg);
         }
-        ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
+        vlan_pcp = ofpact_put_SET_VLAN_PCP(ofpacts);
+        vlan_pcp->vlan_pcp = pcp;
+        vlan_pcp->ofpact.compat = code;
+        vlan_pcp->push_vlan_if_needed = code == OFPUTIL_OFPAT10_SET_VLAN_PCP;
         break;
 
     case OFPUTIL_OFPAT12_SET_FIELD:
@@ -645,7 +653,7 @@ parse_named_action(enum ofputil_action_code code,
 
     case OFPUTIL_OFPAT10_STRIP_VLAN:
     case OFPUTIL_OFPAT11_POP_VLAN:
-        ofpact_put_STRIP_VLAN(ofpacts);
+        ofpact_put_STRIP_VLAN(ofpacts)->ofpact.compat = code;
         break;
 
     case OFPUTIL_OFPAT11_PUSH_VLAN:
@@ -700,6 +708,27 @@ parse_named_action(enum ofputil_action_code code,
         ofpact_put_SET_IP_DSCP(ofpacts)->dscp = tos;
         break;
 
+    case OFPUTIL_OFPAT11_SET_NW_ECN:
+        error = str_to_u8(arg, "ECN", &ecn);
+        if (error) {
+            return error;
+        }
+
+        if (ecn & ~IP_ECN_MASK) {
+            return xasprintf("%s: not a valid ECN", arg);
+        }
+        ofpact_put_SET_IP_ECN(ofpacts)->ecn = ecn;
+        break;
+
+    case OFPUTIL_OFPAT11_SET_NW_TTL:
+        error = str_to_u8(arg, "TTL", &ttl);
+        if (error) {
+            return error;
+        }
+
+        ofpact_put_SET_IP_TTL(ofpacts)->ttl = ttl;
+        break;
+
     case OFPUTIL_OFPAT11_DEC_NW_TTL:
         NOT_REACHED();
 
@@ -1131,7 +1160,8 @@ parse_field(const struct mf_field *mf, const char *s, struct match *match,
 
 static char * WARN_UNUSED_RESULT
 parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
-                enum ofputil_protocol *usable_protocols)
+                enum ofputil_protocol *usable_protocols,
+                bool enforce_consistency)
 {
     enum {
         F_OUT_PORT = 1 << 0,
@@ -1339,10 +1369,21 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
             enum ofperr err;
 
             err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow,
-                                OFPP_MAX, 0);
+                                OFPP_MAX, 0, true);
             if (err) {
-                error = xasprintf("actions are invalid with specified match "
-                                  "(%s)", ofperr_to_string(err));
+                if (!enforce_consistency &&
+                    err == OFPERR_OFPBAC_MATCH_INCONSISTENT) {
+                    /* Allow inconsistent actions to be used with OF 1.0. */
+                    *usable_protocols &= OFPUTIL_P_OF10_ANY;
+                    /* Try again, allowing for inconsistency.
+                     * XXX: As a side effect, logging may be duplicated. */
+                    err = ofpacts_check(ofpacts.data, ofpacts.size,
+                                        &fm->match.flow, OFPP_MAX, 0, false);
+                }
+                if (err) {
+                    error = xasprintf("actions are invalid with specified match "
+                                      "(%s)", ofperr_to_string(err));
+                }
             }
         }
         if (error) {
@@ -1372,12 +1413,14 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
  * error.  The caller is responsible for freeing the returned string. */
 char * WARN_UNUSED_RESULT
 parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
-              enum ofputil_protocol *usable_protocols)
+              enum ofputil_protocol *usable_protocols,
+              bool enforce_consistency)
 {
     char *string = xstrdup(str_);
     char *error;
 
-    error = parse_ofp_str__(fm, command, string, usable_protocols);
+    error = parse_ofp_str__(fm, command, string, usable_protocols,
+                            enforce_consistency);
     if (error) {
         fm->ofpacts = NULL;
         fm->ofpacts_len = 0;
@@ -1724,9 +1767,11 @@ parse_ofpacts(const char *s_, struct ofpbuf *ofpacts,
 char * WARN_UNUSED_RESULT
 parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
                        uint16_t command,
-                       enum ofputil_protocol *usable_protocols)
+                       enum ofputil_protocol *usable_protocols,
+                       bool enforce_consistency)
 {
-    char *error = parse_ofp_str(fm, command, string, usable_protocols);
+    char *error = parse_ofp_str(fm, command, string, usable_protocols,
+                                enforce_consistency);
     if (!error) {
         /* Normalize a copy of the match.  This ensures that non-normalized
          * flows get logged but doesn't affect what gets sent to the switch, so
@@ -1788,7 +1833,8 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
 char * WARN_UNUSED_RESULT
 parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
                         struct ofputil_flow_mod **fms, size_t *n_fms,
-                        enum ofputil_protocol *usable_protocols)
+                        enum ofputil_protocol *usable_protocols,
+                        bool enforce_consistency)
 {
     size_t allocated_fms;
     int line_number;
@@ -1817,7 +1863,7 @@ parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
             *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms);
         }
         error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command,
-                                       &usable);
+                                       &usable, enforce_consistency);
         if (error) {
             size_t i;
 
@@ -1849,12 +1895,14 @@ parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
 char * WARN_UNUSED_RESULT
 parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
                                  bool aggregate, const char *string,
-                                 enum ofputil_protocol *usable_protocols)
+                                 enum ofputil_protocol *usable_protocols,
+                                 bool enforce_consistency)
 {
     struct ofputil_flow_mod fm;
     char *error;
 
-    error = parse_ofp_str(&fm, -1, string, usable_protocols);
+    error = parse_ofp_str(&fm, -1, string, usable_protocols,
+                          enforce_consistency);
     if (error) {
         return error;
     }