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