Better abstract OFPT_SET_CONFIG and OFPT_GET_CONFIG_REPLY, make stricter.
[cascardo/ovs.git] / lib / ofp-util.c
index 3058df8..effd96a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -137,6 +137,56 @@ log_property(bool loose, const char *message, ...)
     }
 }
 
+static enum ofperr
+ofputil_check_mask(uint16_t type, uint32_t mask)
+{
+    switch (type) {
+    case OFPACPT_PACKET_IN_SLAVE:
+    case OFPACPT_PACKET_IN_MASTER:
+        if (mask > MAXIMUM_MASK_PACKET_IN) {
+            return OFPERR_OFPACFC_INVALID;
+        }
+        break;
+
+    case OFPACPT_FLOW_REMOVED_SLAVE:
+    case OFPACPT_FLOW_REMOVED_MASTER:
+        if (mask > MAXIMUM_MASK_FLOW_REMOVED) {
+            return OFPERR_OFPACFC_INVALID;
+        }
+        break;
+
+    case OFPACPT_PORT_STATUS_SLAVE:
+    case OFPACPT_PORT_STATUS_MASTER:
+        if (mask > MAXIMUM_MASK_PORT_STATUS) {
+            return OFPERR_OFPACFC_INVALID;
+        }
+        break;
+
+    case OFPACPT_ROLE_STATUS_SLAVE:
+    case OFPACPT_ROLE_STATUS_MASTER:
+        if (mask > MAXIMUM_MASK_ROLE_STATUS) {
+            return OFPERR_OFPACFC_INVALID;
+        }
+        break;
+
+    case OFPACPT_TABLE_STATUS_SLAVE:
+    case OFPACPT_TABLE_STATUS_MASTER:
+        if ((mask < MINIMUM_MASK_TABLE_STATUS && mask != 0) |
+            (mask > MAXIMUM_MASK_TABLE_STATUS)) {
+            return OFPERR_OFPACFC_INVALID;
+        }
+        break;
+
+    case OFPACPT_REQUESTFORWARD_SLAVE:
+    case OFPACPT_REQUESTFORWARD_MASTER:
+        if (mask > MAXIMUM_MASK_REQUESTFORWARD) {
+            return OFPERR_OFPACFC_INVALID;
+        }
+        break;
+    }
+    return 0;
+}
+
 static size_t
 start_property(struct ofpbuf *msg, uint16_t type)
 {
@@ -1983,12 +2033,14 @@ ofputil_append_meter_config(struct ovs_list *replies,
 {
     struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
     size_t start_ofs = msg->size;
-    struct ofp13_meter_config *reply = ofpbuf_put_uninit(msg, sizeof *reply);
-    reply->flags = htons(mc->flags);
-    reply->meter_id = htonl(mc->meter_id);
+    struct ofp13_meter_config *reply;
 
+    ofpbuf_put_uninit(msg, sizeof *reply);
     ofputil_put_bands(mc->n_bands, mc->bands, msg);
 
+    reply = ofpbuf_at_assert(msg, start_ofs, sizeof *reply);
+    reply->flags = htons(mc->flags);
+    reply->meter_id = htonl(mc->meter_id);
     reply->length = htons(msg->size - start_ofs);
 
     ofpmp_postappend(replies, start_ofs);
@@ -2440,14 +2492,23 @@ ofputil_decode_queue_get_config_request(const struct ofp_header *oh,
     case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST:
         qgcr10 = b.data;
         *port = u16_to_ofp(ntohs(qgcr10->port));
-        return 0;
+        break;
 
     case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST:
         qgcr11 = b.data;
-        return ofputil_port_from_ofp11(qgcr11->port, port);
+        enum ofperr error = ofputil_port_from_ofp11(qgcr11->port, port);
+        if (error || *port == OFPP_ANY) {
+            return error;
+        }
+        break;
+
+    default:
+        OVS_NOT_REACHED();
     }
 
-    OVS_NOT_REACHED();
+    return (ofp_to_u16(*port) < ofp_to_u16(OFPP_MAX)
+            ? 0
+            : OFPERR_OFPQOFC_BAD_PORT);
 }
 
 /* Constructs and returns the beginning of a reply to
@@ -2524,15 +2585,10 @@ ofputil_append_queue_get_config_reply(struct ofpbuf *reply,
         opq10->queue_id = htonl(oqc->queue_id);
         len_ofs = (char *) &opq10->len - (char *) reply->data;
     } else {
-        struct ofp11_queue_get_config_reply *qgcr11;
         struct ofp12_packet_queue *opq12;
-        ovs_be32 port;
-
-        qgcr11 = reply->msg;
-        port = qgcr11->port;
 
         opq12 = ofpbuf_put_zeros(reply, sizeof *opq12);
-        opq12->port = port;
+        opq12->port = ofputil_port_to_ofp11(oqc->port);
         opq12->queue_id = htonl(oqc->queue_id);
         len_ofs = (char *) &opq12->len - (char *) reply->data;
     }
@@ -4018,6 +4074,84 @@ ofputil_append_port_desc_stats_reply(const struct ofputil_phy_port *pp,
     ofpmp_postappend(replies, start_ofs);
 }
 \f
+/* ofputil_switch_config */
+
+/* Decodes 'oh', which must be an OFPT_GET_CONFIG_REPLY or OFPT_SET_CONFIG
+ * message, into 'config'.  Returns false if 'oh' contained any flags that
+ * aren't specified in its version of OpenFlow, true otherwise. */
+static bool
+ofputil_decode_switch_config(const struct ofp_header *oh,
+                             struct ofputil_switch_config *config)
+{
+    const struct ofp_switch_config *osc;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+    osc = ofpbuf_pull(&b, sizeof *osc);
+
+    config->frag = ntohs(osc->flags) & OFPC_FRAG_MASK;
+    config->miss_send_len = ntohs(osc->miss_send_len);
+
+    ovs_be16 valid_mask = htons(OFPC_FRAG_MASK);
+    if (oh->version < OFP13_VERSION) {
+        const ovs_be16 ttl_bit = htons(OFPC_INVALID_TTL_TO_CONTROLLER);
+        valid_mask |= ttl_bit;
+        config->invalid_ttl_to_controller = (osc->flags & ttl_bit) != 0;
+    } else {
+        config->invalid_ttl_to_controller = -1;
+    }
+
+    return !(osc->flags & ~valid_mask);
+}
+
+void
+ofputil_decode_get_config_reply(const struct ofp_header *oh,
+                                struct ofputil_switch_config *config)
+{
+    ofputil_decode_switch_config(oh, config);
+}
+
+enum ofperr
+ofputil_decode_set_config(const struct ofp_header *oh,
+                          struct ofputil_switch_config *config)
+{
+    return (ofputil_decode_switch_config(oh, config)
+            ? 0
+            : OFPERR_OFPSCFC_BAD_FLAGS);
+}
+
+static struct ofpbuf *
+ofputil_put_switch_config(const struct ofputil_switch_config *config,
+                          struct ofpbuf *b)
+{
+    const struct ofp_header *oh = b->data;
+    struct ofp_switch_config *osc = ofpbuf_put_zeros(b, sizeof *osc);
+    osc->flags = htons(config->frag);
+    if (config->invalid_ttl_to_controller > 0 && oh->version < OFP13_VERSION) {
+        osc->flags |= htons(OFPC_INVALID_TTL_TO_CONTROLLER);
+    }
+    osc->miss_send_len = htons(config->miss_send_len);
+    return b;
+}
+
+struct ofpbuf *
+ofputil_encode_get_config_reply(const struct ofp_header *request,
+                                const struct ofputil_switch_config *config)
+{
+    struct ofpbuf *b = ofpraw_alloc_reply(OFPRAW_OFPT_GET_CONFIG_REPLY,
+                                          request, 0);
+    return ofputil_put_switch_config(config, b);
+}
+
+struct ofpbuf *
+ofputil_encode_set_config(const struct ofputil_switch_config *config,
+                          enum ofp_version version)
+{
+    struct ofpbuf *b = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, version, 0);
+    return ofputil_put_switch_config(config, b);
+}
+\f
 /* ofputil_switch_features */
 
 #define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \
@@ -5501,6 +5635,7 @@ ofputil_encode_requestforward(const struct ofputil_requestforward *rf,
         inner = ofputil_encode_meter_mod(ofp_version, rf->meter_mod);
         break;
 
+    case OFPRFR_N_REASONS:
     default:
         OVS_NOT_REACHED();
     }
@@ -5599,6 +5734,10 @@ ofputil_destroy_requestforward(struct ofputil_requestforward *rf)
     case OFPRFR_METER_MOD:
         ofpbuf_uninit(&rf->bands);
         free(rf->meter_mod);
+        break;
+
+    case OFPRFR_N_REASONS:
+        OVS_NOT_REACHED();
     }
 }
 
@@ -6392,29 +6531,30 @@ ofputil_encode_barrier_request(enum ofp_version ofp_version)
 }
 
 const char *
-ofputil_frag_handling_to_string(enum ofp_config_flags flags)
+ofputil_frag_handling_to_string(enum ofputil_frag_handling frag)
 {
-    switch (flags & OFPC_FRAG_MASK) {
-    case OFPC_FRAG_NORMAL:   return "normal";
-    case OFPC_FRAG_DROP:     return "drop";
-    case OFPC_FRAG_REASM:    return "reassemble";
-    case OFPC_FRAG_NX_MATCH: return "nx-match";
+    switch (frag) {
+    case OFPUTIL_FRAG_NORMAL:   return "normal";
+    case OFPUTIL_FRAG_DROP:     return "drop";
+    case OFPUTIL_FRAG_REASM:    return "reassemble";
+    case OFPUTIL_FRAG_NX_MATCH: return "nx-match";
     }
 
     OVS_NOT_REACHED();
 }
 
 bool
-ofputil_frag_handling_from_string(const char *s, enum ofp_config_flags *flags)
+ofputil_frag_handling_from_string(const char *s,
+                                  enum ofputil_frag_handling *frag)
 {
     if (!strcasecmp(s, "normal")) {
-        *flags = OFPC_FRAG_NORMAL;
+        *frag = OFPUTIL_FRAG_NORMAL;
     } else if (!strcasecmp(s, "drop")) {
-        *flags = OFPC_FRAG_DROP;
+        *frag = OFPUTIL_FRAG_DROP;
     } else if (!strcasecmp(s, "reassemble")) {
-        *flags = OFPC_FRAG_REASM;
+        *frag = OFPUTIL_FRAG_REASM;
     } else if (!strcasecmp(s, "nx-match")) {
-        *flags = OFPC_FRAG_NX_MATCH;
+        *frag = OFPUTIL_FRAG_NX_MATCH;
     } else {
         return false;
     }
@@ -8825,7 +8965,9 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
             }
             break;
         default:
-            OVS_NOT_REACHED();
+            /* Returning BAD TYPE to be consistent
+             * though gm->type has been checked already. */
+            return OFPERR_OFPGMFC_BAD_TYPE;
         }
     }
 
@@ -9219,7 +9361,7 @@ ofputil_is_bundlable(enum ofptype type)
     case OFPTYPE_TABLE_MOD:
     case OFPTYPE_METER_MOD:
     case OFPTYPE_PACKET_OUT:
-    case OFPTYPE_NXT_GENEVE_TABLE_MOD:
+    case OFPTYPE_NXT_TLV_TABLE_MOD:
 
         /* Not to be bundlable. */
     case OFPTYPE_ECHO_REQUEST:
@@ -9286,8 +9428,8 @@ ofputil_is_bundlable(enum ofptype type)
     case OFPTYPE_TABLE_DESC_REPLY:
     case OFPTYPE_ROLE_STATUS:
     case OFPTYPE_REQUESTFORWARD:
-    case OFPTYPE_NXT_GENEVE_TABLE_REQUEST:
-    case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
+    case OFPTYPE_NXT_TLV_TABLE_REQUEST:
+    case OFPTYPE_NXT_TLV_TABLE_REPLY:
         break;
     }
 
@@ -9366,12 +9508,12 @@ ofputil_encode_bundle_add(enum ofp_version ofp_version,
 }
 
 static void
-encode_geneve_table_mappings(struct ofpbuf *b, struct ovs_list *mappings)
+encode_tlv_table_mappings(struct ofpbuf *b, struct ovs_list *mappings)
 {
-    struct ofputil_geneve_map *map;
+    struct ofputil_tlv_map *map;
 
     LIST_FOR_EACH (map, list_node, mappings) {
-        struct nx_geneve_map *nx_map;
+        struct nx_tlv_map *nx_map;
 
         nx_map = ofpbuf_put_zeros(b, sizeof *nx_map);
         nx_map->option_class = htons(map->option_class);
@@ -9382,29 +9524,29 @@ encode_geneve_table_mappings(struct ofpbuf *b, struct ovs_list *mappings)
 }
 
 struct ofpbuf *
-ofputil_encode_geneve_table_mod(enum ofp_version ofp_version,
-                                struct ofputil_geneve_table_mod *gtm)
+ofputil_encode_tlv_table_mod(enum ofp_version ofp_version,
+                                struct ofputil_tlv_table_mod *ttm)
 {
     struct ofpbuf *b;
-    struct nx_geneve_table_mod *nx_gtm;
+    struct nx_tlv_table_mod *nx_ttm;
 
-    b = ofpraw_alloc(OFPRAW_NXT_GENEVE_TABLE_MOD, ofp_version, 0);
-    nx_gtm = ofpbuf_put_zeros(b, sizeof *nx_gtm);
-    nx_gtm->command = htons(gtm->command);
-    encode_geneve_table_mappings(b, &gtm->mappings);
+    b = ofpraw_alloc(OFPRAW_NXT_TLV_TABLE_MOD, ofp_version, 0);
+    nx_ttm = ofpbuf_put_zeros(b, sizeof *nx_ttm);
+    nx_ttm->command = htons(ttm->command);
+    encode_tlv_table_mappings(b, &ttm->mappings);
 
     return b;
 }
 
 static enum ofperr
-decode_geneve_table_mappings(struct ofpbuf *msg, unsigned int max_fields,
+decode_tlv_table_mappings(struct ofpbuf *msg, unsigned int max_fields,
                              struct ovs_list *mappings)
 {
     list_init(mappings);
 
     while (msg->size) {
-        struct nx_geneve_map *nx_map;
-        struct ofputil_geneve_map *map;
+        struct nx_tlv_map *nx_map;
+        struct ofputil_tlv_map *map;
 
         nx_map = ofpbuf_pull(msg, sizeof *nx_map);
         map = xmalloc(sizeof *map);
@@ -9414,21 +9556,21 @@ decode_geneve_table_mappings(struct ofpbuf *msg, unsigned int max_fields,
         map->option_type = nx_map->option_type;
 
         map->option_len = nx_map->option_len;
-        if (map->option_len % 4 || map->option_len > GENEVE_MAX_OPT_SIZE) {
+        if (map->option_len % 4 || map->option_len > TLV_MAX_OPT_SIZE) {
             VLOG_WARN_RL(&bad_ofmsg_rl,
-                         "geneve table option length (%u) is not a valid option size",
+                         "tlv table option length (%u) is not a valid option size",
                          map->option_len);
-            ofputil_uninit_geneve_table(mappings);
-            return OFPERR_NXGTMFC_BAD_OPT_LEN;
+            ofputil_uninit_tlv_table(mappings);
+            return OFPERR_NXTTMFC_BAD_OPT_LEN;
         }
 
         map->index = ntohs(nx_map->index);
         if (map->index >= max_fields) {
             VLOG_WARN_RL(&bad_ofmsg_rl,
-                         "geneve table field index (%u) is too large (max %u)",
+                         "tlv table field index (%u) is too large (max %u)",
                          map->index, max_fields - 1);
-            ofputil_uninit_geneve_table(mappings);
-            return OFPERR_NXGTMFC_BAD_FIELD_IDX;
+            ofputil_uninit_tlv_table(mappings);
+            return OFPERR_NXTTMFC_BAD_FIELD_IDX;
         }
     }
 
@@ -9436,72 +9578,72 @@ decode_geneve_table_mappings(struct ofpbuf *msg, unsigned int max_fields,
 }
 
 enum ofperr
-ofputil_decode_geneve_table_mod(const struct ofp_header *oh,
-                                struct ofputil_geneve_table_mod *gtm)
+ofputil_decode_tlv_table_mod(const struct ofp_header *oh,
+                                struct ofputil_tlv_table_mod *ttm)
 {
     struct ofpbuf msg;
-    struct nx_geneve_table_mod *nx_gtm;
+    struct nx_tlv_table_mod *nx_ttm;
 
     ofpbuf_use_const(&msg, oh, ntohs(oh->length));
     ofpraw_pull_assert(&msg);
 
-    nx_gtm = ofpbuf_pull(&msg, sizeof *nx_gtm);
-    gtm->command = ntohs(nx_gtm->command);
-    if (gtm->command > NXGTMC_CLEAR) {
+    nx_ttm = ofpbuf_pull(&msg, sizeof *nx_ttm);
+    ttm->command = ntohs(nx_ttm->command);
+    if (ttm->command > NXTTMC_CLEAR) {
         VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "geneve table mod command (%u) is out of range",
-                     gtm->command);
-        return OFPERR_NXGTMFC_BAD_COMMAND;
+                     "tlv table mod command (%u) is out of range",
+                     ttm->command);
+        return OFPERR_NXTTMFC_BAD_COMMAND;
     }
 
-    return decode_geneve_table_mappings(&msg, TUN_METADATA_NUM_OPTS,
-                                        &gtm->mappings);
+    return decode_tlv_table_mappings(&msg, TUN_METADATA_NUM_OPTS,
+                                        &ttm->mappings);
 }
 
 struct ofpbuf *
-ofputil_encode_geneve_table_reply(const struct ofp_header *oh,
-                                  struct ofputil_geneve_table_reply *gtr)
+ofputil_encode_tlv_table_reply(const struct ofp_header *oh,
+                                  struct ofputil_tlv_table_reply *ttr)
 {
     struct ofpbuf *b;
-    struct nx_geneve_table_reply *nx_gtr;
+    struct nx_tlv_table_reply *nx_ttr;
 
-    b = ofpraw_alloc_reply(OFPRAW_NXT_GENEVE_TABLE_REPLY, oh, 0);
-    nx_gtr = ofpbuf_put_zeros(b, sizeof *nx_gtr);
-    nx_gtr->max_option_space = htonl(gtr->max_option_space);
-    nx_gtr->max_fields = htons(gtr->max_fields);
+    b = ofpraw_alloc_reply(OFPRAW_NXT_TLV_TABLE_REPLY, oh, 0);
+    nx_ttr = ofpbuf_put_zeros(b, sizeof *nx_ttr);
+    nx_ttr->max_option_space = htonl(ttr->max_option_space);
+    nx_ttr->max_fields = htons(ttr->max_fields);
 
-    encode_geneve_table_mappings(b, &gtr->mappings);
+    encode_tlv_table_mappings(b, &ttr->mappings);
 
     return b;
 }
 
-/* Decodes the NXT_GENEVE_TABLE_REPLY message in 'oh' into '*gtr'.  Returns 0
+/* Decodes the NXT_TLV_TABLE_REPLY message in 'oh' into '*ttr'.  Returns 0
  * if successful, otherwise an ofperr.
  *
- * The decoder verifies that the indexes in 'gtr->mappings' are less than
- * 'gtr->max_fields', but the caller must ensure, if necessary, that they are
+ * The decoder verifies that the indexes in 'ttr->mappings' are less than
+ * 'ttr->max_fields', but the caller must ensure, if necessary, that they are
  * less than TUN_METADATA_NUM_OPTS. */
 enum ofperr
-ofputil_decode_geneve_table_reply(const struct ofp_header *oh,
-                                  struct ofputil_geneve_table_reply *gtr)
+ofputil_decode_tlv_table_reply(const struct ofp_header *oh,
+                                  struct ofputil_tlv_table_reply *ttr)
 {
     struct ofpbuf msg;
-    struct nx_geneve_table_reply *nx_gtr;
+    struct nx_tlv_table_reply *nx_ttr;
 
     ofpbuf_use_const(&msg, oh, ntohs(oh->length));
     ofpraw_pull_assert(&msg);
 
-    nx_gtr = ofpbuf_pull(&msg, sizeof *nx_gtr);
-    gtr->max_option_space = ntohl(nx_gtr->max_option_space);
-    gtr->max_fields = ntohs(nx_gtr->max_fields);
+    nx_ttr = ofpbuf_pull(&msg, sizeof *nx_ttr);
+    ttr->max_option_space = ntohl(nx_ttr->max_option_space);
+    ttr->max_fields = ntohs(nx_ttr->max_fields);
 
-    return decode_geneve_table_mappings(&msg, gtr->max_fields, &gtr->mappings);
+    return decode_tlv_table_mappings(&msg, ttr->max_fields, &ttr->mappings);
 }
 
 void
-ofputil_uninit_geneve_table(struct ovs_list *mappings)
+ofputil_uninit_tlv_table(struct ovs_list *mappings)
 {
-    struct ofputil_geneve_map *map;
+    struct ofputil_tlv_map *map;
 
     LIST_FOR_EACH_POP (map, list_node, mappings) {
         free(map);
@@ -9517,7 +9659,13 @@ ofputil_uninit_geneve_table(struct ovs_list *mappings)
  * treats unknown properties and values as an error, as a switch would want to
  * do when interpreting a configuration request made by a controller.
  *
- * Returns 0 if successful, otherwise an OFPERR_* value. */
+ * Returns 0 if successful, otherwise an OFPERR_* value.
+ *
+ * Returns error code OFPERR_OFPACFC_INVALID if the value of mask is not in
+ * the valid range of mask.
+ *
+ * Returns error code OFPERR_OFPACFC_UNSUPPORTED if the configuration is not
+ * supported.*/
 enum ofperr
 ofputil_decode_set_async_config(const struct ofp_header *oh,
                                 uint32_t master[OAM_N_TYPES],
@@ -9563,6 +9711,13 @@ ofputil_decode_set_async_config(const struct ofp_header *oh,
                 return OFPERR_OFPBRC_BAD_LEN;
             }
 
+            if (!loose) {
+                error = ofputil_check_mask(type, ntohl(msg->mask));
+                if (error) {
+                    return error;
+                }
+             }
+
             switch (type) {
             case OFPACPT_PACKET_IN_SLAVE:
                 slave[OAM_PACKET_IN] = ntohl(msg->mask);
@@ -9613,10 +9768,7 @@ ofputil_decode_set_async_config(const struct ofp_header *oh,
                 break;
 
             default:
-                error = loose ? 0 : OFPERR_OFPBPC_BAD_TYPE;
-                break;
-            }
-            if (error) {
+                error = loose ? 0 : OFPERR_OFPACFC_UNSUPPORTED;
                 return error;
             }
         }