aboutsummaryrefslogtreecommitdiff
path: root/datapath
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2010-08-24 16:00:27 -0700
committerBen Pfaff <blp@nicira.com>2010-08-26 10:56:20 -0700
commit401eeb92d32ac0fa07f34f5b803d67b8032b6403 (patch)
treed3c36f17662b43a6f4ed65e938b12ab3823ed2a5 /datapath
parente5ae7df8c7f44cb2d6f42daaab1d3c26c7a88ae3 (diff)
Add Nicira extension to OpenFlow for dropping spoofed ARP packets.
"ARP spoofing" is when a host claims an incorrect association between an IP address and a MAC address for deceptive purposes. OpenFlow by itself can prevent a host from sending out ARP replies from an incorrect MAC address in the Ethernet L2 header, but it cannot control the MAC addresses inside the ARP L3 packet. This commit adds a new action that can be used to drop these spoofed packets. CC: Paul Ingram <paul@nicira.com> Signed-off-by: Ben Pfaff <blp@nicira.com>
Diffstat (limited to 'datapath')
-rw-r--r--datapath/actions.c36
-rw-r--r--datapath/flow.c15
-rw-r--r--datapath/flow.h16
3 files changed, 52 insertions, 15 deletions
diff --git a/datapath/actions.c b/datapath/actions.c
index a6771b6c..943d7570 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -14,6 +14,7 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/in6.h>
+#include <linux/if_arp.h>
#include <linux/if_vlan.h>
#include <net/inet_ecn.h>
#include <net/ip.h>
@@ -318,6 +319,35 @@ static struct sk_buff *set_tp_port(struct sk_buff *skb,
return skb;
}
+/**
+ * is_spoofed_arp - check for invalid ARP packet
+ *
+ * @skb: skbuff containing an Ethernet packet, with network header pointing
+ * just past the Ethernet and optional 802.1Q header.
+ * @key: flow key extracted from @skb by flow_extract()
+ *
+ * Returns true if @skb is an invalid Ethernet+IPv4 ARP packet: one with screwy
+ * or truncated header fields or one whose inner and outer Ethernet address
+ * differ.
+ */
+static bool is_spoofed_arp(struct sk_buff *skb, const struct odp_flow_key *key)
+{
+ struct arp_eth_header *arp;
+
+ if (key->dl_type != htons(ETH_P_ARP))
+ return false;
+
+ if (skb_network_offset(skb) + sizeof(struct arp_eth_header) > skb->len)
+ return true;
+
+ arp = (struct arp_eth_header *)skb_network_header(skb);
+ return (arp->ar_hrd != htons(ARPHRD_ETHER) ||
+ arp->ar_pro != htons(ETH_P_IP) ||
+ arp->ar_hln != ETH_ALEN ||
+ arp->ar_pln != 4 ||
+ compare_ether_addr(arp->ar_sha, eth_hdr(skb)->h_source));
+}
+
static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
{
struct dp_port *p;
@@ -484,10 +514,16 @@ int execute_actions(struct datapath *dp, struct sk_buff *skb,
case ODPAT_POP_PRIORITY:
skb->priority = priority;
break;
+
+ case ODPAT_DROP_SPOOFED_ARP:
+ if (unlikely(is_spoofed_arp(skb, key)))
+ goto exit;
+ break;
}
if (!skb)
return -ENOMEM;
}
+exit:
if (prev_port != -1)
do_output(dp, skb, prev_port);
else
diff --git a/datapath/flow.c b/datapath/flow.c
index 5e362e30..c37c8e0f 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -34,21 +34,6 @@
struct kmem_cache *flow_cache;
static unsigned int hash_seed;
-struct arp_eth_header
-{
- __be16 ar_hrd; /* format of hardware address */
- __be16 ar_pro; /* format of protocol address */
- unsigned char ar_hln; /* length of hardware address */
- unsigned char ar_pln; /* length of protocol address */
- __be16 ar_op; /* ARP opcode (command) */
-
- /* Ethernet+IPv4 specific members. */
- unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
- unsigned char ar_sip[4]; /* sender IP address */
- unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
- unsigned char ar_tip[4]; /* target IP address */
-} __attribute__((packed));
-
static inline bool arphdr_ok(struct sk_buff *skb)
{
int nh_ofs = skb_network_offset(skb);
diff --git a/datapath/flow.h b/datapath/flow.h
index 534c7af2..80a5b66b 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/rcupdate.h>
#include <linux/gfp.h>
+#include <linux/if_ether.h>
#include <linux/jiffies.h>
#include <linux/time.h>
@@ -42,6 +43,21 @@ struct sw_flow {
u8 tcp_flags; /* Union of seen TCP flags. */
};
+struct arp_eth_header
+{
+ __be16 ar_hrd; /* format of hardware address */
+ __be16 ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ __be16 ar_op; /* ARP opcode (command) */
+
+ /* Ethernet+IPv4 specific members. */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ unsigned char ar_sip[4]; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ unsigned char ar_tip[4]; /* target IP address */
+} __attribute__((packed));
+
extern struct kmem_cache *flow_cache;
struct sw_flow_actions *flow_actions_alloc(size_t n_actions);