Fix handling of OFPP_ANY in OpenFlow 1.1 and later.
[openvswitch] / lib / ofp-util.c
index c2e97384639a14b9fd1e51a7a26b0a99ac201c55..b7feff800197c5377895d1593d1987b4845de965 100644 (file)
@@ -85,7 +85,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
@@ -588,6 +588,7 @@ static const struct proto_abbrev proto_abbrevs[] = {
 #define N_PROTO_ABBREVS ARRAY_SIZE(proto_abbrevs)
 
 enum ofputil_protocol ofputil_flow_dump_protocols[] = {
+    OFPUTIL_P_OF12_OXM,
     OFPUTIL_P_OF10_NXM,
     OFPUTIL_P_OF10_STD,
 };
@@ -726,7 +727,7 @@ ofputil_protocol_to_string(enum ofputil_protocol protocol)
         return "OpenFlow10+table_id";
 
     case OFPUTIL_P_OF12_OXM:
-        return NULL;
+        return "OXM";
     }
 
     /* Check abbreviations. */
@@ -847,7 +848,7 @@ ofputil_protocols_from_string(const char *s)
     return protocols;
 }
 
-static enum ofp_version
+static int
 ofputil_version_from_string(const char *s)
 {
     if (!strcasecmp(s, "OpenFlow10")) {
@@ -859,7 +860,7 @@ ofputil_version_from_string(const char *s)
     if (!strcasecmp(s, "OpenFlow12")) {
         return OFP12_VERSION;
     }
-    VLOG_FATAL("Unknown OpenFlow version: \"%s\"", s);
+    return 0;
 }
 
 static bool
@@ -876,7 +877,7 @@ ofputil_versions_from_string(const char *s)
 
     while (s[i]) {
         size_t j;
-        enum ofp_version version;
+        int version;
         char *key;
 
         if (is_delimiter(s[i])) {
@@ -889,6 +890,9 @@ ofputil_versions_from_string(const char *s)
         }
         key = xmemdup0(s + i, j);
         version = ofputil_version_from_string(key);
+        if (!version) {
+            VLOG_FATAL("Unknown OpenFlow version: \"%s\"", key);
+        }
         free(key);
         bitmap |= 1u << version;
         i += j;
@@ -897,6 +901,23 @@ ofputil_versions_from_string(const char *s)
     return bitmap;
 }
 
+uint32_t
+ofputil_versions_from_strings(char ** const s, size_t count)
+{
+    uint32_t bitmap = 0;
+
+    while (count--) {
+        int version = ofputil_version_from_string(s[count]);
+        if (!version) {
+            VLOG_WARN("Unknown OpenFlow version: \"%s\"", s[count]);
+        } else {
+            bitmap |= 1u << version;
+        }
+    }
+
+    return bitmap;
+}
+
 const char *
 ofputil_version_to_string(enum ofp_version ofp_version)
 {
@@ -958,6 +979,16 @@ regs_fully_wildcarded(const struct flow_wildcards *wc)
     return true;
 }
 
+static bool
+tun_parms_fully_wildcarded(const struct flow_wildcards *wc)
+{
+    return (!wc->masks.tunnel.ip_src &&
+            !wc->masks.tunnel.ip_dst &&
+            !wc->masks.tunnel.ip_ttl &&
+            !wc->masks.tunnel.ip_tos &&
+            !wc->masks.tunnel.flags);
+}
+
 /* Returns a bit-mask of ofputil_protocols that can be used for sending 'match'
  * to a switch (e.g. to add or remove a flow).  Only NXM can handle tunnel IDs,
  * registers, or fixing the Ethernet multicast bit.  Otherwise, it's better to
@@ -967,73 +998,78 @@ ofputil_usable_protocols(const struct match *match)
 {
     const struct flow_wildcards *wc = &match->wc;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
 
-    /* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */
+    /* tunnel params other than tun_id can't be sent in a flow_mod */
+    if (!tun_parms_fully_wildcarded(wc)) {
+        return OFPUTIL_P_NONE;
+    }
+
+    /* NXM, OXM, and OF1.1 support bitwise matching on ethernet addresses. */
     if (!eth_mask_is_exact(wc->masks.dl_src)
         && !eth_addr_is_zero(wc->masks.dl_src)) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
     if (!eth_mask_is_exact(wc->masks.dl_dst)
         && !eth_addr_is_zero(wc->masks.dl_dst)) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* NXM and OF1.1+ support matching metadata. */
+    /* NXM, OXM, and OF1.1+ support matching metadata. */
     if (wc->masks.metadata != htonll(0)) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* Only NXM supports matching ARP hardware addresses. */
+    /* NXM and OXM support matching ARP hardware addresses. */
     if (!eth_addr_is_zero(wc->masks.arp_sha) ||
         !eth_addr_is_zero(wc->masks.arp_tha)) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* Only NXM supports matching IPv6 traffic. */
+    /* NXM and OXM support matching IPv6 traffic. */
     if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* Only NXM supports matching registers. */
+    /* NXM and OXM support matching registers. */
     if (!regs_fully_wildcarded(wc)) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* Only NXM supports matching tun_id. */
+    /* NXM and OXM support matching tun_id. */
     if (wc->masks.tunnel.tun_id != htonll(0)) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* Only NXM supports matching fragments. */
+    /* NXM and OXM support matching fragments. */
     if (wc->masks.nw_frag) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* Only NXM supports matching IPv6 flow label. */
+    /* NXM and OXM support matching IPv6 flow label. */
     if (wc->masks.ipv6_label) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* Only NXM supports matching IP ECN bits. */
+    /* NXM and OXM support matching IP ECN bits. */
     if (wc->masks.nw_tos & IP_ECN_MASK) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* Only NXM supports matching IP TTL/hop limit. */
+    /* NXM and OXM support matching IP TTL/hop limit. */
     if (wc->masks.nw_ttl) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* Only NXM supports non-CIDR IPv4 address masks. */
+    /* NXM and OXM support non-CIDR IPv4 address masks. */
     if (!ip_is_cidr(wc->masks.nw_src) || !ip_is_cidr(wc->masks.nw_dst)) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
-    /* Only NXM supports bitwise matching on transport port. */
+    /* NXM and OXM support bitwise matching on transport port. */
     if ((wc->masks.tp_src && wc->masks.tp_src != htons(UINT16_MAX)) ||
         (wc->masks.tp_dst && wc->masks.tp_dst != htons(UINT16_MAX))) {
-        return OFPUTIL_P_OF10_NXM_ANY;
+        return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
 
     /* Other formats can express this rule. */
@@ -1201,15 +1237,25 @@ ofputil_encode_hello(uint32_t allowed_versions)
  * connection if the switch processes the returned message correctly.  (If
  * '*next != want' then the caller will have to iterate.)
  *
- * If 'current == want', returns NULL and stores 'current' in '*next'. */
+ * If 'current == want', or if it is not possible to transition from 'current'
+ * to 'want' (because, for example, 'current' and 'want' use different OpenFlow
+ * protocol versions), returns NULL and stores 'current' in '*next'. */
 struct ofpbuf *
 ofputil_encode_set_protocol(enum ofputil_protocol current,
                             enum ofputil_protocol want,
                             enum ofputil_protocol *next)
 {
+    enum ofp_version cur_version, want_version;
     enum ofputil_protocol cur_base, want_base;
     bool cur_tid, want_tid;
 
+    cur_version = ofputil_protocol_to_ofp_version(current);
+    want_version = ofputil_protocol_to_ofp_version(want);
+    if (cur_version != want_version) {
+        *next = current;
+        return NULL;
+    }
+
     cur_base = ofputil_protocol_to_base(current);
     want_base = ofputil_protocol_to_base(want);
     if (cur_base != want_base) {
@@ -1223,7 +1269,9 @@ ofputil_encode_set_protocol(enum ofputil_protocol current,
             return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10);
 
         case OFPUTIL_P_OF12_OXM:
-            return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW12);
+            /* There's only one OpenFlow 1.2 protocol and we already verified
+             * above that we're not trying to change versions. */
+            NOT_REACHED();
 
         case OFPUTIL_P_OF10_STD_TID:
         case OFPUTIL_P_OF10_NXM_TID:
@@ -1273,9 +1321,6 @@ ofputil_nx_flow_format_to_protocol(enum nx_flow_format flow_format)
     case NXFF_NXM:
         return OFPUTIL_P_OF10_NXM;
 
-    case NXFF_OPENFLOW12:
-        return OFPUTIL_P_OF12_OXM;
-
     default:
         return 0;
     }
@@ -1298,8 +1343,6 @@ ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format)
         return "openflow10";
     case NXFF_NXM:
         return "nxm";
-    case NXFF_OPENFLOW12:
-        return "openflow12";
     default:
         NOT_REACHED();
     }
@@ -1597,12 +1640,11 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
             usable_protocols &= OFPUTIL_P_TID;
         }
 
-        /* Matching of the cookie is only supported through NXM. */
+        /* Matching of the cookie is only supported through NXM or OF1.1+. */
         if (fm->cookie_mask != htonll(0)) {
-            usable_protocols &= OFPUTIL_P_OF10_NXM_ANY;
+            usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
         }
     }
-    assert(usable_protocols);
 
     return usable_protocols;
 }
@@ -1791,7 +1833,7 @@ ofputil_flow_stats_request_usable_protocols(
 
     usable_protocols = ofputil_usable_protocols(&fsr->match);
     if (fsr->cookie_mask != htonll(0)) {
-        usable_protocols &= OFPUTIL_P_OF10_NXM_ANY;
+        usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
     }
     return usable_protocols;
 }
@@ -3762,6 +3804,11 @@ ofputil_check_output_port(uint16_t port, int max_ports)
         OFPUTIL_NAMED_PORT(ALL)                 \
         OFPUTIL_NAMED_PORT(CONTROLLER)          \
         OFPUTIL_NAMED_PORT(LOCAL)               \
+        OFPUTIL_NAMED_PORT(ANY)
+
+/* For backwards compatibility, so that "none" is recognized as OFPP_ANY */
+#define OFPUTIL_NAMED_PORTS_WITH_NONE           \
+        OFPUTIL_NAMED_PORTS                     \
         OFPUTIL_NAMED_PORT(NONE)
 
 /* Stores the port number represented by 's' into '*portp'.  's' may be an
@@ -3821,7 +3868,7 @@ ofputil_port_from_string(const char *s, uint16_t *portp)
         };
         static const struct pair pairs[] = {
 #define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME},
-            OFPUTIL_NAMED_PORTS
+            OFPUTIL_NAMED_PORTS_WITH_NONE
 #undef OFPUTIL_NAMED_PORT
         };
         const struct pair *p;
@@ -4425,9 +4472,13 @@ ofputil_decode_queue_stats_request(const struct ofp_header *request,
     }
 
     case OFP10_VERSION: {
-        const struct ofp10_queue_stats_request *qsr11 = ofpmsg_body(request);
-        oqsr->queue_id = ntohl(qsr11->queue_id);
-        oqsr->port_no = ntohs(qsr11->port_no);
+        const struct ofp10_queue_stats_request *qsr10 = ofpmsg_body(request);
+        oqsr->queue_id = ntohl(qsr10->queue_id);
+        oqsr->port_no = ntohs(qsr10->port_no);
+        /* OF 1.0 uses OFPP_ALL for OFPP_ANY */
+        if (oqsr->port_no == OFPP_ALL) {
+            oqsr->port_no = OFPP_ANY;
+        }
         return 0;
     }
 
@@ -4459,7 +4510,9 @@ ofputil_encode_queue_stats_request(enum ofp_version ofp_version,
         struct ofp10_queue_stats_request *req;
         request = ofpraw_alloc(OFPRAW_OFPST10_QUEUE_REQUEST, ofp_version, 0);
         req = ofpbuf_put_zeros(request, sizeof *req);
-        req->port_no = htons(oqsr->port_no);
+        /* OpenFlow 1.0 needs OFPP_ALL instead of OFPP_ANY */
+        req->port_no = htons(oqsr->port_no == OFPP_ANY
+                             ? OFPP_ALL : oqsr->port_no);
         req->queue_id = htonl(oqsr->queue_id);
         break;
     }