[ovs-discuss] [PATCH 7/9] Add ability for the datapath to match IP address in ARPs

Justin Pettit jpettit at nicira.com
Wed Jul 29 22:21:07 UTC 2009


The ability to match the IP addresses in ARP packets allows for fine-grained
control of ARP processing.  Some forthcoming changes to allow in-band
control to operate over L3 requires this support if we don't want to
allow overly broad rules regarding ARPs to always be white-listed.
Unfortunately, OpenFlow does not support this sort of processing yet, so
we must treat OpenFlow ARP rules as having wildcarded those L3 fields.
---
 datapath/flow.c                         |   37 ++++++++++++++++++++++++++++++
 include/openvswitch/datapath-protocol.h |    3 +-
 lib/flow.c                              |   38 +++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+), 1 deletions(-)

diff --git a/datapath/flow.c b/datapath/flow.c
index 2ac79e7..001fb93 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/in.h>
 #include <linux/rcupdate.h>
+#include <linux/if_arp.h>
 #include <linux/if_ether.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
@@ -29,6 +30,27 @@
 
 struct kmem_cache *flow_cache;
 
+struct arp_eth_header {
+	/* Generic members. */
+	uint16_t ar_hrd;           /* Hardware type. */
+	uint16_t ar_pro;           /* Protocol type. */
+	uint8_t ar_hln;            /* Hardware address length. */
+	uint8_t ar_pln;            /* Protocol address length. */
+	uint16_t ar_op;            /* Opcode. */
+
+	/* Ethernet+IPv4 specific members. */
+	uint8_t ar_sha[ETH_ALEN];  /* Sender hardware address. */
+	uint32_t ar_sip;           /* Sender protocol address. */
+	uint8_t ar_tha[ETH_ALEN];  /* Target hardware address. */
+	uint32_t ar_tip;           /* Target protocol address. */
+} __attribute__((packed));
+
+static inline int arphdr_ok(struct sk_buff *skb)
+{
+	int nh_ofs = skb_network_offset(skb);
+	return pskb_may_pull(skb, nh_ofs + sizeof(struct arp_eth_header));
+}
+
 static inline int iphdr_ok(struct sk_buff *skb)
 {
 	int nh_ofs = skb_network_offset(skb);
@@ -266,6 +288,21 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct odp_flow_key *key)
 		} else {
 			retval = 1;
 		}
+	} else if (key->dl_type == htons(ETH_P_ARP) && arphdr_ok(skb)) {
+		struct arp_eth_header *arp;
+
+		arp = (struct arp_eth_header *)skb_network_header(skb);
+
+		/* We only match on the lower 8 bits of the opcode. */
+		if (ntohs(arp->ar_op) <= 0xff) {
+			key->nw_proto = ntohs(arp->ar_op);
+		}
+
+		if ((key->nw_proto == ARPOP_REQUEST) 
+				|| (key->nw_proto == ARPOP_REPLY)) {
+			key->nw_src = arp->ar_sip;
+			key->nw_dst = arp->ar_tip;
+		}
 	} else {
 		skb_reset_transport_header(skb);
 	}
diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h
index 951664a..bbc29f6 100644
--- a/include/openvswitch/datapath-protocol.h
+++ b/include/openvswitch/datapath-protocol.h
@@ -162,7 +162,8 @@ struct odp_flow_key {
     __be16 tp_dst;               /* TCP/UDP destination port. */
     __u8   dl_src[ETH_ALEN];     /* Ethernet source address. */
     __u8   dl_dst[ETH_ALEN];     /* Ethernet destination address. */
-    __u8   nw_proto;             /* IP protocol. */
+    __u8   nw_proto;             /* IP protocol or lower 8 bits of 
+                                    ARP opcode. */
     __u8   reserved;             /* Pad to 64 bits. */
 };
 
diff --git a/lib/flow.c b/lib/flow.c
index 1801d4d..e705434 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -31,6 +31,15 @@
 #include "vlog.h"
 #define THIS_MODULE VLM_flow
 
+static struct arp_eth_header *
+pull_arp(struct ofpbuf *packet)
+{
+    if (packet->size >= ARP_ETH_HEADER_LEN) {
+        return ofpbuf_pull(packet, ARP_ETH_HEADER_LEN);
+    }
+    return NULL;
+}
+
 static struct ip_header *
 pull_ip(struct ofpbuf *packet)
 {
@@ -185,6 +194,20 @@ flow_extract(struct ofpbuf *packet, uint16_t in_port, flow_t *flow)
                     retval = 1;
                 }
             }
+        } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+            const struct arp_eth_header *arp = pull_arp(&b);
+            if (arp) {
+                /* We only match on the lower 8 bits of the opcode. */
+                if (ntohs(arp->ar_op) <= 0xff) {
+                    flow->nw_proto = ntohs(arp->ar_op);
+                }
+
+                if ((flow->nw_proto == ARP_OP_REQUEST) 
+                        || (flow->nw_proto == ARP_OP_REPLY)) {
+                    flow->nw_src = arp->ar_spa;
+                    flow->nw_dst = arp->ar_tpa;
+                }
+            }
         }
     }
     return retval;
@@ -215,7 +238,15 @@ flow_extract_stats(const flow_t *flow, struct ofpbuf *packet,
 void
 flow_to_match(const flow_t *flow, uint32_t wildcards, struct ofp_match *match)
 {
+    /* The datapath supports matching on an ARP's opcode and IP addresses, 
+     * but OpenFlow does not.  We need to clear the appropriate wildcard 
+     * bits so that OpenFlow is unaware of our trickery. */
+    if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+        wildcards = ~(OFPFW_NW_PROTO | OFPFW_NW_SRC_ALL | OFPFW_NW_DST_ALL) 
+                    & wildcards;
+    }
     match->wildcards = htonl(wildcards);
+
     match->in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL
                            : flow->in_port);
     match->dl_vlan = flow->dl_vlan;
@@ -237,6 +268,13 @@ flow_from_match(flow_t *flow, uint32_t *wildcards,
     if (wildcards) {
         *wildcards = ntohl(match->wildcards);
     }
+    /* The datapath supports matching on an ARP's opcode and IP addresses, 
+     * but OpenFlow does not.  We need to set the appropriate wildcard 
+     * bits so that OpenFlow's wishes are obeyed. */
+    if (match->dl_type == htons(ETH_TYPE_ARP)) {
+        *wildcards |= (OFPFW_NW_PROTO | OFPFW_NW_SRC_ALL | OFPFW_NW_DST_ALL);
+    }
+
     flow->nw_src = match->nw_src;
     flow->nw_dst = match->nw_dst;
     flow->in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL
-- 
1.6.3.1





More information about the discuss mailing list