From 73c0ce349ba8d13a63a249a56aad0bec6e6caf26 Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Tue, 29 May 2012 00:38:21 +1200 Subject: [PATCH] flow: Adds support for arbitrary ethernet masking Arbitrary ethernet mask support is one step on the way to support for OpenFlow 1.1+. This patch set seeks to add this capability without breaking current protocol support. Signed-off-by: Joe Stringer [blp@nicira.com made some updates, see http://openvswitch.org/pipermail/dev/2012-May/017585.html] Signed-off-by: Ben Pfaff --- NEWS | 1 + include/openflow/nicira-ext.h | 8 +- lib/classifier.c | 71 +++++++++--------- lib/classifier.h | 2 + lib/flow.c | 134 +++++++++------------------------- lib/flow.h | 31 ++++---- lib/learn.c | 2 +- lib/meta-flow.c | 38 +++++----- lib/meta-flow.h | 3 +- lib/nx-match.c | 32 ++++---- lib/nx-match.h | 6 +- lib/ofp-util.c | 31 +++++--- lib/packets.c | 2 +- tests/ovs-ofctl.at | 6 ++ tests/test-classifier.c | 14 +++- utilities/ovs-ofctl.8.in | 8 +- 16 files changed, 176 insertions(+), 213 deletions(-) diff --git a/NEWS b/NEWS index 9e5ad14d..99680b29 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ post-v1.7.0 ------------------------ - ovs-ofctl: - "mod-port" command can now control all OpenFlow config flags. + - Added support for arbitrary ethernet masks v1.7.0 - xx xxx xxxx diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index 21888a9d..55d74eda 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -1368,13 +1368,13 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24); * * Format: 48-bit Ethernet MAC address. * - * 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. */ + * Masking: Fully maskable, in versions 1.8 and later. Earlier versions only + * supported the following masks for NXM_OF_ETH_DST_W: 00:00:00:00:00:00, + * fe:ff:ff:ff:ff:ff, 01:00:00:00:00:00, ff:ff:ff:ff:ff:ff. */ #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) +#define NXM_OF_ETH_SRC_W NXM_HEADER_W(0x0000, 2, 6) /* Packet's Ethernet type. * diff --git a/lib/classifier.c b/lib/classifier.c index e11a5852..327a8b2c 100644 --- a/lib/classifier.c +++ b/lib/classifier.c @@ -149,16 +149,31 @@ cls_rule_set_dl_type(struct cls_rule *rule, ovs_be16 dl_type) void cls_rule_set_dl_src(struct cls_rule *rule, const uint8_t dl_src[ETH_ADDR_LEN]) { - rule->wc.wildcards &= ~FWW_DL_SRC; memcpy(rule->flow.dl_src, dl_src, ETH_ADDR_LEN); + memset(rule->wc.dl_src_mask, 0xff, ETH_ADDR_LEN); +} + +/* Modifies 'rule' so that the Ethernet address must match 'dl_src' after each + * byte is ANDed with the appropriate byte in 'mask'. */ +void +cls_rule_set_dl_src_masked(struct cls_rule *rule, + const uint8_t dl_src[ETH_ADDR_LEN], + const uint8_t mask[ETH_ADDR_LEN]) +{ + size_t i; + + for (i = 0; i < ETH_ADDR_LEN; i++) { + rule->flow.dl_src[i] = dl_src[i] & mask[i]; + rule->wc.dl_src_mask[i] = mask[i]; + } } /* Modifies 'rule' so that the Ethernet address must match 'dl_dst' exactly. */ void cls_rule_set_dl_dst(struct cls_rule *rule, const uint8_t dl_dst[ETH_ADDR_LEN]) { - rule->wc.wildcards &= ~(FWW_DL_DST | FWW_ETH_MCAST); memcpy(rule->flow.dl_dst, dl_dst, ETH_ADDR_LEN); + memset(rule->wc.dl_dst_mask, 0xff, ETH_ADDR_LEN); } /* Modifies 'rule' so that the Ethernet address must match 'dl_dst' after each @@ -171,12 +186,11 @@ cls_rule_set_dl_dst_masked(struct cls_rule *rule, const uint8_t dl_dst[ETH_ADDR_LEN], const uint8_t mask[ETH_ADDR_LEN]) { - flow_wildcards_t *wc = &rule->wc.wildcards; size_t i; - *wc = flow_wildcards_set_dl_dst_mask(*wc, mask); for (i = 0; i < ETH_ADDR_LEN; i++) { rule->flow.dl_dst[i] = dl_dst[i] & mask[i]; + rule->wc.dl_dst_mask[i] = mask[i]; } } @@ -448,6 +462,17 @@ cls_rule_hash(const struct cls_rule *rule, uint32_t basis) return hash_int(rule->priority, h1); } +static void +format_eth_masked(struct ds *s, const char *name, const uint8_t eth[6], + const uint8_t mask[6]) +{ + if (!eth_addr_is_zero(mask)) { + ds_put_format(s, "%s=", name); + eth_format_masked(eth, mask, s); + ds_put_char(s, ','); + } +} + static void format_ip_netmask(struct ds *s, const char *name, ovs_be32 ip, ovs_be32 netmask) @@ -500,7 +525,7 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s) int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); if (rule->priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "priority=%d,", rule->priority); @@ -597,24 +622,8 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s) ntohs(f->vlan_tci), ntohs(wc->vlan_tci_mask)); } } - if (!(w & FWW_DL_SRC)) { - ds_put_format(s, "dl_src="ETH_ADDR_FMT",", ETH_ADDR_ARGS(f->dl_src)); - } - switch (w & (FWW_DL_DST | FWW_ETH_MCAST)) { - case 0: - ds_put_format(s, "dl_dst="ETH_ADDR_FMT",", ETH_ADDR_ARGS(f->dl_dst)); - break; - case FWW_DL_DST: - ds_put_format(s, "dl_dst="ETH_ADDR_FMT"/01:00:00:00:00:00,", - ETH_ADDR_ARGS(f->dl_dst)); - break; - case FWW_ETH_MCAST: - ds_put_format(s, "dl_dst="ETH_ADDR_FMT"/fe:ff:ff:ff:ff:ff,", - ETH_ADDR_ARGS(f->dl_dst)); - break; - case FWW_DL_DST | FWW_ETH_MCAST: - break; - } + format_eth_masked(s, "dl_src", f->dl_src, wc->dl_src_mask); + format_eth_masked(s, "dl_dst", f->dl_dst, wc->dl_dst_mask); if (!skip_type && !(w & FWW_DL_TYPE)) { ds_put_format(s, "dl_type=0x%04"PRIx16",", ntohs(f->dl_type)); } @@ -1179,7 +1188,7 @@ flow_equal_except(const struct flow *a, const struct flow *b, const flow_wildcards_t wc = wildcards->wildcards; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); for (i = 0; i < FLOW_N_REGS; i++) { if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) { @@ -1195,16 +1204,10 @@ flow_equal_except(const struct flow *a, const struct flow *b, && (wc & FWW_DL_TYPE || a->dl_type == b->dl_type) && !((a->tp_src ^ b->tp_src) & wildcards->tp_src_mask) && !((a->tp_dst ^ b->tp_dst) & wildcards->tp_dst_mask) - && (wc & FWW_DL_SRC || eth_addr_equals(a->dl_src, b->dl_src)) - && (wc & FWW_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)) + && !eth_addr_equal_except(a->dl_src, b->dl_src, + wildcards->dl_src_mask) + && !eth_addr_equal_except(a->dl_dst, b->dl_dst, + wildcards->dl_dst_mask) && (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto) && (wc & FWW_NW_TTL || a->nw_ttl == b->nw_ttl) && (wc & FWW_NW_DSCP || !((a->nw_tos ^ b->nw_tos) & IP_DSCP_MASK)) diff --git a/lib/classifier.h b/lib/classifier.h index 92ccc2fb..9e4b33e2 100644 --- a/lib/classifier.h +++ b/lib/classifier.h @@ -96,6 +96,8 @@ void cls_rule_set_tun_id_masked(struct cls_rule *, void cls_rule_set_in_port(struct cls_rule *, uint16_t ofp_port); void cls_rule_set_dl_type(struct cls_rule *, ovs_be16); void cls_rule_set_dl_src(struct cls_rule *, const uint8_t[6]); +void cls_rule_set_dl_src_masked(struct cls_rule *, const uint8_t dl_src[6], + const uint8_t mask[6]); void cls_rule_set_dl_dst(struct cls_rule *, const uint8_t[6]); void cls_rule_set_dl_dst_masked(struct cls_rule *, const uint8_t dl_dst[6], const uint8_t mask[6]); diff --git a/lib/flow.c b/lib/flow.c index fc616106..46e0e2d2 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -444,7 +444,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards) const flow_wildcards_t wc = wildcards->wildcards; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); for (i = 0; i < FLOW_N_REGS; i++) { flow->regs[i] &= wildcards->reg_masks[i]; @@ -461,16 +461,8 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards) } flow->tp_src &= wildcards->tp_src_mask; flow->tp_dst &= wildcards->tp_dst_mask; - if (wc & FWW_DL_SRC) { - memset(flow->dl_src, 0, sizeof flow->dl_src); - } - if (wc & FWW_DL_DST) { - flow->dl_dst[0] &= 0x01; - memset(&flow->dl_dst[1], 0, 5); - } - if (wc & FWW_ETH_MCAST) { - flow->dl_dst[0] &= 0xfe; - } + eth_addr_bitand(flow->dl_src, wildcards->dl_src_mask, flow->dl_src); + eth_addr_bitand(flow->dl_dst, wildcards->dl_dst_mask, flow->dl_dst); if (wc & FWW_NW_PROTO) { flow->nw_proto = 0; } @@ -506,7 +498,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards) void flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); fmd->tun_id = flow->tun_id; fmd->tun_id_mask = htonll(UINT64_MAX); @@ -595,7 +587,7 @@ flow_print(FILE *stream, const struct flow *flow) void flow_wildcards_init_catchall(struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); wc->wildcards = FWW_ALL; wc->tun_id_mask = htonll(0); @@ -609,6 +601,8 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc) wc->nw_frag_mask = 0; wc->tp_src_mask = htons(0); wc->tp_dst_mask = htons(0); + memset(wc->dl_src_mask, 0, ETH_ADDR_LEN); + memset(wc->dl_dst_mask, 0, ETH_ADDR_LEN); memset(wc->zeros, 0, sizeof wc->zeros); } @@ -617,7 +611,7 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc) void flow_wildcards_init_exact(struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); wc->wildcards = 0; wc->tun_id_mask = htonll(UINT64_MAX); @@ -631,6 +625,8 @@ flow_wildcards_init_exact(struct flow_wildcards *wc) wc->nw_frag_mask = UINT8_MAX; wc->tp_src_mask = htons(UINT16_MAX); wc->tp_dst_mask = htons(UINT16_MAX); + memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN); + memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN); memset(wc->zeros, 0, sizeof wc->zeros); } @@ -641,7 +637,7 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc) { int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); if (wc->wildcards || wc->tun_id_mask != htonll(UINT64_MAX) @@ -650,6 +646,8 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc) || wc->tp_src_mask != htons(UINT16_MAX) || wc->tp_dst_mask != htons(UINT16_MAX) || wc->vlan_tci_mask != htons(UINT16_MAX) + || !eth_mask_is_exact(wc->dl_src_mask) + || !eth_mask_is_exact(wc->dl_dst_mask) || !ipv6_mask_is_exact(&wc->ipv6_src_mask) || !ipv6_mask_is_exact(&wc->ipv6_dst_mask) || !ipv6_mask_is_exact(&wc->nd_target_mask) @@ -673,7 +671,7 @@ flow_wildcards_is_catchall(const struct flow_wildcards *wc) { int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); if (wc->wildcards != FWW_ALL || wc->tun_id_mask != htonll(0) @@ -682,6 +680,8 @@ flow_wildcards_is_catchall(const struct flow_wildcards *wc) || wc->tp_src_mask != htons(0) || wc->tp_dst_mask != htons(0) || wc->vlan_tci_mask != htons(0) + || !eth_addr_is_zero(wc->dl_src_mask) + || !eth_addr_is_zero(wc->dl_dst_mask) || !ipv6_mask_is_any(&wc->ipv6_src_mask) || !ipv6_mask_is_any(&wc->ipv6_dst_mask) || !ipv6_mask_is_any(&wc->nd_target_mask) @@ -708,7 +708,7 @@ flow_wildcards_combine(struct flow_wildcards *dst, { int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); dst->wildcards = src1->wildcards | src2->wildcards; dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask; @@ -726,6 +726,8 @@ flow_wildcards_combine(struct flow_wildcards *dst, dst->vlan_tci_mask = src1->vlan_tci_mask & src2->vlan_tci_mask; dst->tp_src_mask = src1->tp_src_mask & src2->tp_src_mask; dst->tp_dst_mask = src1->tp_dst_mask & src2->tp_dst_mask; + eth_addr_bitand(src1->dl_src_mask, src2->dl_src_mask, dst->dl_src_mask); + eth_addr_bitand(src1->dl_dst_mask, src2->dl_dst_mask, dst->dl_dst_mask); } /* Returns a hash of the wildcards in 'wc'. */ @@ -735,7 +737,7 @@ flow_wildcards_hash(const struct flow_wildcards *wc, uint32_t basis) /* If you change struct flow_wildcards and thereby trigger this * assertion, please check that the new struct flow_wildcards has no holes * in it before you update the assertion. */ - BUILD_ASSERT_DECL(sizeof *wc == 80 + FLOW_N_REGS * 4); + BUILD_ASSERT_DECL(sizeof *wc == 88 + FLOW_N_REGS * 4); return hash_bytes(wc, sizeof *wc, basis); } @@ -747,7 +749,7 @@ flow_wildcards_equal(const struct flow_wildcards *a, { int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); if (a->wildcards != b->wildcards || a->tun_id_mask != b->tun_id_mask @@ -758,7 +760,9 @@ flow_wildcards_equal(const struct flow_wildcards *a, || !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_mask) || !ipv6_addr_equals(&a->nd_target_mask, &b->nd_target_mask) || a->tp_src_mask != b->tp_src_mask - || a->tp_dst_mask != b->tp_dst_mask) { + || a->tp_dst_mask != b->tp_dst_mask + || !eth_addr_equals(a->dl_src_mask, b->dl_src_mask) + || !eth_addr_equals(a->dl_dst_mask, b->dl_dst_mask)) { return false; } @@ -778,9 +782,10 @@ flow_wildcards_has_extra(const struct flow_wildcards *a, const struct flow_wildcards *b) { int i; + uint8_t eth_masked[ETH_ADDR_LEN]; struct in6_addr ipv6_masked; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); for (i = 0; i < FLOW_N_REGS; i++) { if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) { @@ -788,6 +793,16 @@ flow_wildcards_has_extra(const struct flow_wildcards *a, } } + eth_addr_bitand(a->dl_src_mask, b->dl_src_mask, eth_masked); + if (!eth_addr_equals(eth_masked, b->dl_src_mask)) { + return true; + } + + eth_addr_bitand(a->dl_dst_mask, b->dl_dst_mask, eth_masked); + if (!eth_addr_equals(eth_masked, b->dl_dst_mask)) { + return true; + } + ipv6_masked = ipv6_addr_bitand(&a->ipv6_src_mask, &b->ipv6_src_mask); if (!ipv6_addr_equals(&ipv6_masked, &b->ipv6_src_mask)) { return true; @@ -820,83 +835,6 @@ flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask) wc->reg_masks[idx] = mask; } -/* Returns the wildcard bitmask for the Ethernet destination address - * that 'wc' specifies. The bitmask has a 0 in each bit that is wildcarded - * and a 1 in each bit that must match. */ -const uint8_t * -flow_wildcards_to_dl_dst_mask(flow_wildcards_t wc) -{ - static const uint8_t no_wild[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - static const uint8_t addr_wild[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; - static const uint8_t mcast_wild[] = {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff}; - static const uint8_t all_wild[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - switch (wc & (FWW_DL_DST | FWW_ETH_MCAST)) { - case 0: return no_wild; - case FWW_DL_DST: return addr_wild; - case FWW_ETH_MCAST: return mcast_wild; - case FWW_DL_DST | FWW_ETH_MCAST: return all_wild; - } - NOT_REACHED(); -} - -/* Returns true if 'mask' is a valid wildcard bitmask for the Ethernet - * destination address. Valid bitmasks are either all-bits-0 or all-bits-1, - * except that the multicast bit may differ from the rest of the bits. So, - * there are four possible valid bitmasks: - * - * - 00:00:00:00:00:00 - * - 01:00:00:00:00:00 - * - fe:ff:ff:ff:ff:ff - * - ff:ff:ff:ff:ff:ff - * - * All other bitmasks are invalid. */ -bool -flow_wildcards_is_dl_dst_mask_valid(const uint8_t mask[ETH_ADDR_LEN]) -{ - switch (mask[0]) { - case 0x00: - case 0x01: - return (mask[1] | mask[2] | mask[3] | mask[4] | mask[5]) == 0x00; - - case 0xfe: - case 0xff: - return (mask[1] & mask[2] & mask[3] & mask[4] & mask[5]) == 0xff; - - default: - return false; - } -} - -/* Returns 'wc' with the FWW_DL_DST and FWW_ETH_MCAST bits modified - * appropriately to match 'mask'. - * - * This function will assert-fail if 'mask' is invalid. Only 'mask' values - * accepted by flow_wildcards_is_dl_dst_mask_valid() are allowed. */ -flow_wildcards_t -flow_wildcards_set_dl_dst_mask(flow_wildcards_t wc, - const uint8_t mask[ETH_ADDR_LEN]) -{ - assert(flow_wildcards_is_dl_dst_mask_valid(mask)); - - switch (mask[0]) { - case 0x00: - return wc | FWW_DL_DST | FWW_ETH_MCAST; - - case 0x01: - return (wc | FWW_DL_DST) & ~FWW_ETH_MCAST; - - case 0xfe: - return (wc & ~FWW_DL_DST) | FWW_ETH_MCAST; - - case 0xff: - return wc & ~(FWW_DL_DST | FWW_ETH_MCAST); - - default: - NOT_REACHED(); - } -} - /* Hashes 'flow' based on its L2 through L4 protocol information. */ uint32_t flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis) diff --git a/lib/flow.h b/lib/flow.h index 7ee9a268..1964115c 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -35,7 +35,7 @@ struct ofpbuf; /* This sequence number should be incremented whenever anything involving flows * or the wildcarding of flows changes. This will cause build assertion * failures in places which likely need to be updated. */ -#define FLOW_WC_SEQ 10 +#define FLOW_WC_SEQ 11 #define FLOW_N_REGS 8 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS); @@ -100,7 +100,7 @@ BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nw_frag) == 1); BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE); /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ -BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 142 && FLOW_WC_SEQ == 10); +BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 142 && FLOW_WC_SEQ == 11); void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id, uint16_t in_port, struct flow *); @@ -147,24 +147,19 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t; /* Same values and meanings as corresponding OFPFW_* bits. */ #define FWW_IN_PORT ((OVS_FORCE flow_wildcards_t) (1 << 0)) -#define FWW_DL_SRC ((OVS_FORCE flow_wildcards_t) (1 << 2)) -#define FWW_DL_DST ((OVS_FORCE flow_wildcards_t) (1 << 3)) - /* excluding the multicast bit */ #define FWW_DL_TYPE ((OVS_FORCE flow_wildcards_t) (1 << 4)) #define FWW_NW_PROTO ((OVS_FORCE flow_wildcards_t) (1 << 5)) /* No corresponding OFPFW_* bits. */ -#define FWW_ETH_MCAST ((OVS_FORCE flow_wildcards_t) (1 << 1)) - /* multicast bit only */ -#define FWW_NW_DSCP ((OVS_FORCE flow_wildcards_t) (1 << 6)) -#define FWW_NW_ECN ((OVS_FORCE flow_wildcards_t) (1 << 7)) -#define FWW_ARP_SHA ((OVS_FORCE flow_wildcards_t) (1 << 8)) -#define FWW_ARP_THA ((OVS_FORCE flow_wildcards_t) (1 << 9)) -#define FWW_IPV6_LABEL ((OVS_FORCE flow_wildcards_t) (1 << 10)) -#define FWW_NW_TTL ((OVS_FORCE flow_wildcards_t) (1 << 11)) -#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 12)) - 1)) +#define FWW_NW_DSCP ((OVS_FORCE flow_wildcards_t) (1 << 1)) +#define FWW_NW_ECN ((OVS_FORCE flow_wildcards_t) (1 << 2)) +#define FWW_ARP_SHA ((OVS_FORCE flow_wildcards_t) (1 << 3)) +#define FWW_ARP_THA ((OVS_FORCE flow_wildcards_t) (1 << 6)) +#define FWW_IPV6_LABEL ((OVS_FORCE flow_wildcards_t) (1 << 7)) +#define FWW_NW_TTL ((OVS_FORCE flow_wildcards_t) (1 << 8)) +#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 9)) - 1)) /* Remember to update FLOW_WC_SEQ when adding or removing FWW_*. */ -BUILD_ASSERT_DECL(FWW_ALL == ((1 << 12) - 1) && FLOW_WC_SEQ == 10); +BUILD_ASSERT_DECL(FWW_ALL == ((1 << 9) - 1) && FLOW_WC_SEQ == 11); /* Information on wildcards for a flow, as a supplement to "struct flow". * @@ -184,11 +179,13 @@ struct flow_wildcards { ovs_be16 tp_src_mask; /* 1-bit in each significant tp_src bit. */ ovs_be16 tp_dst_mask; /* 1-bit in each significant tp_dst bit. */ uint8_t nw_frag_mask; /* 1-bit in each significant nw_frag bit. */ - uint8_t zeros[5]; /* Padding field set to zero. */ + uint8_t dl_src_mask[6]; /* 1-bit in each significant dl_src bit. */ + uint8_t dl_dst_mask[6]; /* 1-bit in each significant dl_dst bit. */ + uint8_t zeros[1]; /* Padding field set to zero. */ }; /* Remember to update FLOW_WC_SEQ when updating struct flow_wildcards. */ -BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 112 && FLOW_WC_SEQ == 10); +BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 120 && FLOW_WC_SEQ == 11); void flow_wildcards_init_catchall(struct flow_wildcards *); void flow_wildcards_init_exact(struct flow_wildcards *); diff --git a/lib/learn.c b/lib/learn.c index ba33287a..cbecb10d 100644 --- a/lib/learn.c +++ b/lib/learn.c @@ -184,7 +184,7 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow) * prerequisites. No prerequisite depends on the value of * a field that is wider than 64 bits. So just skip * setting it entirely. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); } } } diff --git a/lib/meta-flow.c b/lib/meta-flow.c index 9387d3a4..f18d1a0b 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -113,7 +113,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = { { MFF_ETH_SRC, "eth_src", "dl_src", MF_FIELD_SIZES(mac), - MFM_NONE, FWW_DL_SRC, + MFM_FULLY, 0, MFS_ETHERNET, MFP_NONE, true, @@ -122,7 +122,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = { }, { MFF_ETH_DST, "eth_dst", "dl_dst", MF_FIELD_SIZES(mac), - MFM_MCAST, 0, + MFM_FULLY, 0, MFS_ETHERNET, MFP_NONE, true, @@ -543,7 +543,6 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) { switch (mf->id) { case MFF_IN_PORT: - case MFF_ETH_SRC: case MFF_ETH_TYPE: case MFF_IP_PROTO: case MFF_IP_DSCP: @@ -590,9 +589,10 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) #endif return !wc->reg_masks[mf->id - MFF_REG0]; + case MFF_ETH_SRC: + return eth_addr_is_zero(wc->dl_src_mask); case MFF_ETH_DST: - return ((wc->wildcards & (FWW_ETH_MCAST | FWW_DL_DST)) - == (FWW_ETH_MCAST | FWW_DL_DST)); + return eth_addr_is_zero(wc->dl_dst_mask); case MFF_VLAN_TCI: return !wc->vlan_tci_mask; @@ -651,7 +651,6 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc, { switch (mf->id) { case MFF_IN_PORT: - case MFF_ETH_SRC: case MFF_ETH_TYPE: case MFF_IP_PROTO: case MFF_IP_DSCP: @@ -702,8 +701,11 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc, break; case MFF_ETH_DST: - memcpy(mask->mac, flow_wildcards_to_dl_dst_mask(wc->wildcards), - ETH_ADDR_LEN); + memcpy(mask->mac, wc->dl_dst_mask, ETH_ADDR_LEN); + break; + + case MFF_ETH_SRC: + memcpy(mask->mac, wc->dl_src_mask, ETH_ADDR_LEN); break; case MFF_VLAN_TCI: @@ -786,9 +788,6 @@ mf_is_mask_valid(const struct mf_field *mf, const union mf_value *mask) return (mf->n_bytes == 4 ? ip_is_cidr(mask->be32) : ipv6_is_cidr(&mask->ipv6)); - - case MFM_MCAST: - return flow_wildcards_is_dl_dst_mask_valid(mask->mac); } NOT_REACHED(); @@ -1532,13 +1531,13 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule) #endif case MFF_ETH_SRC: - rule->wc.wildcards |= FWW_DL_SRC; - memset(rule->flow.dl_src, 0, sizeof rule->flow.dl_src); + memset(rule->flow.dl_src, 0, ETH_ADDR_LEN); + memset(rule->wc.dl_src_mask, 0, ETH_ADDR_LEN); break; case MFF_ETH_DST: - rule->wc.wildcards |= FWW_DL_DST | FWW_ETH_MCAST; - memset(rule->flow.dl_dst, 0, sizeof rule->flow.dl_dst); + memset(rule->flow.dl_dst, 0, ETH_ADDR_LEN); + memset(rule->wc.dl_dst_mask, 0, ETH_ADDR_LEN); break; case MFF_ETH_TYPE: @@ -1678,7 +1677,6 @@ mf_set(const struct mf_field *mf, switch (mf->id) { case MFF_IN_PORT: - case MFF_ETH_SRC: case MFF_ETH_TYPE: case MFF_VLAN_VID: case MFF_VLAN_PCP: @@ -1734,9 +1732,11 @@ mf_set(const struct mf_field *mf, break; case MFF_ETH_DST: - if (flow_wildcards_is_dl_dst_mask_valid(mask->mac)) { - cls_rule_set_dl_dst_masked(rule, value->mac, mask->mac); - } + cls_rule_set_dl_dst_masked(rule, value->mac, mask->mac); + break; + + case MFF_ETH_SRC: + cls_rule_set_dl_src_masked(rule, value->mac, mask->mac); break; case MFF_VLAN_TCI: diff --git a/lib/meta-flow.h b/lib/meta-flow.h index 632cb46d..29e3fa79 100644 --- a/lib/meta-flow.h +++ b/lib/meta-flow.h @@ -144,8 +144,7 @@ enum mf_prereqs { enum mf_maskable { MFM_NONE, /* No sub-field masking. */ MFM_FULLY, /* Every bit is individually maskable. */ - MFM_CIDR, /* Contiguous low-order bits may be masked. */ - MFM_MCAST /* Byte 0, bit 0 is separately maskable. */ + MFM_CIDR /* Contiguous low-order bits may be masked. */ }; /* How to format or parse a field's value. */ diff --git a/lib/nx-match.c b/lib/nx-match.c index 34c8354b..5e3c3dc5 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -354,20 +354,18 @@ nxm_put_eth(struct ofpbuf *b, uint32_t header, } static void -nxm_put_eth_dst(struct ofpbuf *b, - flow_wildcards_t wc, const uint8_t value[ETH_ADDR_LEN]) +nxm_put_eth_masked(struct ofpbuf *b, uint32_t header, + const uint8_t value[ETH_ADDR_LEN], + const uint8_t mask[ETH_ADDR_LEN]) { - switch (wc & (FWW_DL_DST | FWW_ETH_MCAST)) { - case FWW_DL_DST | FWW_ETH_MCAST: - break; - default: - nxm_put_header(b, NXM_OF_ETH_DST_W); - ofpbuf_put(b, value, ETH_ADDR_LEN); - ofpbuf_put(b, flow_wildcards_to_dl_dst_mask(wc), ETH_ADDR_LEN); - break; - case 0: - nxm_put_eth(b, NXM_OF_ETH_DST, value); - break; + if (!eth_addr_is_zero(mask)) { + if (eth_mask_is_exact(mask)) { + nxm_put_eth(b, header, value); + } else { + nxm_put_header(b, NXM_MAKE_WILD_HEADER(header)); + ofpbuf_put(b, value, ETH_ADDR_LEN); + ofpbuf_put(b, mask, ETH_ADDR_LEN); + } } } @@ -471,7 +469,7 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr, int match_len; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); /* Metadata. */ if (!(wc & FWW_IN_PORT)) { @@ -480,10 +478,8 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr, } /* Ethernet. */ - nxm_put_eth_dst(b, wc, flow->dl_dst); - if (!(wc & FWW_DL_SRC)) { - nxm_put_eth(b, NXM_OF_ETH_SRC, flow->dl_src); - } + nxm_put_eth_masked(b, NXM_OF_ETH_SRC, flow->dl_src, cr->wc.dl_src_mask); + nxm_put_eth_masked(b, NXM_OF_ETH_DST, flow->dl_dst, cr->wc.dl_dst_mask); if (!(wc & FWW_DL_TYPE)) { nxm_put_16(b, NXM_OF_ETH_TYPE, ofputil_dl_type_to_openflow(flow->dl_type)); diff --git a/lib/nx-match.h b/lib/nx-match.h index a039225d..fd101b6c 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -90,7 +90,7 @@ void nxm_decode(struct mf_subfield *, ovs_be32 header, ovs_be16 ofs_nbits); void nxm_decode_discrete(struct mf_subfield *, ovs_be32 header, ovs_be16 ofs, ovs_be16 n_bits); -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); /* Upper bound on the length of an nx_match. The longest nx_match (an * IPV6 neighbor discovery message using 5 registers) would be: * @@ -98,7 +98,7 @@ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); * ------ ----- ---- ----- * NXM_OF_IN_PORT 4 2 -- 6 * NXM_OF_ETH_DST_W 4 6 6 16 - * NXM_OF_ETH_SRC 4 6 -- 10 + * NXM_OF_ETH_SRC_W 4 6 6 16 * NXM_OF_ETH_TYPE 4 2 -- 6 * NXM_OF_VLAN_TCI 4 2 2 8 * NXM_OF_IP_TOS 4 1 -- 5 @@ -123,7 +123,7 @@ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); * NXM_NX_REG_W(7) 4 4 4 12 * NXM_NX_TUN_ID_W 4 8 8 20 * ------------------------------------------- - * total 327 + * total 333 * * So this value is conservative. */ diff --git a/lib/ofp-util.c b/lib/ofp-util.c index c326b727..5c5fc99c 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -76,8 +76,6 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask) * name. */ #define WC_INVARIANT_LIST \ WC_INVARIANT_BIT(IN_PORT) \ - WC_INVARIANT_BIT(DL_SRC) \ - WC_INVARIANT_BIT(DL_DST) \ WC_INVARIANT_BIT(DL_TYPE) \ WC_INVARIANT_BIT(NW_PROTO) @@ -101,7 +99,7 @@ static const flow_wildcards_t WC_INVARIANTS = 0 void ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); /* Initialize most of rule->wc. */ flow_wildcards_init_catchall(wc); @@ -127,11 +125,11 @@ ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc) wc->tp_dst_mask = htons(UINT16_MAX); } - if (ofpfw & OFPFW_DL_DST) { - /* OpenFlow 1.0 OFPFW_DL_DST covers the whole Ethernet destination, but - * Open vSwitch breaks the Ethernet destination into bits as FWW_DL_DST - * and FWW_ETH_MCAST. */ - wc->wildcards |= FWW_ETH_MCAST; + if (!(ofpfw & OFPFW_DL_SRC)) { + memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN); + } + if (!(ofpfw & OFPFW_DL_DST)) { + memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN); } /* VLAN TCI mask. */ @@ -212,6 +210,12 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match) if (!wc->tp_dst_mask) { ofpfw |= OFPFW_TP_DST; } + if (eth_addr_is_zero(wc->dl_src_mask)) { + ofpfw |= OFPFW_DL_SRC; + } + if (eth_addr_is_zero(wc->dl_dst_mask)) { + ofpfw |= OFPFW_DL_DST; + } /* Translate VLANs. */ match->dl_vlan = htons(0); @@ -1174,10 +1178,15 @@ ofputil_usable_protocols(const struct cls_rule *rule) { const struct flow_wildcards *wc = &rule->wc; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); - /* Only NXM supports separately wildcards the Ethernet multicast bit. */ - if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) { + /* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */ + if (!eth_mask_is_exact(wc->dl_src_mask) + && !eth_addr_is_zero(wc->dl_src_mask)) { + return OFPUTIL_P_NXM_ANY; + } + if (!eth_mask_is_exact(wc->dl_dst_mask) + && !eth_addr_is_zero(wc->dl_dst_mask)) { return OFPUTIL_P_NXM_ANY; } diff --git a/lib/packets.c b/lib/packets.c index b3851280..631abf83 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -153,7 +153,7 @@ eth_format_masked(const uint8_t eth[ETH_ADDR_LEN], const uint8_t mask[ETH_ADDR_LEN], struct ds *s) { ds_put_format(s, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth)); - if (mask) { + if (mask && !eth_mask_is_exact(mask)) { ds_put_format(s, "/"ETH_ADDR_FMT, ETH_ADDR_ARGS(mask)); } } diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 37498a74..9cface8e 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -230,9 +230,12 @@ NXM_OF_ETH_DST_W(000000000000/010000000000) NXM_OF_ETH_DST_W(ffffffffffff/010000000000) NXM_OF_ETH_DST_W(0002e30f80a4/ffffffffffff) NXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff) +NXM_OF_ETH_DST_W(60175619848f/5a5a5a5a5a5a) # eth src NXM_OF_ETH_SRC(020898456ddb) +NXM_OF_ETH_SRC_W(012345abcdef/ffffff555555) +NXM_OF_ETH_SRC_W(020898456ddb/ffffffffffff) # eth type NXM_OF_ETH_TYPE(0800) @@ -427,9 +430,12 @@ NXM_OF_ETH_DST_W(000000000000/010000000000) NXM_OF_ETH_DST_W(010000000000/010000000000) NXM_OF_ETH_DST(0002e30f80a4) NXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff) +NXM_OF_ETH_DST_W(40125218000a/5a5a5a5a5a5a) # eth src NXM_OF_ETH_SRC(020898456ddb) +NXM_OF_ETH_SRC_W(012345014545/ffffff555555) +NXM_OF_ETH_SRC(020898456ddb) # eth type NXM_OF_ETH_TYPE(0800) diff --git a/tests/test-classifier.c b/tests/test-classifier.c index fcafdb2d..7403159d 100644 --- a/tests/test-classifier.c +++ b/tests/test-classifier.c @@ -52,8 +52,8 @@ CLS_FIELD(FWW_DL_TYPE, dl_type, DL_TYPE) \ CLS_FIELD(0, tp_src, TP_SRC) \ CLS_FIELD(0, tp_dst, TP_DST) \ - CLS_FIELD(FWW_DL_SRC, dl_src, DL_SRC) \ - CLS_FIELD(FWW_DL_DST | FWW_ETH_MCAST, dl_dst, DL_DST) \ + CLS_FIELD(0, dl_src, DL_SRC) \ + CLS_FIELD(0, dl_dst, DL_DST) \ CLS_FIELD(FWW_NW_PROTO, nw_proto, NW_PROTO) \ CLS_FIELD(FWW_NW_DSCP, nw_tos, NW_DSCP) @@ -202,6 +202,12 @@ match(const struct cls_rule *wild, const struct flow *fixed) eq = !((fixed->tp_src ^ wild->flow.tp_src) & wild->wc.tp_src_mask); } else if (f_idx == CLS_F_IDX_TP_DST) { eq = !((fixed->tp_dst ^ wild->flow.tp_dst) & wild->wc.tp_dst_mask); + } else if (f_idx == CLS_F_IDX_DL_SRC) { + eq = !eth_addr_equal_except(fixed->dl_src, wild->flow.dl_src, + wild->wc.dl_src_mask); + } else if (f_idx == CLS_F_IDX_DL_DST) { + eq = !eth_addr_equal_except(fixed->dl_dst, wild->flow.dl_dst, + wild->wc.dl_dst_mask); } else if (f_idx == CLS_F_IDX_VLAN_TCI) { eq = !((fixed->vlan_tci ^ wild->flow.vlan_tci) & wild->wc.vlan_tci_mask); @@ -471,6 +477,10 @@ make_rule(int wc_fields, unsigned int priority, int value_pat) rule->cls_rule.wc.tp_src_mask = htons(UINT16_MAX); } else if (f_idx == CLS_F_IDX_TP_DST) { rule->cls_rule.wc.tp_dst_mask = htons(UINT16_MAX); + } else if (f_idx == CLS_F_IDX_DL_SRC) { + memset(rule->cls_rule.wc.dl_src_mask, 0xff, ETH_ADDR_LEN); + } else if (f_idx == CLS_F_IDX_DL_DST) { + memset(rule->cls_rule.wc.dl_dst_mask, 0xff, ETH_ADDR_LEN); } else if (f_idx == CLS_F_IDX_VLAN_TCI) { rule->cls_rule.wc.vlan_tci_mask = htons(UINT16_MAX); } else if (f_idx == CLS_F_IDX_TUN_ID) { diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 67cc3e95..085a2c25 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -381,11 +381,13 @@ Matches an Ethernet source (or destination) address specified as 6 pairs of hexadecimal digits delimited by colons (e.g. \fB00:0A:E4:25:6B:B0\fR). . -.IP \fBdl_dst=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR +.IP \fBdl_src=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR +.IQ \fBdl_dst=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR Matches an Ethernet destination address specified as 6 pairs of hexadecimal digits delimited by colons (e.g. \fB00:0A:E4:25:6B:B0\fR), -with a wildcard mask following the slash. Only -the following masks are allowed: +with a wildcard mask following the slash. Open vSwitch 1.8 and later +support arbitrary masks for source and/or destination. Earlier +versions only support masking the destination with the following masks: .RS .IP \fB01:00:00:00:00:00\fR Match only the multicast bit. Thus, -- 2.30.2