ovs-ofctl: Accept port keywords, OF1.1 port numbers, reject port number 0.
[sliver-openvswitch.git] / lib / ofp-util.c
index 33ff6ea..fc98894 100644 (file)
@@ -2279,7 +2279,6 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
                           const struct ofp_header *oh,
                           struct ofpbuf *ofpacts)
 {
-    enum ofperr bad_in_port_err;
     enum ofpraw raw;
     struct ofpbuf b;
 
@@ -2301,8 +2300,6 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         if (error) {
             return error;
         }
-
-        bad_in_port_err = OFPERR_OFPBMC_BAD_VALUE;
     } else if (raw == OFPRAW_OFPT10_PACKET_OUT) {
         enum ofperr error;
         const struct ofp_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
@@ -2314,8 +2311,6 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         if (error) {
             return error;
         }
-
-        bad_in_port_err = OFPERR_NXBRC_BAD_IN_PORT;
     } else {
         NOT_REACHED();
     }
@@ -2324,7 +2319,7 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         && po->in_port != OFPP_NONE && po->in_port != OFPP_CONTROLLER) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx16,
                      po->in_port);
-        return bad_in_port_err;
+        return OFPERR_OFPBRC_BAD_PORT;
     }
 
     po->ofpacts = ofpacts->data;
@@ -2938,6 +2933,143 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
     return b;
 }
 \f
+/* Table stats. */
+
+static void
+ofputil_put_ofp10_table_stats(const struct ofp12_table_stats *in,
+                              struct ofpbuf *buf)
+{
+    struct wc_map {
+        enum ofp_flow_wildcards wc10;
+        enum oxm12_ofb_match_fields mf12;
+    };
+
+    static const struct wc_map wc_map[] = {
+        { OFPFW10_IN_PORT,     OFPXMT12_OFB_IN_PORT },
+        { OFPFW10_DL_VLAN,     OFPXMT12_OFB_VLAN_VID },
+        { OFPFW10_DL_SRC,      OFPXMT12_OFB_ETH_SRC },
+        { OFPFW10_DL_DST,      OFPXMT12_OFB_ETH_DST},
+        { OFPFW10_DL_TYPE,     OFPXMT12_OFB_ETH_TYPE },
+        { OFPFW10_NW_PROTO,    OFPXMT12_OFB_IP_PROTO },
+        { OFPFW10_TP_SRC,      OFPXMT12_OFB_TCP_SRC },
+        { OFPFW10_TP_DST,      OFPXMT12_OFB_TCP_DST },
+        { OFPFW10_NW_SRC_MASK, OFPXMT12_OFB_IPV4_SRC },
+        { OFPFW10_NW_DST_MASK, OFPXMT12_OFB_IPV4_DST },
+        { OFPFW10_DL_VLAN_PCP, OFPXMT12_OFB_VLAN_PCP },
+        { OFPFW10_NW_TOS,      OFPXMT12_OFB_IP_DSCP },
+    };
+
+    struct ofp10_table_stats *out;
+    const struct wc_map *p;
+
+    out = ofpbuf_put_uninit(buf, sizeof *out);
+    out->table_id = in->table_id;
+    strcpy(out->name, in->name);
+    out->wildcards = 0;
+    for (p = wc_map; p < &wc_map[ARRAY_SIZE(wc_map)]; p++) {
+        if (in->wildcards & htonll(1ULL << p->mf12)) {
+            out->wildcards |= htonl(p->wc10);
+        }
+    }
+    out->max_entries = in->max_entries;
+    out->active_count = in->active_count;
+    put_32aligned_be64(&out->lookup_count, in->lookup_count);
+    put_32aligned_be64(&out->matched_count, in->matched_count);
+}
+
+static ovs_be32
+oxm12_to_ofp11_flow_match_fields(ovs_be64 oxm12)
+{
+    struct map {
+        enum ofp11_flow_match_fields fmf11;
+        enum oxm12_ofb_match_fields mf12;
+    };
+
+    static const struct map map[] = {
+        { OFPFMF11_IN_PORT,     OFPXMT12_OFB_IN_PORT },
+        { OFPFMF11_DL_VLAN,     OFPXMT12_OFB_VLAN_VID },
+        { OFPFMF11_DL_VLAN_PCP, OFPXMT12_OFB_VLAN_PCP },
+        { OFPFMF11_DL_TYPE,     OFPXMT12_OFB_ETH_TYPE },
+        { OFPFMF11_NW_TOS,      OFPXMT12_OFB_IP_DSCP },
+        { OFPFMF11_NW_PROTO,    OFPXMT12_OFB_IP_PROTO },
+        { OFPFMF11_TP_SRC,      OFPXMT12_OFB_TCP_SRC },
+        { OFPFMF11_TP_DST,      OFPXMT12_OFB_TCP_DST },
+        { OFPFMF11_MPLS_LABEL,  OFPXMT12_OFB_MPLS_LABEL },
+        { OFPFMF11_MPLS_TC,     OFPXMT12_OFB_MPLS_TC },
+        /* I don't know what OFPFMF11_TYPE means. */
+        { OFPFMF11_DL_SRC,      OFPXMT12_OFB_ETH_SRC },
+        { OFPFMF11_DL_DST,      OFPXMT12_OFB_ETH_DST },
+        { OFPFMF11_NW_SRC,      OFPXMT12_OFB_IPV4_SRC },
+        { OFPFMF11_NW_DST,      OFPXMT12_OFB_IPV4_DST },
+        { OFPFMF11_METADATA,    OFPXMT12_OFB_METADATA },
+    };
+
+    const struct map *p;
+    uint32_t fmf11;
+
+    fmf11 = 0;
+    for (p = map; p < &map[ARRAY_SIZE(map)]; p++) {
+        if (oxm12 & htonll(1ULL << p->mf12)) {
+            fmf11 |= p->fmf11;
+        }
+    }
+    return htonl(fmf11);
+}
+
+static void
+ofputil_put_ofp11_table_stats(const struct ofp12_table_stats *in,
+                              struct ofpbuf *buf)
+{
+    struct ofp11_table_stats *out;
+
+    out = ofpbuf_put_uninit(buf, sizeof *out);
+    out->table_id = in->table_id;
+    strcpy(out->name, in->name);
+    out->wildcards = oxm12_to_ofp11_flow_match_fields(in->wildcards);
+    out->match = oxm12_to_ofp11_flow_match_fields(in->match);
+    out->instructions = in->instructions;
+    out->write_actions = in->write_actions;
+    out->apply_actions = in->apply_actions;
+    out->config = in->config;
+    out->max_entries = in->max_entries;
+    out->active_count = in->active_count;
+    out->lookup_count = in->lookup_count;
+    out->matched_count = in->matched_count;
+}
+
+struct ofpbuf *
+ofputil_encode_table_stats_reply(const struct ofp12_table_stats stats[], int n,
+                                 const struct ofp_header *request)
+{
+    struct ofpbuf *reply;
+    int i;
+
+    reply = ofpraw_alloc_stats_reply(request, n * sizeof *stats);
+
+    switch ((enum ofp_version) request->version) {
+    case OFP10_VERSION:
+        for (i = 0; i < n; i++) {
+            ofputil_put_ofp10_table_stats(&stats[i], reply);
+        }
+        break;
+
+    case OFP11_VERSION:
+        for (i = 0; i < n; i++) {
+            ofputil_put_ofp11_table_stats(&stats[i], reply);
+        }
+        break;
+
+    case OFP12_VERSION:
+        ofpbuf_put(reply, stats, n * sizeof *stats);
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    return reply;
+}
+\f
 /* ofputil_flow_monitor_request */
 
 /* Converts an NXST_FLOW_MONITOR request in 'msg' into an abstract
@@ -3404,36 +3536,72 @@ ofputil_check_output_port(uint16_t port, int max_ports)
         OFPUTIL_NAMED_PORT(LOCAL)               \
         OFPUTIL_NAMED_PORT(NONE)
 
-/* Checks whether 's' is the string representation of an OpenFlow port number,
- * either as an integer or a string name (e.g. "LOCAL").  If it is, stores the
- * number in '*port' and returns true.  Otherwise, returns false. */
-bool
-ofputil_port_from_string(const char *name, uint16_t *port)
+/* Returns the port number represented by 's', which may be an integer or, for
+ * reserved ports, the standard OpenFlow name for the port (e.g. "LOCAL").
+ *
+ * Returns 0 if 's' is not a valid OpenFlow port number or name.  The caller
+ * should issue an error message in this case, because this function usually
+ * does not.  (This gives the caller an opportunity to look up the port name
+ * another way, e.g. by contacting the switch and listing the names of all its
+ * ports).
+ *
+ * This function accepts OpenFlow 1.0 port numbers.  It also accepts a subset
+ * of OpenFlow 1.1+ port numbers, mapping those port numbers into the 16-bit
+ * range as described in include/openflow/openflow-1.1.h. */
+uint16_t
+ofputil_port_from_string(const char *s)
 {
-    struct pair {
-        const char *name;
-        uint16_t value;
-    };
-    static const struct pair pairs[] = {
+    unsigned int port32;
+
+    if (str_to_uint(s, 10, &port32)) {
+        if (port32 == 0) {
+            VLOG_WARN("port 0 is not a valid OpenFlow port number");
+            return 0;
+        } else if (port32 < OFPP_MAX) {
+            return port32;
+        } else if (port32 < OFPP_FIRST_RESV) {
+            VLOG_WARN("port %u is a reserved OF1.0 port number that will "
+                      "be translated to %u when talking to an OF1.1 or "
+                      "later controller", port32, port32 + OFPP11_OFFSET);
+            return port32;
+        } else if (port32 <= OFPP_LAST_RESV) {
+            struct ds s;
+
+            ds_init(&s);
+            ofputil_format_port(port32, &s);
+            VLOG_WARN("port %u is better referred to as %s, for compatibility "
+                      "with future versions of OpenFlow",
+                      port32, ds_cstr(&s));
+            ds_destroy(&s);
+
+            return port32;
+        } else if (port32 < OFPP11_MAX) {
+            VLOG_WARN("port %u is outside the supported range 0 through "
+                      "%"PRIx16"or 0x%x through 0x%"PRIx32, port32,
+                      UINT16_MAX, (unsigned int) OFPP11_MAX, UINT32_MAX);
+            return 0;
+        } else {
+            return port32 - OFPP11_OFFSET;
+        }
+    } else {
+        struct pair {
+            const char *name;
+            uint16_t value;
+        };
+        static const struct pair pairs[] = {
 #define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME},
-        OFPUTIL_NAMED_PORTS
+            OFPUTIL_NAMED_PORTS
 #undef OFPUTIL_NAMED_PORT
-    };
-    static const int n_pairs = ARRAY_SIZE(pairs);
-    int i;
-
-    if (str_to_int(name, 0, &i) && i >= 0 && i < UINT16_MAX) {
-        *port = i;
-        return true;
-    }
+        };
+        const struct pair *p;
 
-    for (i = 0; i < n_pairs; i++) {
-        if (!strcasecmp(name, pairs[i].name)) {
-            *port = pairs[i].value;
-            return true;
+        for (p = pairs; p < &pairs[ARRAY_SIZE(pairs)]; p++) {
+            if (!strcasecmp(s, p->name)) {
+                return p->value;
+            }
         }
+        return 0;
     }
-    return false;
 }
 
 /* Appends to 's' a string representation of the OpenFlow port number 'port'.