aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--datapath/flow.c6
-rw-r--r--datapath/flow.h2
-rw-r--r--include/openflow/nicira-ext.h19
-rw-r--r--include/openvswitch/datapath-protocol.h2
-rw-r--r--lib/classifier.c38
-rw-r--r--lib/classifier.h2
-rw-r--r--lib/flow.c18
-rw-r--r--lib/flow.h15
-rw-r--r--lib/nx-match.c21
-rw-r--r--lib/nx-match.def2
-rw-r--r--lib/odp-util.c10
-rw-r--r--lib/ofp-parse.c14
-rw-r--r--lib/ofp-util.c9
-rw-r--r--tests/ovs-ofctl.at32
-rw-r--r--utilities/ovs-ofctl.8.in14
15 files changed, 177 insertions, 27 deletions
diff --git a/datapath/flow.c b/datapath/flow.c
index eb67cf45..d83c17d9 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -390,6 +390,8 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
|| key->nw_proto == ARPOP_REPLY) {
memcpy(&key->nw_src, arp->ar_sip, sizeof(key->nw_src));
memcpy(&key->nw_dst, arp->ar_tip, sizeof(key->nw_dst));
+ memcpy(key->arp_sha, arp->ar_sha, ETH_ALEN);
+ memcpy(key->arp_tha, arp->ar_tha, ETH_ALEN);
}
}
}
@@ -538,6 +540,8 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
if (arp_key->arp_op & htons(0xff00))
return -EINVAL;
swkey->nw_proto = ntohs(arp_key->arp_op);
+ memcpy(swkey->arp_sha, arp_key->arp_sha, ETH_ALEN);
+ memcpy(swkey->arp_tha, arp_key->arp_tha, ETH_ALEN);
break;
default:
@@ -665,6 +669,8 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
arp_key->arp_sip = swkey->nw_src;
arp_key->arp_tip = swkey->nw_dst;
arp_key->arp_op = htons(swkey->nw_proto);
+ memcpy(arp_key->arp_sha, swkey->arp_sha, ETH_ALEN);
+ memcpy(arp_key->arp_tha, swkey->arp_tha, ETH_ALEN);
}
return 0;
diff --git a/datapath/flow.h b/datapath/flow.h
index be734532..b9af2722 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -42,6 +42,8 @@ struct sw_flow_key {
u8 dl_dst[ETH_ALEN]; /* Ethernet destination address. */
u8 nw_proto; /* IP protocol or lower 8 bits of ARP opcode. */
u8 nw_tos; /* IP ToS (DSCP field, 6 bits). */
+ u8 arp_sha[ETH_ALEN]; /* ARP source hardware address. */
+ u8 arp_tha[ETH_ALEN]; /* ARP target hardware address. */
};
struct sw_flow {
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 5013a5a0..c5b0a7ca 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -312,9 +312,9 @@ OFP_ASSERT(sizeof(struct nx_action_set_tunnel64) == 24);
* Ethernet+IPv4 ARP packet for which the source Ethernet address inside the
* ARP packet differs from the source Ethernet address in the Ethernet header.
*
- * This is useful because OpenFlow does not provide a way to match on the
- * Ethernet addresses inside ARP packets, so there is no other way to drop
- * spoofed ARPs other than sending every ARP packet to a controller. */
+ * (This action is deprecated in favor of defining flows using the
+ * NXM_NX_ARP_SHA flow match and will likely be removed in a future version
+ * of Open vSwitch.) */
struct nx_action_drop_spoofed_arp {
ovs_be16 type; /* OFPAT_VENDOR. */
ovs_be16 len; /* Length is 16. */
@@ -386,6 +386,8 @@ OFP_ASSERT(sizeof(struct nx_action_pop_queue) == 16);
* - NXM_OF_ARP_SPA
* - NXM_OF_ARP_TPA
* - NXM_NX_TUN_ID
+ * - NXM_NX_ARP_SHA
+ * - NXM_NX_ARP_THA
* - NXM_NX_REG(idx) for idx in the switch's accepted range.
*
* The following nxm_header values are potentially acceptable as 'dst':
@@ -1038,6 +1040,17 @@ enum nx_mp_algorithm {
#define NXM_NX_TUN_ID NXM_HEADER (0x0001, 16, 8)
#define NXM_NX_TUN_ID_W NXM_HEADER_W(0x0001, 16, 8)
+/* For an Ethernet+IP ARP packet, the source or target hardware address
+ * in the ARP header. Always 0 otherwise.
+ *
+ * Prereqs: NXM_OF_ETH_TYPE must match 0x0806 exactly.
+ *
+ * Format: 48-bit Ethernet MAC address.
+ *
+ * Masking: Not maskable. */
+#define NXM_NX_ARP_SHA NXM_HEADER (0x0001, 17, 6)
+#define NXM_NX_ARP_THA NXM_HEADER (0x0001, 18, 6)
+
/* ## --------------------- ## */
/* ## Requests and replies. ## */
/* ## --------------------- ## */
diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h
index 1fb0bf99..fdd225db 100644
--- a/include/openvswitch/datapath-protocol.h
+++ b/include/openvswitch/datapath-protocol.h
@@ -356,6 +356,8 @@ struct odp_key_arp {
ovs_be32 arp_sip;
ovs_be32 arp_tip;
ovs_be16 arp_op;
+ uint8_t arp_sha[6];
+ uint8_t arp_tha[6];
};
/**
diff --git a/lib/classifier.c b/lib/classifier.c
index 53280e50..a4532a61 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -319,6 +319,20 @@ cls_rule_set_icmp_code(struct cls_rule *rule, uint8_t icmp_code)
rule->flow.icmp_code = htons(icmp_code);
}
+void
+cls_rule_set_arp_sha(struct cls_rule *rule, const uint8_t sha[ETH_ADDR_LEN])
+{
+ rule->wc.wildcards &= ~FWW_ARP_SHA;
+ memcpy(rule->flow.arp_sha, sha, ETH_ADDR_LEN);
+}
+
+void
+cls_rule_set_arp_tha(struct cls_rule *rule, const uint8_t tha[ETH_ADDR_LEN])
+{
+ rule->wc.wildcards &= ~FWW_ARP_THA;
+ memcpy(rule->flow.arp_tha, tha, ETH_ADDR_LEN);
+}
+
/* Returns true if 'a' and 'b' have the same priority, wildcard the same
* fields, and have the same values for fixed fields, otherwise false. */
bool
@@ -467,6 +481,16 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
ds_put_format(s, "nw_proto=%"PRIu8",", f->nw_proto);
}
}
+ if (f->dl_type == htons(ETH_TYPE_ARP)) {
+ if (!(w & FWW_ARP_SHA)) {
+ ds_put_format(s, "arp_sha="ETH_ADDR_FMT",",
+ ETH_ADDR_ARGS(f->arp_sha));
+ }
+ if (!(w & FWW_ARP_THA)) {
+ ds_put_format(s, "arp_tha="ETH_ADDR_FMT",",
+ ETH_ADDR_ARGS(f->arp_tha));
+ }
+ }
if (!(w & FWW_NW_TOS)) {
ds_put_format(s, "nw_tos=%"PRIu8",", f->nw_tos);
}
@@ -947,7 +971,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_SIG_SIZE == 40 + FLOW_N_REGS * 4);
+ BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 52 + FLOW_N_REGS * 4);
for (i = 0; i < FLOW_N_REGS; i++) {
if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
@@ -974,7 +998,9 @@ flow_equal_except(const struct flow *a, const struct flow *b,
&& (wc & FWW_ETH_MCAST
|| !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01))
&& (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
- && (wc & FWW_NW_TOS || a->nw_tos == b->nw_tos));
+ && (wc & FWW_NW_TOS || a->nw_tos == b->nw_tos)
+ && (wc & FWW_ARP_SHA || eth_addr_equals(a->arp_sha, b->arp_sha))
+ && (wc & FWW_ARP_THA || eth_addr_equals(a->arp_tha, b->arp_tha)));
}
static void
@@ -983,7 +1009,7 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
const flow_wildcards_t wc = wildcards->wildcards;
int i;
- BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 40 + 4 * FLOW_N_REGS);
+ BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 52 + 4 * FLOW_N_REGS);
for (i = 0; i < FLOW_N_REGS; i++) {
flow->regs[i] &= wildcards->reg_masks[i];
@@ -1020,4 +1046,10 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
if (wc & FWW_NW_TOS) {
flow->nw_tos = 0;
}
+ if (wc & FWW_ARP_SHA) {
+ memset(flow->arp_sha, 0, sizeof flow->arp_sha);
+ }
+ if (wc & FWW_ARP_THA) {
+ memset(flow->arp_tha, 0, sizeof flow->arp_tha);
+ }
}
diff --git a/lib/classifier.h b/lib/classifier.h
index 7b347e7b..40149727 100644
--- a/lib/classifier.h
+++ b/lib/classifier.h
@@ -101,6 +101,8 @@ bool cls_rule_set_nw_dst_masked(struct cls_rule *, ovs_be32 ip, ovs_be32 mask);
void cls_rule_set_nw_tos(struct cls_rule *, uint8_t);
void cls_rule_set_icmp_type(struct cls_rule *, uint8_t);
void cls_rule_set_icmp_code(struct cls_rule *, uint8_t);
+void cls_rule_set_arp_sha(struct cls_rule *, const uint8_t[6]);
+void cls_rule_set_arp_tha(struct cls_rule *, const uint8_t[6]);
bool cls_rule_equal(const struct cls_rule *, const struct cls_rule *);
diff --git a/lib/flow.c b/lib/flow.c
index 7be10e29..ff06318c 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -224,6 +224,8 @@ flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t in_port,
|| (flow->nw_proto == ARP_OP_REPLY)) {
flow->nw_src = arp->ar_spa;
flow->nw_dst = arp->ar_tpa;
+ memcpy(flow->arp_sha, arp->ar_sha, ETH_ADDR_LEN);
+ memcpy(flow->arp_tha, arp->ar_tha, ETH_ADDR_LEN);
}
}
}
@@ -274,17 +276,23 @@ flow_format(struct ds *ds, const struct flow *flow)
" type%04"PRIx16
" proto%"PRIu8
" tos%"PRIu8
- " ip"IP_FMT"->"IP_FMT
- " port%"PRIu16"->%"PRIu16,
+ " ip"IP_FMT"->"IP_FMT,
ETH_ADDR_ARGS(flow->dl_src),
ETH_ADDR_ARGS(flow->dl_dst),
ntohs(flow->dl_type),
flow->nw_proto,
flow->nw_tos,
IP_ARGS(&flow->nw_src),
- IP_ARGS(&flow->nw_dst),
- ntohs(flow->tp_src),
- ntohs(flow->tp_dst));
+ IP_ARGS(&flow->nw_dst));
+ if (flow->tp_src || flow->tp_dst) {
+ ds_put_format(ds, " port%"PRIu16"->%"PRIu16,
+ ntohs(flow->tp_src), ntohs(flow->tp_dst));
+ }
+ if (!eth_addr_is_zero(flow->arp_sha) || !eth_addr_is_zero(flow->arp_tha)) {
+ ds_put_format(ds, " arp_ha"ETH_ADDR_FMT"->"ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(flow->arp_sha),
+ ETH_ADDR_ARGS(flow->arp_tha));
+ }
}
void
diff --git a/lib/flow.h b/lib/flow.h
index 2dd0c4ae..a5b271ee 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -54,14 +54,17 @@ struct flow {
uint8_t dl_dst[6]; /* Ethernet destination address. */
uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */
uint8_t nw_tos; /* IP ToS (DSCP field, 6 bits). */
+ uint8_t arp_sha[6]; /* ARP source hardware address. */
+ uint8_t arp_tha[6]; /* ARP target hardware address. */
+ uint32_t reserved; /* Reserved for 64-bit packing. */
};
/* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct
* flow", followed by FLOW_PAD_SIZE bytes of padding. */
-#define FLOW_SIG_SIZE (40 + FLOW_N_REGS * 4)
-#define FLOW_PAD_SIZE 0
-BUILD_ASSERT_DECL(offsetof(struct flow, nw_tos) == FLOW_SIG_SIZE - 1);
-BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nw_tos) == 1);
+#define FLOW_SIG_SIZE (52 + FLOW_N_REGS * 4)
+#define FLOW_PAD_SIZE 4
+BUILD_ASSERT_DECL(offsetof(struct flow, arp_tha) == FLOW_SIG_SIZE - 6);
+BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->arp_tha) == 6);
BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
int flow_extract(struct ofpbuf *, uint64_t tun_id, uint16_t in_port,
@@ -115,7 +118,9 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t;
/* No corresponding OFPFW_* or OVSFW_* bits. */
#define FWW_ETH_MCAST ((OVS_FORCE flow_wildcards_t) (1 << 8))
/* multicast bit only */
-#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 9)) - 1))
+#define FWW_ARP_SHA ((OVS_FORCE flow_wildcards_t) (1 << 9))
+#define FWW_ARP_THA ((OVS_FORCE flow_wildcards_t) (1 << 10))
+#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 11)) - 1))
/* Information on wildcards for a flow, as a supplement to "struct flow".
*
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 7326b114..d43372b8 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -318,6 +318,13 @@ parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
return 0;
}
+ case NFI_NXM_NX_ARP_SHA:
+ memcpy(flow->arp_sha, value, ETH_ADDR_LEN);
+ return 0;
+ case NFI_NXM_NX_ARP_THA:
+ memcpy(flow->arp_tha, value, ETH_ADDR_LEN);
+ return 0;
+
/* Tunnel ID. */
case NFI_NXM_NX_TUN_ID:
if (wc->tun_id_mask) {
@@ -693,6 +700,12 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
}
nxm_put_32m(b, NXM_OF_ARP_SPA, flow->nw_src, cr->wc.nw_src_mask);
nxm_put_32m(b, NXM_OF_ARP_TPA, flow->nw_dst, cr->wc.nw_dst_mask);
+ if (!(wc & FWW_ARP_SHA)) {
+ nxm_put_eth(b, NXM_NX_ARP_SHA, flow->arp_sha);
+ }
+ if (!(wc & FWW_ARP_THA)) {
+ nxm_put_eth(b, NXM_NX_ARP_THA, flow->arp_tha);
+ }
}
/* Tunnel ID. */
@@ -1162,6 +1175,12 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow)
#error
#endif
+ case NFI_NXM_NX_ARP_SHA:
+ return eth_addr_to_uint64(flow->arp_sha);
+
+ case NFI_NXM_NX_ARP_THA:
+ return eth_addr_to_uint64(flow->arp_tha);
+
case NFI_NXM_NX_TUN_ID_W:
case NFI_NXM_OF_ETH_DST_W:
case NFI_NXM_OF_VLAN_TCI_W:
@@ -1234,6 +1253,8 @@ nxm_write_field(const struct nxm_field *dst, struct flow *flow,
case NFI_NXM_OF_IP_DST_W:
case NFI_NXM_OF_ARP_SPA_W:
case NFI_NXM_OF_ARP_TPA_W:
+ case NFI_NXM_NX_ARP_SHA:
+ case NFI_NXM_NX_ARP_THA:
case N_NXM_FIELDS:
NOT_REACHED();
}
diff --git a/lib/nx-match.def b/lib/nx-match.def
index d3e240ed..966143dc 100644
--- a/lib/nx-match.def
+++ b/lib/nx-match.def
@@ -39,6 +39,8 @@ DEFINE_FIELD (OF_ARP_OP, FWW_NW_PROTO, ETH_TYPE_ARP, 0, false)
DEFINE_FIELD_M(OF_ARP_SPA, 0, ETH_TYPE_ARP, 0, false)
DEFINE_FIELD_M(OF_ARP_TPA, 0, ETH_TYPE_ARP, 0, false)
DEFINE_FIELD_M(NX_TUN_ID, 0, 0, 0, true)
+DEFINE_FIELD (NX_ARP_SHA, FWW_ARP_SHA, ETH_TYPE_ARP, 0, false)
+DEFINE_FIELD (NX_ARP_THA, FWW_ARP_THA, ETH_TYPE_ARP, 0, false)
DEFINE_FIELD_M(NX_REG0, 0, 0, 0, true)
#if FLOW_N_REGS >= 2
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 5f1a77ce..bacb1c04 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -307,9 +307,11 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
case ODP_KEY_ATTR_ARP:
arp_key = nl_attr_get(a);
- ds_put_format(ds, "arp(sip="IP_FMT",tip="IP_FMT",op=%"PRIu16")",
+ ds_put_format(ds, "arp(sip="IP_FMT",tip="IP_FMT",op=%"PRIu16","
+ "sha="ETH_ADDR_FMT",tha="ETH_ADDR_FMT")",
IP_ARGS(&arp_key->arp_sip), IP_ARGS(&arp_key->arp_tip),
- ntohs(arp_key->arp_op));
+ ntohs(arp_key->arp_op), ETH_ADDR_ARGS(arp_key->arp_sha),
+ ETH_ADDR_ARGS(arp_key->arp_tha));
break;
default:
@@ -416,6 +418,8 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
arp_key->arp_sip = flow->nw_src;
arp_key->arp_tip = flow->nw_dst;
arp_key->arp_op = htons(flow->nw_proto);
+ memcpy(arp_key->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
+ memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
}
}
@@ -541,6 +545,8 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
return EINVAL;
}
flow->nw_proto = ntohs(arp_key->arp_op);
+ memcpy(flow->arp_sha, arp_key->arp_sha, ETH_ADDR_LEN);
+ memcpy(flow->arp_tha, arp_key->arp_tha, ETH_ADDR_LEN);
break;
default:
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 65d7a654..366664e3 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -491,7 +491,9 @@ parse_protocol(const char *name, const struct protocol **p_out)
FIELD(F_TP_SRC, "tp_src", FWW_TP_SRC) \
FIELD(F_TP_DST, "tp_dst", FWW_TP_DST) \
FIELD(F_ICMP_TYPE, "icmp_type", FWW_TP_SRC) \
- FIELD(F_ICMP_CODE, "icmp_code", FWW_TP_DST)
+ FIELD(F_ICMP_CODE, "icmp_code", FWW_TP_DST) \
+ FIELD(F_ARP_SHA, "arp_sha", FWW_ARP_SHA) \
+ FIELD(F_ARP_THA, "arp_tha", FWW_ARP_THA)
enum field_index {
#define FIELD(ENUM, NAME, WILDCARD) ENUM,
@@ -607,6 +609,16 @@ parse_field_value(struct cls_rule *rule, enum field_index index,
cls_rule_set_icmp_code(rule, str_to_u32(value));
break;
+ case F_ARP_SHA:
+ str_to_mac(value, mac);
+ cls_rule_set_arp_sha(rule, mac);
+ break;
+
+ case F_ARP_THA:
+ str_to_mac(value, mac);
+ cls_rule_set_arp_tha(rule, mac);
+ break;
+
case N_FIELDS:
NOT_REACHED();
}
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 925f45fd..59a5fc43 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -124,6 +124,10 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
/* Initialize most of rule->wc. */
flow_wildcards_init_catchall(wc);
wc->wildcards = ofpfw & WC_INVARIANTS;
+
+ /* Wildcard fields that aren't defined by ofp_match or tun_id. */
+ wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA);
+
if (ofpfw & OFPFW_NW_TOS) {
wc->wildcards |= FWW_NW_TOS;
}
@@ -859,6 +863,11 @@ is_nxm_required(const struct cls_rule *rule, bool cookie_support,
return true;
}
+ /* Only NXM supports matching ARP hardware addresses. */
+ if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) {
+ return true;
+ }
+
/* Only NXM supports matching registers. */
if (!regs_fully_wildcarded(wc)) {
return true;
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 0648f067..7eecf287 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -61,6 +61,7 @@ AT_DATA([flows.txt], [
tcp,tp_src=123,actions=flood
in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop
arp,nw_src=192.168.0.1 actions=drop_spoofed_arp,NORMAL
+arp,dl_src=00:0A:E4:25:6B:B0,arp_sha=00:0A:E4:25:6B:B0 actions=drop
udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0
tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
@@ -75,6 +76,7 @@ AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [dnl
NXT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
NXT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
NXT_FLOW_MOD: ADD arp,nw_src=192.168.0.1 actions=drop_spoofed_arp,NORMAL
+NXT_FLOW_MOD: ADD arp,dl_src=00:0a:e4:25:6b:b0,arp_sha=00:0a:e4:25:6b:b0 actions=drop
NXT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
NXT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
NXT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
@@ -92,6 +94,7 @@ AT_DATA([flows.txt], [[
tcp,tp_src=123,actions=flood
in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop
arp,nw_src=192.168.0.1 actions=drop_spoofed_arp,NORMAL
+arp,dl_src=00:0A:E4:25:6B:B0,arp_sha=00:0A:E4:25:6B:B0 actions=drop
udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0
tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
@@ -106,6 +109,7 @@ AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
[[NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(007b) actions=FLOOD
NXT_FLOW_MOD: ADD NXM_OF_IN_PORT(fffe), NXM_OF_ETH_SRC(000ae4256bb0), NXM_OF_VLAN_TCI_W(1009/1fff) actions=drop
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(c0a80001) actions=drop_spoofed_arp,NORMAL
+NXT_FLOW_MOD: ADD NXM_OF_ETH_SRC(000ae4256bb0), NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_SHA(000ae4256bb0) actions=drop
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_VLAN_TCI_W(f000/f000), NXM_OF_IP_PROTO(11) idle:5 actions=strip_vlan,output:0
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(0050) actions=set_queue:37,output:1
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(0035) actions=pop_queue,output:1
@@ -205,18 +209,28 @@ NXM_OF_ETH_TYPE(0000) NXM_OF_ARP_OP(0001)
NXM_OF_ARP_OP(0001)
NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_OP(0001) NXM_OF_ARP_OP(0001)
-# ARP source
+# ARP source protocol address
NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA(ac100014)
NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/FFFFFF00)
NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_SPA(ac100014)
NXM_OF_ARP_SPA_W(C0D8fedc/FFFF0000)
-# ARP destination
+# ARP destination protocol address
NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA(ac100014)
NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a812fe/FFFFFF00)
NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_TPA(ac100014)
NXM_OF_ARP_TPA_W(C0D80000/FFFF0000)
+# ARP source hardware address
+NXM_OF_ETH_TYPE(0806) NXM_NX_ARP_SHA(0002e30f80a4)
+NXM_OF_ETH_TYPE(0800) NXM_NX_ARP_SHA(0002e30f80a4)
+NXM_NX_ARP_SHA(0002e30f80a4)
+
+# ARP destination hardware address
+NXM_OF_ETH_TYPE(0806) NXM_NX_ARP_THA(0002e30f80a4)
+NXM_OF_ETH_TYPE(0800) NXM_NX_ARP_THA(0002e30f80a4)
+NXM_NX_ARP_THA(0002e30f80a4)
+
# Tunnel ID.
NXM_NX_TUN_ID(00000000abcdef01)
NXM_NX_TUN_ID_W(84200000abcdef01/84200000FFFFFFFF)
@@ -323,18 +337,28 @@ nx_pull_match() returned error 44010104
nx_pull_match() returned error 44010104
nx_pull_match() returned error 44010105
-# ARP source
+# ARP source protocol address
NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(ac100014)
NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA_W(c0a81200/ffffff00)
nx_pull_match() returned error 44010104
nx_pull_match() returned error 44010104
-# ARP destination
+# ARP destination protocol address
NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA(ac100014)
NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA_W(c0a81200/ffffff00)
nx_pull_match() returned error 44010104
nx_pull_match() returned error 44010104
+# ARP source hardware address
+NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_SHA(0002e30f80a4)
+nx_pull_match() returned error 44010104
+nx_pull_match() returned error 44010104
+
+# ARP destination hardware address
+NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_THA(0002e30f80a4)
+nx_pull_match() returned error 44010104
+nx_pull_match() returned error 44010104
+
# Tunnel ID.
NXM_NX_TUN_ID(00000000abcdef01)
NXM_NX_TUN_ID_W(84200000abcdef01/84200000ffffffff)
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index d83a50be..135e705c 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -345,6 +345,12 @@ Extended Match) extension to OpenFlow. When one of these is specified,
extension. If the switch does not support NXM, then \fBovs\-ofctl\fR
will report a fatal error.
.
+.IP \fBarp_sha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
+.IQ \fBarp_tha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
+When \fBdl_type\fR specifies ARP, \fBarp_sha\fR and \fBarp_tha\fR match
+the source and target hardware address, respectively. An address is
+specified as 6 pairs of hexadecimal digits delimited by colons.
+.
.IP \fBtun_id=\fItunnel-id\fR[\fB/\fImask\fR]
Matches tunnel identifier \fItunnel-id\fR. Only packets that arrive
over a tunnel that carries a key (e.g. GRE with the RFC 2890 key
@@ -506,10 +512,10 @@ Stops processing further actions, if the packet being processed is an
Ethernet+IPv4 ARP packet for which the source Ethernet address inside
the ARP packet differs from the source Ethernet address in the
Ethernet header.
-.
-This is useful because OpenFlow does not provide a way to match on the
-Ethernet addresses inside ARP packets, so there is no other way to
-drop spoofed ARPs other than sending every ARP packet to a controller.
+.IP
+This action is deprecated in favor of defining flows using the
+\fBarp_sha\fR match field described earlier and will likely be removed
+in a future version of Open vSwitch.
.
.IP \fBset_queue\fB:\fIqueue\fR
Sets the queue that should be used to \fIqueue\fR when packets are