Allow repeated extraction of flow from packets

Split the L3 and above portion of ovs_flow_extract() out into
ovs_flow_extract_l3_onwards() and call ovs_flow_extract_l3_onwards()
from ovs_flow_extract().

This is to allow re-extraction of L3 and higher information using
an ethernet type supplied by actions, for example the mpls_pop.

Signed-off-by: Simon Horman <ho...@verge.net.au>

---

v2.17
* Rebase

v2.14 - v2.16
* No change

v2.13
* As suggested by Jarno Rajahalme
  - Update change log to more closely reflect the extent of the
    changes in the patch

v2.12
* Rebase

v2.11
* First post
---
 datapath/flow.c |  156 ++++++++++++++++++++++++++++++++++---------------------
 datapath/flow.h |    2 +
 2 files changed, 100 insertions(+), 58 deletions(-)

diff --git a/datapath/flow.c b/datapath/flow.c
index 27e1920..75f85b4 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -586,7 +586,8 @@ out:
 }
 
 /**
- * ovs_flow_extract - extracts a flow key from an Ethernet frame.
+ * ovs_flow_extract_l3_onwards - extracts l3 and l4 portion of a flow key
+ * from an Ethernet frame.
  * @skb: sk_buff that contains the frame, with skb->data pointing to the
  * Ethernet header
  * @in_port: port number on which @skb was received.
@@ -594,62 +595,27 @@ out:
  * @key_lenp: length of output flow key
  *
  * The caller must ensure that skb->len >= ETH_HLEN.
+ * The caller must ensure that the rest of the flow is initialised.
+ * This, ovs_flow_extract_l3_onwards() should be called by or after
+ * vs_flow_extract().
  *
  * Returns 0 if successful, otherwise a negative errno value.
  *
  * Initializes @skb header pointers as follows:
  *
- *    - skb->mac_header: the Ethernet header.
- *
- *    - skb->network_header: just past the Ethernet header, or just past the
- *      VLAN header, to the first byte of the Ethernet payload.
- *
- *    - skb->transport_header: If key->dl_type is ETH_P_IP or ETH_P_IPV6
+ *    - skb->transport_header: If eth_type is ETH_P_IP or ETH_P_IPV6
  *      on output, then just past the IP header, if one is present and
  *      of a correct length, otherwise the same as skb->network_header.
  *      For other key->dl_type values it is left untouched.
  */
-int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
-                int *key_lenp)
+int ovs_flow_extract_l3_onwards(struct sk_buff *skb, struct sw_flow_key *key,
+                               int *key_lenp, __be16 eth_type)
 {
        int error = 0;
-       int key_len = SW_FLOW_KEY_OFFSET(eth);
-       struct ethhdr *eth;
-
-       memset(key, 0, sizeof(*key));
-
-       key->phy.priority = skb->priority;
-       if (OVS_CB(skb)->tun_key)
-               memcpy(&key->tun_key, OVS_CB(skb)->tun_key, 
sizeof(key->tun_key));
-       key->phy.in_port = in_port;
-       key->phy.skb_mark = skb_get_mark(skb);
-
-       skb_reset_mac_header(skb);
-
-       /* Link layer.  We are guaranteed to have at least the 14 byte Ethernet
-        * header in the linear data area.
-        */
-       eth = eth_hdr(skb);
-       memcpy(key->eth.src, eth->h_source, ETH_ALEN);
-       memcpy(key->eth.dst, eth->h_dest, ETH_ALEN);
-
-       __skb_pull(skb, 2 * ETH_ALEN);
-
-       if (vlan_tx_tag_present(skb))
-               key->eth.tci = htons(vlan_get_tci(skb));
-       else if (eth->h_proto == htons(ETH_P_8021Q))
-               if (unlikely(parse_vlan(skb, key)))
-                       return -ENOMEM;
-
-       key->eth.type = parse_ethertype(skb);
-       if (unlikely(key->eth.type == htons(0)))
-               return -ENOMEM;
-
-       skb_reset_network_header(skb);
-       __skb_push(skb, skb->data - skb_mac_header(skb));
+       int key_len = *key_lenp;
 
        /* Network layer. */
-       if (key->eth.type == htons(ETH_P_IP)) {
+       if (eth_type == htons(ETH_P_IP)) {
                struct iphdr *nh;
                __be16 offset;
 
@@ -708,8 +674,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, 
struct sw_flow_key *key,
                        }
                }
 
-       } else if ((key->eth.type == htons(ETH_P_ARP) ||
-                  key->eth.type == htons(ETH_P_RARP)) && arphdr_ok(skb)) {
+       } else if ((eth_type == htons(ETH_P_ARP) ||
+                   eth_type == htons(ETH_P_RARP)) && arphdr_ok(skb)) {
                struct arp_eth_header *arp;
 
                arp = (struct arp_eth_header *)skb_network_header(skb);
@@ -728,18 +694,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, 
struct sw_flow_key *key,
                        memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN);
                        key_len = SW_FLOW_KEY_OFFSET(ipv4.arp);
                }
-       } else if (eth_p_mpls(key->eth.type)) {
-               error = check_header(skb, MPLS_HLEN);
-               if (unlikely(error))
-                       goto out;
-
-               key_len = SW_FLOW_KEY_OFFSET(mpls.top_lse);
-               memcpy(&key->mpls.top_lse, skb_network_header(skb), MPLS_HLEN);
-
-               /* Update network header */
-               skb_set_network_header(skb, skb_network_header(skb) -
-                                      skb->data + MPLS_HLEN);
-       } else if (key->eth.type == htons(ETH_P_IPV6)) {
+       } else if (eth_type == htons(ETH_P_IPV6)) {
                int nh_len;             /* IPv6 Header + Extensions */
 
                nh_len = parse_ipv6hdr(skb, key, &key_len);
@@ -786,6 +741,91 @@ out:
        return error;
 }
 
+/**
+ * ovs_flow_extract - extracts a flow key from an Ethernet frame.
+ * @skb: sk_buff that contains the frame, with skb->data pointing to the
+ * Ethernet header
+ * @in_port: port number on which @skb was received.
+ * @key: output flow key
+ * @key_lenp: length of output flow key
+ *
+ * The caller must ensure that skb->len >= ETH_HLEN.
+ *
+ * Returns 0 if successful, otherwise a negative errno value.
+ *
+ * Initializes @skb header pointers as follows:
+ *
+ *    - skb->mac_header: the Ethernet header.
+ *
+ *    - skb->network_header: just past the Ethernet header, or just past the
+ *      VLAN header, to the first byte of the Ethernet payload.
+ *
+ *    - skb->transport_header: If key->dl_type is ETH_P_IP or ETH_P_IPV6
+ *      on output, then just past the IP header, if one is present and
+ *      of a correct length, otherwise the same as skb->network_header.
+ *      For other key->dl_type values it is left untouched.
+ */
+int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
+                    int *key_lenp)
+{
+       int error = 0;
+       int key_len = SW_FLOW_KEY_OFFSET(eth);
+       struct ethhdr *eth;
+
+       memset(key, 0, sizeof(*key));
+
+       key->phy.priority = skb->priority;
+       if (OVS_CB(skb)->tun_key)
+               memcpy(&key->tun_key, OVS_CB(skb)->tun_key, 
sizeof(key->tun_key));
+       key->phy.in_port = in_port;
+       key->phy.skb_mark = skb_get_mark(skb);
+
+       skb_reset_mac_header(skb);
+
+       /* Link layer.  We are guaranteed to have at least the 14 byte Ethernet
+        * header in the linear data area.
+        */
+       eth = eth_hdr(skb);
+       memcpy(key->eth.src, eth->h_source, ETH_ALEN);
+       memcpy(key->eth.dst, eth->h_dest, ETH_ALEN);
+
+       __skb_pull(skb, 2 * ETH_ALEN);
+
+       if (vlan_tx_tag_present(skb))
+               key->eth.tci = htons(vlan_get_tci(skb));
+       else if (eth->h_proto == htons(ETH_P_8021Q))
+               if (unlikely(parse_vlan(skb, key)))
+                       return -ENOMEM;
+
+       key->eth.type = parse_ethertype(skb);
+       if (unlikely(key->eth.type == htons(0)))
+               return -ENOMEM;
+
+       skb_reset_network_header(skb);
+       __skb_push(skb, skb->data - skb_mac_header(skb));
+
+       /* MPLS */
+       if (eth_p_mpls(key->eth.type)) {
+               error = check_header(skb, MPLS_HLEN);
+               if (unlikely(error))
+                       goto err;
+
+               key_len = SW_FLOW_KEY_OFFSET(mpls.top_lse);
+               memcpy(&key->mpls.top_lse, skb_network_header(skb), MPLS_HLEN);
+
+               /* Update transport header */
+               skb_set_network_header(skb, skb_network_header(skb) -
+                                      skb->data + MPLS_HLEN);
+       }
+
+       *key_lenp = key_len;
+       return ovs_flow_extract_l3_onwards(skb, key, key_lenp,
+                                          key->eth.type);
+
+err:
+       return error;
+}
+
 static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, int 
key_len)
 {
        return jhash2((u32 *)((u8 *)key + key_start),
diff --git a/datapath/flow.h b/datapath/flow.h
index b7f21e2..3969d7c 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -155,6 +155,8 @@ void ovs_flow_free(struct sw_flow *);
 struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len);
 void ovs_flow_deferred_free_acts(struct sw_flow_actions *);
 
+int ovs_flow_extract_l3_onwards(struct sk_buff *, struct sw_flow_key *,
+                               int *key_lenp, __be16 eth_type);
 int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
                     int *key_lenp);
 void ovs_flow_used(struct sw_flow *, struct sk_buff *);
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to