Add support for matching Ethernet multicast frames.
authorBen Pfaff <blp@nicira.com>
Thu, 11 Nov 2010 18:46:23 +0000 (10:46 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 11 Nov 2010 18:46:23 +0000 (10:46 -0800)
include/openflow/nicira-ext.h
lib/classifier.c
lib/flow.c
lib/flow.h
lib/nx-match.c
lib/nx-match.def
tests/ovs-ofctl.at
tests/test-classifier.c

index 3a4a694016bb0bc0a5880aae37276da10b1ef386..3c2856f9e3949e8b5f4085221759d63a68544e1a 100644 (file)
@@ -669,8 +669,12 @@ OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24);
  *
  * Format: 48-bit Ethernet MAC address.
  *
- * Masking: Not maskable. */
+ * Masking: The nxm_mask patterns 01:00:00:00:00:00 and FE:FF:FF:FF:FF:FF must
+ *   be supported for NXM_OF_ETH_DST_W (as well as the trivial patterns that
+ *   are all-0-bits or all-1-bits).  Support for other patterns and for masking
+ *   of NXM_OF_ETH_SRC is optional. */
 #define NXM_OF_ETH_DST    NXM_HEADER  (0x0000,  1, 6)
+#define NXM_OF_ETH_DST_W  NXM_HEADER_W(0x0000,  1, 6)
 #define NXM_OF_ETH_SRC    NXM_HEADER  (0x0000,  2, 6)
 
 /* Packet's Ethernet type.
index 47a1bd771a1c57eb6ea3abb6555d255b204276c8..51d338b01975e0e9f231f9e5b7f0cc0db71f7b85 100644 (file)
@@ -142,7 +142,7 @@ void
 cls_rule_init_catchall(struct cls_rule *rule, unsigned int priority)
 {
     memset(&rule->flow, 0, sizeof rule->flow);
-    flow_wildcards_init(&rule->wc, OVSFW_ALL);
+    flow_wildcards_init(&rule->wc, OVSFW_ALL | FWW_ALL);
     rule->priority = priority;
 }
 
@@ -184,7 +184,7 @@ cls_rule_set_dl_src(struct cls_rule *rule, const uint8_t dl_src[ETH_ADDR_LEN])
 void
 cls_rule_set_dl_dst(struct cls_rule *rule, const uint8_t dl_dst[ETH_ADDR_LEN])
 {
-    rule->wc.wildcards &= ~OFPFW_DL_DST;
+    rule->wc.wildcards &= ~(OFPFW_DL_DST | FWW_ETH_MCAST);
     memcpy(rule->flow.dl_dst, dl_dst, ETH_ADDR_LEN);
 }
 
@@ -828,7 +828,14 @@ flow_equal_except(const struct flow *a, const struct flow *b,
             && (wc & OFPFW_TP_SRC || a->tp_src == b->tp_src)
             && (wc & OFPFW_TP_DST || a->tp_dst == b->tp_dst)
             && (wc & OFPFW_DL_SRC || eth_addr_equals(a->dl_src, b->dl_src))
-            && (wc & OFPFW_DL_DST || eth_addr_equals(a->dl_dst, b->dl_dst))
+            && (wc & OFPFW_DL_DST
+                || (!((a->dl_dst[0] ^ b->dl_dst[0]) & 0xfe)
+                    && a->dl_dst[1] == b->dl_dst[1]
+                    && a->dl_dst[2] == b->dl_dst[2]
+                    && a->dl_dst[3] == b->dl_dst[3]
+                    && a->dl_dst[4] == b->dl_dst[4]
+                    && a->dl_dst[5] == b->dl_dst[5]))
+            && (wc & FWW_ETH_MCAST || !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01))
             && (wc & OFPFW_NW_PROTO || a->nw_proto == b->nw_proto)
             && (wc & OFPFW_DL_VLAN_PCP || a->dl_vlan_pcp == b->dl_vlan_pcp)
             && (wc & OFPFW_NW_TOS || a->nw_tos == b->nw_tos));
@@ -869,7 +876,11 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
         memset(flow->dl_src, 0, sizeof flow->dl_src);
     }
     if (wc & OFPFW_DL_DST) {
-        memset(flow->dl_dst, 0, sizeof flow->dl_dst);
+        flow->dl_dst[0] &= 0x01;
+        memset(&flow->dl_dst[1], 0, 5);
+    }
+    if (wc & FWW_ETH_MCAST) {
+        flow->dl_dst[0] &= 0xfe;
     }
     if (wc & OFPFW_NW_PROTO) {
         flow->nw_proto = 0;
index 72202106b356d8925d32cd2b1986236971037073..0b6541f6f9b7dd3c52d3638e6498cd4106519516 100644 (file)
@@ -291,6 +291,12 @@ flow_from_match(const struct ofp_match *match, int flow_format,
             flow->tun_id = htonl(ntohll(cookie) >> 32);
         }
     }
+    if (wildcards & OFPFW_DL_DST) {
+        /* OpenFlow 1.0 OFPFW_DL_DST covers the whole Ethernet destination, but
+         * internally to OVS it excludes the multicast bit, which has to be set
+         * separately with FWW_ETH_MCAST. */
+        wildcards |= FWW_ETH_MCAST;
+    }
     flow_wildcards_init(wc, wildcards);
 
     flow->nw_src = match->nw_src;
@@ -377,7 +383,7 @@ flow_nw_bits_to_mask(uint32_t wildcards, int shift)
 static inline uint32_t
 flow_wildcards_normalize(uint32_t wildcards)
 {
-    wildcards &= wildcards & OVSFW_ALL;
+    wildcards &= wildcards & (OVSFW_ALL | FWW_ALL);
     if (wildcards & (0x20 << OFPFW_NW_SRC_SHIFT)) {
         wildcards &= ~(0x1f << OFPFW_NW_SRC_SHIFT);
     }
index a87efc1244334f0e05f9f687de92905331bd1cee..a19a9ae5e42b5356ec2a9cee4a5a9a1609059a80 100644 (file)
@@ -93,14 +93,27 @@ flow_hash(const struct flow *flow, uint32_t basis)
     return hash_bytes(flow, FLOW_SIG_SIZE, basis);
 }
 
-/* Set to 1 in the 'wildcards' member of struct flow_wildcards if any bits in
- * any of the reg_masks are wildcarded.  This maintains the invariant that
- * 'wildcards' is nonzero if and only if any bits are wildcarded.
+/* Open vSwitch internal-only wildcard bits.
  *
- * This is used only internally to Open vSwitch--it never appears in the wire
- * protocol. */
+ * These are used only internally to Open vSwitch, in the 'wildcards' member of
+ * struct flow_wildcards.  They never appear in the wire protocol in this
+ * form. */
+
+/* Set to 1 if any bits in any of the reg_masks are wildcarded.  This maintains
+ * the invariant that 'wildcards' is nonzero if and only if any bits are
+ * wildcarded. */
 #define FWW_REGS (1u << 31)
-BUILD_ASSERT_DECL(!(FWW_REGS & OVSFW_ALL)); /* Avoid collisions. */
+
+/* Set to 1 if bit 0 (the multicast bit) of the flow's dl_dst is wildcarded.
+ *
+ * (We reinterpret OFPFW_DL_DST as excluding bit 0.  Both OFPFW_DL_DST and
+ * FWW_ETH_MCAST have to be set to wildcard the entire Ethernet destination
+ * address.) */
+#define FWW_ETH_MCAST (1u << 30)
+
+/* Avoid collisions. */
+#define FWW_ALL (FWW_REGS | FWW_ETH_MCAST)
+BUILD_ASSERT_DECL(!(FWW_ALL & OVSFW_ALL));
 
 /* Information on wildcards for a flow, as a supplement to "struct flow".
  *
@@ -110,7 +123,7 @@ BUILD_ASSERT_DECL(!(FWW_REGS & OVSFW_ALL)); /* Avoid collisions. */
  * 1. 'wildcards' is nonzero if and only if at least one bit or field is
  *    wildcarded.
  *
- * 2. Bits in 'wildcards' not included in OVSFW_ALL or FWW_REGS are set to 0.
+ * 2. Bits in 'wildcards' not included in OVSFW_ALL or FWW_ALL are set to 0.
  *    (This is a corollary to invariant #1.)
  *
  * 3. The fields in 'wildcards' masked by OFPFW_NW_SRC_MASK and
@@ -127,7 +140,7 @@ BUILD_ASSERT_DECL(!(FWW_REGS & OVSFW_ALL)); /* Avoid collisions. */
  *    other members can be correctly predicted based on 'wildcards' alone.
  */
 struct flow_wildcards {
-    uint32_t wildcards;         /* enum ofp_flow_wildcards. */
+    uint32_t wildcards;         /* OFPFW_* | OVSFW_* | FWW_*. */
     uint32_t reg_masks[FLOW_N_REGS]; /* 1-bit in each significant regs bit. */
     ovs_be32 nw_src_mask;       /* 1-bit in each significant nw_src bit. */
     ovs_be32 nw_dst_mask;       /* 1-bit in each significant nw_dst bit. */
index 8e918ebb4b644224911def691b135ae11f7d49cf..12ea4fb8c520f9b2f2e8cd5f2b8bd0c595b80005 100644 (file)
@@ -72,6 +72,16 @@ static struct nxm_field nxm_fields[N_NXM_FIELDS] = {
 /* Hash table of 'nxm_fields'. */
 static struct hmap all_nxm_fields = HMAP_INITIALIZER(&all_nxm_fields);
 
+/* Possible masks for NXM_OF_ETH_DST_W. */
+static const uint8_t eth_all_0s[ETH_ADDR_LEN]
+    = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static const uint8_t eth_all_1s[ETH_ADDR_LEN]
+    = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static const uint8_t eth_mcast_1[ETH_ADDR_LEN]
+    = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
+static const uint8_t eth_mcast_0[ETH_ADDR_LEN]
+    = {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff};
+
 static void
 nxm_init(void)
 {
@@ -178,8 +188,34 @@ parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
 
         /* Ethernet header. */
     case NFI_NXM_OF_ETH_DST:
-        memcpy(flow->dl_dst, value, ETH_ADDR_LEN);
-        return 0;
+        if ((wc->wildcards & (OFPFW_DL_DST | FWW_ETH_MCAST))
+            != (OFPFW_DL_DST | FWW_ETH_MCAST)) {
+            return NXM_DUP_TYPE;
+        } else {
+            wc->wildcards &= ~(OFPFW_DL_DST | FWW_ETH_MCAST);
+            memcpy(flow->dl_dst, value, ETH_ADDR_LEN);
+            return 0;
+        }
+    case NFI_NXM_OF_ETH_DST_W:
+        if ((wc->wildcards & (OFPFW_DL_DST | FWW_ETH_MCAST))
+            != (OFPFW_DL_DST | FWW_ETH_MCAST)) {
+            return NXM_DUP_TYPE;
+        } else if (eth_addr_equals(mask, eth_mcast_1)) {
+            wc->wildcards &= ~FWW_ETH_MCAST;
+            flow->dl_dst[0] = *(uint8_t *) value & 0x01;
+        } else if (eth_addr_equals(mask, eth_mcast_0)) {
+            wc->wildcards &= ~OFPFW_DL_DST;
+            memcpy(flow->dl_dst, value, ETH_ADDR_LEN);
+            flow->dl_dst[0] &= 0xfe;
+        } else if (eth_addr_equals(mask, eth_all_0s)) {
+            return 0;
+        } else if (eth_addr_equals(mask, eth_all_1s)) {
+            wc->wildcards &= ~(OFPFW_DL_DST | FWW_ETH_MCAST);
+            memcpy(flow->dl_dst, value, ETH_ADDR_LEN);
+            return 0;
+        } else {
+            return NXM_BAD_MASK;
+        }
     case NFI_NXM_OF_ETH_SRC:
         memcpy(flow->dl_src, value, ETH_ADDR_LEN);
         return 0;
@@ -480,7 +516,6 @@ nxm_put_64(struct ofpbuf *b, uint32_t header, ovs_be64 value)
     ofpbuf_put(b, &value, sizeof value);
 }
 
-
 static void
 nxm_put_eth(struct ofpbuf *b, uint32_t header,
             const uint8_t value[ETH_ADDR_LEN])
@@ -489,6 +524,29 @@ nxm_put_eth(struct ofpbuf *b, uint32_t header,
     ofpbuf_put(b, value, ETH_ADDR_LEN);
 }
 
+static void
+nxm_put_eth_dst(struct ofpbuf *b,
+                uint32_t wc, const uint8_t value[ETH_ADDR_LEN])
+{
+    switch (wc & (OFPFW_DL_DST | FWW_ETH_MCAST)) {
+    case OFPFW_DL_DST | FWW_ETH_MCAST:
+        break;
+    case OFPFW_DL_DST:
+        nxm_put_header(b, NXM_OF_ETH_DST_W);
+        ofpbuf_put(b, value, ETH_ADDR_LEN);
+        ofpbuf_put(b, eth_mcast_1, ETH_ADDR_LEN);
+        break;
+    case FWW_ETH_MCAST:
+        nxm_put_header(b, NXM_OF_ETH_DST_W);
+        ofpbuf_put(b, value, ETH_ADDR_LEN);
+        ofpbuf_put(b, eth_mcast_0, ETH_ADDR_LEN);
+        break;
+    case 0:
+        nxm_put_eth(b, NXM_OF_ETH_DST, value);
+        break;
+    }
+}
+
 int
 nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
 {
@@ -509,9 +567,7 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
     }
 
     /* Ethernet. */
-    if (!(wc & OFPFW_DL_DST)) {
-        nxm_put_eth(b, NXM_OF_ETH_DST, flow->dl_dst);
-    }
+    nxm_put_eth_dst(b, wc, flow->dl_dst);
     if (!(wc & OFPFW_DL_SRC)) {
         nxm_put_eth(b, NXM_OF_ETH_SRC, flow->dl_src);
     }
@@ -907,6 +963,7 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow)
 #error
 #endif
 
+    case NFI_NXM_OF_ETH_DST_W:
     case NFI_NXM_OF_VLAN_TCI_W:
     case NFI_NXM_OF_IP_SRC_W:
     case NFI_NXM_OF_IP_DST_W:
index e70a1e8581848d4ee10ac44998fff1287e865f5c..f6167af43cd9f643d3d2002e60280fe354b28bdf 100644 (file)
@@ -21,7 +21,7 @@
 /*             NXM_ bit      OFPFW_* bit     dl_type       nw_proto      */
 /*             ------------  --------------  -----------   ------------- */
 DEFINE_FIELD  (OF_IN_PORT,   OFPFW_IN_PORT,  0,            0)
-DEFINE_FIELD  (OF_ETH_DST,   OFPFW_DL_DST,   0,            0)
+DEFINE_FIELD_M(OF_ETH_DST,   0,              0,            0)
 DEFINE_FIELD  (OF_ETH_SRC,   OFPFW_DL_SRC,   0,            0)
 DEFINE_FIELD  (OF_ETH_TYPE,  OFPFW_DL_TYPE,  0,            0)
 DEFINE_FIELD_M(OF_VLAN_TCI,  0,              0,            0)
index 2ef82a5316fa977473a8dbfa9046f0a6be2cfae2..718249d0e847f323eda915ec53fe111b1a2c60eb 100644 (file)
@@ -36,6 +36,10 @@ NXM_OF_IN_PORT(fffe)
 
 # eth dst
 NXM_OF_ETH_DST(0002e30f80a4)
+NXM_OF_ETH_DST_W(010000000000/010000000000)
+NXM_OF_ETH_DST_W(000000000000/010000000000)
+NXM_OF_ETH_DST_W(0002e30f80a4/ffffffffffff)
+NXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff)
 
 # eth src
 NXM_OF_ETH_SRC(020898456ddb)
@@ -137,6 +141,10 @@ NXM_OF_IN_PORT(fffe)
 
 # eth dst
 NXM_OF_ETH_DST(0002e30f80a4)
+NXM_OF_ETH_DST_W(010000000000/010000000000)
+NXM_OF_ETH_DST_W(000000000000/010000000000)
+NXM_OF_ETH_DST(0002e30f80a4)
+NXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff)
 
 # eth src
 NXM_OF_ETH_SRC(020898456ddb)
index 5da4bf1907b7d0c422e5d510ac5fc6ce9356e46b..a0ed0241dec18d2d062652d53060974a25aa7aab 100644 (file)
@@ -51,7 +51,8 @@
     CLS_FIELD(OFPFW_TP_SRC,      tp_src,      TP_SRC)       \
     CLS_FIELD(OFPFW_TP_DST,      tp_dst,      TP_DST)       \
     CLS_FIELD(OFPFW_DL_SRC,      dl_src,      DL_SRC)       \
-    CLS_FIELD(OFPFW_DL_DST,      dl_dst,      DL_DST)       \
+    CLS_FIELD(OFPFW_DL_DST | FWW_ETH_MCAST,                 \
+                                 dl_dst,      DL_DST)       \
     CLS_FIELD(OFPFW_NW_PROTO,    nw_proto,    NW_PROTO)     \
     CLS_FIELD(OFPFW_DL_VLAN_PCP, dl_vlan_pcp, DL_VLAN_PCP)  \
     CLS_FIELD(OFPFW_NW_TOS,      nw_tos,      NW_TOS)