datapath: Fix build with backported dev_get_stats().
[sliver-openvswitch.git] / datapath / flow.c
index b24c242..548c729 100644 (file)
@@ -1,30 +1,59 @@
 /*
  * Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008, 2009 Nicira Networks.
+ * Copyright (c) 2007, 2008, 2009, 2010 Nicira Networks.
+ *
+ * Significant portions of this file may be copied from parts of the Linux
+ * kernel, by Linus Torvalds and others.
  */
 
 #include "flow.h"
+#include "datapath.h"
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
 #include <net/llc_pdu.h>
 #include <linux/kernel.h>
+#include <linux/jhash.h>
 #include <linux/jiffies.h>
 #include <linux/llc.h>
 #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>
 #include <linux/udp.h>
 #include <linux/icmp.h>
+#include <net/inet_ecn.h>
 #include <net/ip.h>
 
 #include "compat.h"
 
 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 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)
 {
@@ -70,7 +99,6 @@ static inline struct ovs_tcphdr *ovs_tcp_hdr(const struct sk_buff *skb)
 
 void flow_used(struct sw_flow *flow, struct sk_buff *skb)
 {
-       unsigned long flags;
        u8 tcp_flags = 0;
 
        if (flow->key.dl_type == htons(ETH_P_IP) && iphdr_ok(skb)) {
@@ -82,12 +110,12 @@ void flow_used(struct sw_flow *flow, struct sk_buff *skb)
                }
        }
 
-       spin_lock_irqsave(&flow->lock, flags);
+       spin_lock_bh(&flow->lock);
        getnstimeofday(&flow->used);
        flow->packet_count++;
        flow->byte_count += skb->len;
        flow->tcp_flags |= tcp_flags;
-       spin_unlock_irqrestore(&flow->lock, flags);
+       spin_unlock_bh(&flow->lock);
 }
 
 struct sw_flow_actions *flow_actions_alloc(size_t n_actions)
@@ -108,7 +136,7 @@ struct sw_flow_actions *flow_actions_alloc(size_t n_actions)
 
 
 /* Frees 'flow' immediately. */
-void flow_free(struct sw_flow *flow)
+static void flow_free(struct sw_flow *flow)
 {
        if (unlikely(!flow))
                return;
@@ -116,6 +144,12 @@ void flow_free(struct sw_flow *flow)
        kmem_cache_free(flow_cache, flow);
 }
 
+void flow_free_tbl(struct tbl_node *node)
+{
+       struct sw_flow *flow = flow_cast(node);
+       flow_free(flow);
+}
+
 /* RCU callback used by flow_deferred_free. */
 static void rcu_free_flow_callback(struct rcu_head *rcu)
 {
@@ -175,8 +209,9 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct odp_flow_key *key)
        int nh_ofs;
 
        memset(key, 0, sizeof *key);
-       key->dl_vlan = htons(ODP_VLAN_NONE);
+       key->tun_id = OVS_CB(skb)->tun_id;
        key->in_port = in_port;
+       key->dl_vlan = htons(ODP_VLAN_NONE);
 
        if (skb->len < sizeof *eth)
                return 0;
@@ -206,6 +241,7 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct odp_flow_key *key)
                struct vlan_hdr *vh = (struct vlan_hdr*)(skb->data + nh_ofs);
                key->dl_type = vh->h_vlan_encapsulated_proto;
                key->dl_vlan = vh->h_vlan_TCI & htons(VLAN_VID_MASK);
+               key->dl_vlan_pcp = (ntohs(vh->h_vlan_TCI) & VLAN_PCP_MASK) >> VLAN_PCP_SHIFT;
                nh_ofs += sizeof(struct vlan_hdr);
        }
        memcpy(key->dl_src, eth->h_source, ETH_ALEN);
@@ -218,6 +254,7 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct odp_flow_key *key)
                int th_ofs = nh_ofs + nh->ihl * 4;
                key->nw_src = nh->saddr;
                key->nw_dst = nh->daddr;
+               key->nw_tos = nh->tos & ~INET_ECN_MASK;
                key->nw_proto = nh->protocol;
                skb_set_transport_header(skb, th_ofs);
 
@@ -263,12 +300,51 @@ 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);
+
+               if (arp->ar_hrd == htons(ARPHRD_ETHER)
+                               && arp->ar_pro == htons(ETH_P_IP)
+                               && arp->ar_hln == ETH_ALEN
+                               && arp->ar_pln == 4) {
+
+                       /* 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) {
+                               memcpy(&key->nw_src, arp->ar_sip, sizeof(key->nw_src));
+                               memcpy(&key->nw_dst, arp->ar_tip, sizeof(key->nw_dst));
+                       }
+               }
        } else {
                skb_reset_transport_header(skb);
        }
        return retval;
 }
 
+struct sw_flow *flow_cast(const struct tbl_node *node)
+{
+       return container_of(node, struct sw_flow, tbl_node);
+}
+
+u32 flow_hash(const struct odp_flow_key *key)
+{
+       return jhash2((u32*)key, sizeof *key / sizeof(u32), hash_seed);
+}
+
+int flow_cmp(const struct tbl_node *node, void *key2_)
+{
+       const struct odp_flow_key *key1 = &flow_cast(node)->key;
+       const struct odp_flow_key *key2 = key2_;
+
+       return !memcmp(key1, key2, sizeof(struct odp_flow_key));
+}
+
 /* Initializes the flow module.
  * Returns zero if successful or a negative error code. */
 int flow_init(void)
@@ -278,6 +354,8 @@ int flow_init(void)
        if (flow_cache == NULL)
                return -ENOMEM;
 
+       get_random_bytes(&hash_seed, sizeof hash_seed);
+
        return 0;
 }
 
@@ -286,16 +364,3 @@ void flow_exit(void)
 {
        kmem_cache_destroy(flow_cache);
 }
-
-void print_flow(const struct odp_flow_key *key)
-{
-#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
-#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5]
-    printk("port%04x:vlan%d mac"MAC_FMT"->"MAC_FMT" "
-          "type%04x proto%d ip%x->%x port%d->%d\n",
-          key->in_port, ntohs(key->dl_vlan),
-          MAC_ARG(key->dl_src), MAC_ARG(key->dl_dst),
-          ntohs(key->dl_type), key->nw_proto,
-          key->nw_src, key->nw_dst,
-          ntohs(key->tp_src), ntohs(key->tp_dst));
-}