------------------------
- ovs-ofctl:
- "mod-port" command can now control all OpenFlow config flags.
+ - Added support for arbitrary ethernet masks
v1.7.0 - xx xxx xxxx
*
* 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.
*
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
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];
}
}
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)
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);
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));
}
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]) {
&& (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))
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]);
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];
}
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;
}
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);
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);
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);
}
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);
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);
}
{
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)
|| 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)
{
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)
|| 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)
{
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;
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'. */
/* 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);
}
{
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
|| !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;
}
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]) {
}
}
+ 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;
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)
/* 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);
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 *);
/* 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".
*
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 *);
* 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);
}
}
}
{
MFF_ETH_SRC, "eth_src", "dl_src",
MF_FIELD_SIZES(mac),
- MFM_NONE, FWW_DL_SRC,
+ MFM_FULLY, 0,
MFS_ETHERNET,
MFP_NONE,
true,
}, {
MFF_ETH_DST, "eth_dst", "dl_dst",
MF_FIELD_SIZES(mac),
- MFM_MCAST, 0,
+ MFM_FULLY, 0,
MFS_ETHERNET,
MFP_NONE,
true,
{
switch (mf->id) {
case MFF_IN_PORT:
- case MFF_ETH_SRC:
case MFF_ETH_TYPE:
case MFF_IP_PROTO:
case MFF_IP_DSCP:
#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;
{
switch (mf->id) {
case MFF_IN_PORT:
- case MFF_ETH_SRC:
case MFF_ETH_TYPE:
case MFF_IP_PROTO:
case MFF_IP_DSCP:
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:
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();
#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:
switch (mf->id) {
case MFF_IN_PORT:
- case MFF_ETH_SRC:
case MFF_ETH_TYPE:
case MFF_VLAN_VID:
case MFF_VLAN_PCP:
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:
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. */
}
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);
+ }
}
}
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)) {
}
/* 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));
void nxm_decode_discrete(struct mf_subfield *, ovs_be32 header,
ovs_be16 ofs, ovs_be16 n_bits);
\f
-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:
*
* ------ ----- ---- -----
* 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
* 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.
*/
* 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)
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);
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. */
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);
{
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;
}
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));
}
}
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)
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)
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)
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);
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) {
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,