From 1e37a2d75196c6e10ac078213ad94afb4f3e219a Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 11 Nov 2010 10:46:23 -0800 Subject: [PATCH] Add support for matching Ethernet multicast frames. --- include/openflow/nicira-ext.h | 6 ++- lib/classifier.c | 19 ++++++++-- lib/flow.c | 8 +++- lib/flow.h | 29 +++++++++++---- lib/nx-match.c | 69 ++++++++++++++++++++++++++++++++--- lib/nx-match.def | 2 +- tests/ovs-ofctl.at | 8 ++++ tests/test-classifier.c | 3 +- 8 files changed, 122 insertions(+), 22 deletions(-) diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index 3a4a6940..3c2856f9 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -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. diff --git a/lib/classifier.c b/lib/classifier.c index 47a1bd77..51d338b0 100644 --- a/lib/classifier.c +++ b/lib/classifier.c @@ -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; diff --git a/lib/flow.c b/lib/flow.c index 72202106..0b6541f6 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -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); } diff --git a/lib/flow.h b/lib/flow.h index a87efc12..a19a9ae5 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -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. */ diff --git a/lib/nx-match.c b/lib/nx-match.c index 8e918ebb..12ea4fb8 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -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: diff --git a/lib/nx-match.def b/lib/nx-match.def index e70a1e85..f6167af4 100644 --- a/lib/nx-match.def +++ b/lib/nx-match.def @@ -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) diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 2ef82a53..718249d0 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -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) diff --git a/tests/test-classifier.c b/tests/test-classifier.c index 5da4bf19..a0ed0241 100644 --- a/tests/test-classifier.c +++ b/tests/test-classifier.c @@ -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) -- 2.30.2