|| 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);
}
}
}
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:
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;
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 {
* 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. */
* - 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':
#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. ## */
/* ## --------------------- ## */
ovs_be32 arp_sip;
ovs_be32 arp_tip;
ovs_be16 arp_op;
+ uint8_t arp_sha[6];
+ uint8_t arp_tha[6];
};
/**
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
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);
}
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]) {
&& (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
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];
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);
+ }
}
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 *);
|| (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);
}
}
}
" 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
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,
/* 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".
*
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) {
}
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. */
#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:
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();
}
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
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:
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);
}
}
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:
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,
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();
}
/* 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;
}
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;
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
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
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
[[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
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)
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)
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
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