}
 }
 
+void
+normalize_match(struct ofp_match *m)
+{
+    enum { OFPFW_NW = OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_NW_PROTO };
+    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 m 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 = 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 m 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-m on those values.  */
+            wc &= ~OFPFW_TP;
+            m->tp_src = m->tp_dst = 0;
+        }
+        if (wc & OFPFW_NW_SRC_MASK) {
+            m->nw_src &= flow_nw_bits_to_mask(wc, OFPFW_NW_SRC_SHIFT);
+        }
+        if (wc & OFPFW_NW_DST_MASK) {
+            m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT);
+        }
+    } else {
+        /* Network and transport layer fields will always be extracted as
+         * zeros, so we can do an exact-m on those values. */
+        wc &= ~(OFPFW_NW | OFPFW_TP);
+        m->nw_proto = m->nw_src = m->nw_dst = 0;
+        m->tp_src = m->tp_dst = 0;
+    }
+    if (wc & OFPFW_DL_SRC) {
+        memset(m->dl_src, 0, sizeof m->dl_src);
+    }
+    if (wc & OFPFW_DL_DST) {
+        memset(m->dl_dst, 0, sizeof m->dl_dst);
+    }
+    m->wildcards = htonl(wc);
+}
+
 void
 vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
            uint32_t ip, const char *name, bool reconnectable)