Allow push and pop mpls actions to act on layer 3 packets by teaching them not to access non-existent L2 headers of such packets.
Signed-off-by: Simon Horman <simon.hor...@netronome.com> --- v11 * group l2 code in pop_mpls() v10 * Limit scope of hdr in {push,pop}_mpls() v9 * New Patch --- include/uapi/linux/openvswitch.h | 2 ++ net/openvswitch/actions.c | 24 +++++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index d95a3018f6a1..5cde501433eb 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -737,6 +737,8 @@ enum ovs_nat_attr { * is no MPLS label stack, as determined by ethertype, no action is taken. * @OVS_ACTION_ATTR_CT: Track the connection. Populate the conntrack-related * entries in the flow key. + * @OVS_ACTION_ATTR_PUSH_ETH: Push a new outermost Ethernet header onto the * packet. + * @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the packet. * * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 1ecbd7715f6d..12e8a8942a42 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -163,8 +163,6 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key, return -ENOMEM; skb_push(skb, MPLS_HLEN); - memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb), - skb->mac_len); skb_reset_mac_header(skb); new_mpls_lse = (__be32 *)skb_mpls_header(skb); @@ -172,7 +170,11 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key, skb_postpush_rcsum(skb, new_mpls_lse, MPLS_HLEN); - update_ethertype(skb, eth_hdr(skb), mpls->mpls_ethertype); + if (skb->mac_len) { + update_ethertype(skb, eth_hdr(skb), mpls->mpls_ethertype); + memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb), + skb->mac_len); + } if (!skb->inner_protocol) skb_set_inner_protocol(skb, skb->protocol); skb->protocol = mpls->mpls_ethertype; @@ -184,7 +186,6 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key, static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key, const __be16 ethertype) { - struct ethhdr *hdr; int err; err = skb_ensure_writable(skb, skb->mac_len + MPLS_HLEN); @@ -199,11 +200,16 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key, __skb_pull(skb, MPLS_HLEN); skb_reset_mac_header(skb); - /* skb_mpls_header() is used to locate the ethertype - * field correctly in the presence of VLAN tags. - */ - hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN); - update_ethertype(skb, hdr, ethertype); + if (skb->mac_len) { + struct ethhdr *hdr; + + /* skb_mpls_header() is used to locate the ethertype + * field correctly in the presence of VLAN tags. + */ + hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN); + update_ethertype(skb, hdr, ethertype); + } + if (eth_p_mpls(skb->protocol)) skb->protocol = ethertype; -- 2.7.0.rc3.207.g0ac5344 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev