datapath: Don't call genlmsg_reply() under rcu_read_lock().
[openvswitch] / lib / ofp-util.c
index 2e12eb5436072042210027835e05213a7c9f6923..c1e98ba224a1125e1dc5b1c183b0261571557926 100644 (file)
@@ -18,6 +18,7 @@
 #include "ofp-print.h"
 #include <errno.h>
 #include <inttypes.h>
+#include <netinet/icmp6.h>
 #include <stdlib.h>
 #include "autopath.h"
 #include "byte-order.h"
@@ -94,12 +95,11 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 
 /* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all
  * OR'd together. */
-enum {
-    WC_INVARIANTS = 0
+static const flow_wildcards_t WC_INVARIANTS = 0
 #define WC_INVARIANT_BIT(NAME) | FWW_##NAME
     WC_INVARIANT_LIST
 #undef WC_INVARIANT_BIT
-};
+;
 
 /* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
  * 'priority'. */
@@ -108,7 +108,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
                             unsigned int priority, struct cls_rule *rule)
 {
     struct flow_wildcards *wc = &rule->wc;
-    unsigned int ofpfw;
+    uint32_t ofpfw;
     ovs_be16 vid, pcp;
 
     /* Initialize rule->priority. */
@@ -117,7 +117,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
 
     /* Initialize most of rule->wc. */
     flow_wildcards_init_catchall(wc);
-    wc->wildcards = ofpfw & WC_INVARIANTS;
+    wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
 
     /* Wildcard fields that aren't defined by ofp_match or tun_id. */
     wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_ND_TARGET);
@@ -199,10 +199,10 @@ void
 ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match)
 {
     const struct flow_wildcards *wc = &rule->wc;
-    unsigned int ofpfw;
+    uint32_t ofpfw;
 
     /* Figure out most OpenFlow wildcards. */
-    ofpfw = wc->wildcards & WC_INVARIANTS;
+    ofpfw = (OVS_FORCE uint32_t) (wc->wildcards & WC_INVARIANTS);
     ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT;
     ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT;
     if (wc->wildcards & FWW_NW_TOS) {
@@ -902,8 +902,8 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh,
     ofputil_decode_msg_type(oh, &type);
     if (ofputil_msg_type_code(type) == OFPUTIL_OFPT_FLOW_MOD) {
         /* Standard OpenFlow flow_mod. */
-        struct ofp_match match, orig_match;
         const struct ofp_flow_mod *ofm;
+        uint16_t priority;
         int error;
 
         /* Dissect the message. */
@@ -913,26 +913,20 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh,
             return error;
         }
 
-        /* Normalize ofm->match.  If normalization actually changes anything,
-         * then log the differences. */
-        match = ofm->match;
-        match.pad1[0] = match.pad2[0] = 0;
-        orig_match = match;
-        normalize_match(&match);
-        if (memcmp(&match, &orig_match, sizeof orig_match)) {
-            if (!VLOG_DROP_INFO(&bad_ofmsg_rl)) {
-                char *old = ofp_match_to_literal_string(&orig_match);
-                char *new = ofp_match_to_literal_string(&match);
-                VLOG_INFO("normalization changed ofp_match, details:");
-                VLOG_INFO(" pre: %s", old);
-                VLOG_INFO("post: %s", new);
-                free(old);
-                free(new);
-            }
+        /* Set priority based on original wildcards.  Normally we'd allow
+         * ofputil_cls_rule_from_match() to do this for us, but
+         * ofputil_normalize_rule() can put wildcards where the original flow
+         * didn't have them. */
+        priority = ntohs(ofm->priority);
+        if (!(ofm->match.wildcards & htonl(OFPFW_ALL))) {
+            priority = UINT16_MAX;
         }
 
+        /* Translate the rule. */
+        ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr);
+        ofputil_normalize_rule(&fm->cr, NXFF_OPENFLOW10);
+
         /* Translate the message. */
-        ofputil_cls_rule_from_match(&match, ntohs(ofm->priority), &fm->cr);
         fm->cookie = ofm->cookie;
         command = ntohs(ofm->command);
         fm->idle_timeout = ntohs(ofm->idle_timeout);
@@ -1335,6 +1329,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         ofr = make_openflow_xid(sizeof *ofr, OFPT_FLOW_REMOVED, htonl(0),
                                 &msg);
         ofputil_cls_rule_to_match(&fr->rule, &ofr->match);
+        ofr->cookie = fr->cookie;
         ofr->priority = htons(fr->rule.priority);
         ofr->reason = fr->reason;
         ofr->duration_sec = htonl(fr->duration_sec);
@@ -2065,123 +2060,115 @@ actions_next(struct actions_iterator *iter)
     }
 }
 
+/* "Normalizes" the wildcards in 'rule'.  That means:
+ *
+ *    1. If the type of level N is known, then only the valid fields for that
+ *       level may be specified.  For example, ARP does not have a TOS field,
+ *       so nw_tos must be wildcarded if 'rule' specifies an ARP flow.
+ *       Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
+ *       ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an
+ *       IPv4 flow.
+ *
+ *    2. If the type of level N is not known (or not understood by Open
+ *       vSwitch), then no fields at all for that level may be specified.  For
+ *       example, Open vSwitch does not understand SCTP, an L4 protocol, so the
+ *       L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an
+ *       SCTP flow.
+ *
+ * 'flow_format' specifies the format of the flow as received or as intended to
+ * be sent.  This is important for IPv6 and ARP, for which NXM supports more
+ * detailed matching. */
 void
-normalize_match(struct ofp_match *m)
-{
-    enum { OFPFW_NW = (OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_NW_PROTO
-                       | OFPFW_NW_TOS) };
-    enum { OFPFW_TP = OFPFW_TP_SRC | OFPFW_TP_DST };
-    uint32_t wc;
-
-    wc = ntohl(m->wildcards) & OFPFW_ALL;
-    if (wc & OFPFW_DL_TYPE) {
-        m->dl_type = 0;
-
-        /* Can't sensibly match on network or transport headers if the
-         * data link type is unknown. */
-        wc |= OFPFW_NW | OFPFW_TP;
-        m->nw_src = m->nw_dst = m->nw_proto = m->nw_tos = 0;
-        m->tp_src = m->tp_dst = 0;
-    } else if (m->dl_type == htons(ETH_TYPE_IP)) {
-        if (wc & OFPFW_NW_PROTO) {
-            m->nw_proto = 0;
-
-            /* Can't sensibly match on transport headers if the network
-             * protocol is unknown. */
-            wc |= OFPFW_TP;
-            m->tp_src = m->tp_dst = 0;
-        } else if (m->nw_proto == IPPROTO_TCP ||
-                   m->nw_proto == IPPROTO_UDP ||
-                   m->nw_proto == IPPROTO_ICMP) {
-            if (wc & OFPFW_TP_SRC) {
-                m->tp_src = 0;
-            }
-            if (wc & OFPFW_TP_DST) {
-                m->tp_dst = 0;
-            }
-        } else {
-            /* Transport layer fields will always be extracted as zeros, so we
-             * can do an exact-match on those values.  */
-            wc &= ~OFPFW_TP;
-            m->tp_src = m->tp_dst = 0;
-        }
-        if (wc & OFPFW_NW_SRC_MASK) {
-            m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT);
+ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format)
+{
+    enum {
+        MAY_NW_ADDR     = 1 << 0, /* nw_src, nw_dst */
+        MAY_TP_ADDR     = 1 << 1, /* tp_src, tp_dst */
+        MAY_NW_PROTO    = 1 << 2, /* nw_proto */
+        MAY_NW_TOS      = 1 << 3, /* nw_tos */
+        MAY_ARP_SHA     = 1 << 4, /* arp_sha */
+        MAY_ARP_THA     = 1 << 5, /* arp_tha */
+        MAY_IPV6_ADDR   = 1 << 6, /* ipv6_src, ipv6_dst */
+        MAY_ND_TARGET   = 1 << 7  /* nd_target */
+    } may_match;
+
+    struct flow_wildcards wc;
+
+    /* Figure out what fields may be matched. */
+    if (rule->flow.dl_type == htons(ETH_TYPE_IP)) {
+        may_match = MAY_NW_PROTO | MAY_NW_TOS | MAY_NW_ADDR;
+        if (rule->flow.nw_proto == IPPROTO_TCP ||
+            rule->flow.nw_proto == IPPROTO_UDP ||
+            rule->flow.nw_proto == IPPROTO_ICMP) {
+            may_match |= MAY_TP_ADDR;
         }
-        if (wc & OFPFW_NW_DST_MASK) {
-            m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT);
-        }
-        if (wc & OFPFW_NW_TOS) {
-            m->nw_tos = 0;
-        } else {
-            m->nw_tos &= IP_DSCP_MASK;
-        }
-    } else if (m->dl_type == htons(ETH_TYPE_ARP)) {
-        if (wc & OFPFW_NW_PROTO) {
-            m->nw_proto = 0;
-        }
-        if (wc & OFPFW_NW_SRC_MASK) {
-            m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT);
+    } else if (rule->flow.dl_type == htons(ETH_TYPE_IPV6)
+               && flow_format == NXFF_NXM) {
+        may_match = MAY_NW_PROTO | MAY_NW_TOS | MAY_IPV6_ADDR;
+        if (rule->flow.nw_proto == IPPROTO_TCP ||
+            rule->flow.nw_proto == IPPROTO_UDP) {
+            may_match |= MAY_TP_ADDR;
+        } else if (rule->flow.nw_proto == IPPROTO_ICMPV6) {
+            may_match |= MAY_TP_ADDR;
+            if (rule->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
+                may_match |= MAY_ND_TARGET | MAY_ARP_SHA;
+            } else if (rule->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+                may_match |= MAY_ND_TARGET | MAY_ARP_THA;
+            }
         }
-        if (wc & OFPFW_NW_DST_MASK) {
-            m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT);
+    } else if (rule->flow.dl_type == htons(ETH_TYPE_ARP)) {
+        may_match = MAY_NW_PROTO | MAY_NW_ADDR;
+        if (flow_format == NXFF_NXM) {
+            may_match |= MAY_ARP_SHA | MAY_ARP_THA;
         }
-        m->tp_src = m->tp_dst = m->nw_tos = 0;
-    } else if (m->dl_type == htons(ETH_TYPE_IPV6)) {
-        /* Don't normalize IPv6 traffic, since OpenFlow doesn't have a
-         * way to express it. */
     } else {
-        /* Network and transport layer fields will always be extracted as
-         * zeros, so we can do an exact-match on those values. */
-        wc &= ~(OFPFW_NW | OFPFW_TP);
-        m->nw_proto = m->nw_src = m->nw_dst = m->nw_tos = 0;
-        m->tp_src = m->tp_dst = 0;
+        may_match = 0;
     }
-    if (wc & OFPFW_DL_SRC) {
-        memset(m->dl_src, 0, sizeof m->dl_src);
+
+    /* Clear the fields that may not be matched. */
+    wc = rule->wc;
+    if (!(may_match & MAY_NW_ADDR)) {
+        wc.nw_src_mask = wc.nw_dst_mask = htonl(0);
     }
-    if (wc & OFPFW_DL_DST) {
-        memset(m->dl_dst, 0, sizeof m->dl_dst);
+    if (!(may_match & MAY_TP_ADDR)) {
+        wc.wildcards |= FWW_TP_SRC | FWW_TP_DST;
+    }
+    if (!(may_match & MAY_NW_PROTO)) {
+        wc.wildcards |= FWW_NW_PROTO;
+    }
+    if (!(may_match & MAY_NW_TOS)) {
+        wc.wildcards |= FWW_NW_TOS;
+    }
+    if (!(may_match & MAY_ARP_SHA)) {
+        wc.wildcards |= FWW_ARP_SHA;
+    }
+    if (!(may_match & MAY_ARP_THA)) {
+        wc.wildcards |= FWW_ARP_THA;
+    }
+    if (!(may_match & MAY_IPV6_ADDR)) {
+        wc.ipv6_src_mask = wc.ipv6_dst_mask = in6addr_any;
+    }
+    if (!(may_match & MAY_ND_TARGET)) {
+        wc.wildcards |= FWW_ND_TARGET;
     }
-    m->wildcards = htonl(wc);
-}
 
-/* Returns a string that describes 'match' in a very literal way, without
- * interpreting its contents except in a very basic fashion.  The returned
- * string is intended to be fixed-length, so that it is easy to see differences
- * between two such strings if one is put above another.  This is useful for
- * describing changes made by normalize_match().
- *
- * The caller must free the returned string (with free()). */
-char *
-ofp_match_to_literal_string(const struct ofp_match *match)
-{
-    return xasprintf("wildcards=%#10"PRIx32" "
-                     " in_port=%5"PRId16" "
-                     " dl_src="ETH_ADDR_FMT" "
-                     " dl_dst="ETH_ADDR_FMT" "
-                     " dl_vlan=%5"PRId16" "
-                     " dl_vlan_pcp=%3"PRId8" "
-                     " dl_type=%#6"PRIx16" "
-                     " nw_tos=%#4"PRIx8" "
-                     " nw_proto=%#4"PRIx16" "
-                     " nw_src=%#10"PRIx32" "
-                     " nw_dst=%#10"PRIx32" "
-                     " tp_src=%5"PRId16" "
-                     " tp_dst=%5"PRId16,
-                     ntohl(match->wildcards),
-                     ntohs(match->in_port),
-                     ETH_ADDR_ARGS(match->dl_src),
-                     ETH_ADDR_ARGS(match->dl_dst),
-                     ntohs(match->dl_vlan),
-                     match->dl_vlan_pcp,
-                     ntohs(match->dl_type),
-                     match->nw_tos,
-                     match->nw_proto,
-                     ntohl(match->nw_src),
-                     ntohl(match->nw_dst),
-                     ntohs(match->tp_src),
-                     ntohs(match->tp_dst));
+    /* Log any changes. */
+    if (!flow_wildcards_equal(&wc, &rule->wc)) {
+        bool log = !VLOG_DROP_INFO(&bad_ofmsg_rl);
+        char *pre = log ? cls_rule_to_string(rule) : NULL;
+
+        rule->wc = wc;
+        cls_rule_zero_wildcarded_fields(rule);
+
+        if (log) {
+            char *post = cls_rule_to_string(rule);
+            VLOG_INFO("normalization changed ofp_match, details:");
+            VLOG_INFO(" pre: %s", pre);
+            VLOG_INFO("post: %s", post);
+            free(pre);
+            free(post);
+        }
+    }
 }
 
 static uint32_t