2
0
mirror of https://github.com/openvswitch/ovs synced 2025-10-25 15:07:05 +00:00

datapath: Fix few mpls issues.

Found during MPLS upstreaming.  Also sync-up MPLS header files
with upstream code.

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
This commit is contained in:
Pravin B Shelar
2014-11-08 08:14:21 -08:00
parent 684b5f5dd1
commit 2baf0e0c6c
12 changed files with 135 additions and 47 deletions

View File

@@ -27,7 +27,6 @@ openvswitch_headers = \
flow.h \ flow.h \
flow_netlink.h \ flow_netlink.h \
flow_table.h \ flow_table.h \
mpls.h \
vlan.h \ vlan.h \
vport.h \ vport.h \
vport-internal_dev.h \ vport-internal_dev.h \

View File

@@ -32,11 +32,11 @@
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <net/dsfield.h> #include <net/dsfield.h>
#include <net/mpls.h>
#include <net/sctp/checksum.h> #include <net/sctp/checksum.h>
#include "datapath.h" #include "datapath.h"
#include "gso.h" #include "gso.h"
#include "mpls.h"
#include "vlan.h" #include "vlan.h"
#include "vport.h" #include "vport.h"
@@ -133,25 +133,16 @@ static int make_writable(struct sk_buff *skb, int write_len)
return pskb_expand_head(skb, 0, 0, GFP_ATOMIC); return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
} }
/* The end of the mac header.
*
* For non-MPLS skbs this will correspond to the network header.
* For MPLS skbs it will be before the network_header as the MPLS
* label stack lies between the end of the mac header and the network
* header. That is, for MPLS skbs the end of the mac header
* is the top of the MPLS label stack.
*/
static unsigned char *mac_header_end(const struct sk_buff *skb)
{
return skb_mac_header(skb) + skb->mac_len;
}
static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key, static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
const struct ovs_action_push_mpls *mpls) const struct ovs_action_push_mpls *mpls)
{ {
__be32 *new_mpls_lse; __be32 *new_mpls_lse;
struct ethhdr *hdr; struct ethhdr *hdr;
/* Networking stack do not allow simultaneous Tunnel and MPLS GSO. */
if (skb_encapsulation(skb))
return -ENOTSUPP;
if (skb_cow_head(skb, MPLS_HLEN) < 0) if (skb_cow_head(skb, MPLS_HLEN) < 0)
return -ENOMEM; return -ENOMEM;
@@ -160,7 +151,7 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
skb->mac_len); skb->mac_len);
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
new_mpls_lse = (__be32 *)mac_header_end(skb); new_mpls_lse = (__be32 *)skb_mpls_header(skb);
*new_mpls_lse = mpls->mpls_lse; *new_mpls_lse = mpls->mpls_lse;
if (skb->ip_summed == CHECKSUM_COMPLETE) if (skb->ip_summed == CHECKSUM_COMPLETE)
@@ -172,6 +163,7 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
if (!ovs_skb_get_inner_protocol(skb)) if (!ovs_skb_get_inner_protocol(skb))
ovs_skb_set_inner_protocol(skb, skb->protocol); ovs_skb_set_inner_protocol(skb, skb->protocol);
skb->protocol = mpls->mpls_ethertype; skb->protocol = mpls->mpls_ethertype;
invalidate_flow_key(key); invalidate_flow_key(key);
return 0; return 0;
} }
@@ -188,7 +180,7 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
if (skb->ip_summed == CHECKSUM_COMPLETE) if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->csum = csum_sub(skb->csum, skb->csum = csum_sub(skb->csum,
csum_partial(mac_header_end(skb), csum_partial(skb_mpls_header(skb),
MPLS_HLEN, 0)); MPLS_HLEN, 0));
memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb), memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb),
@@ -197,13 +189,14 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
__skb_pull(skb, MPLS_HLEN); __skb_pull(skb, MPLS_HLEN);
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
/* mac_header_end() is used to locate the ethertype /* skb_mpls_header() is used to locate the ethertype
* field correctly in the presence of VLAN tags. * field correctly in the presence of VLAN tags.
*/ */
hdr = (struct ethhdr *)(mac_header_end(skb) - ETH_HLEN); hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN);
hdr->h_proto = ethertype; hdr->h_proto = ethertype;
if (eth_p_mpls(skb->protocol)) if (eth_p_mpls(skb->protocol))
skb->protocol = ethertype; skb->protocol = ethertype;
invalidate_flow_key(key); invalidate_flow_key(key);
return 0; return 0;
} }
@@ -211,13 +204,14 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
static int set_mpls(struct sk_buff *skb, struct sw_flow_key *key, static int set_mpls(struct sk_buff *skb, struct sw_flow_key *key,
const __be32 *mpls_lse) const __be32 *mpls_lse)
{ {
__be32 *stack = (__be32 *)mac_header_end(skb); __be32 *stack;
int err; int err;
err = make_writable(skb, skb->mac_len + MPLS_HLEN); err = make_writable(skb, skb->mac_len + MPLS_HLEN);
if (unlikely(err)) if (unlikely(err))
return err; return err;
stack = (__be32 *)skb_mpls_header(skb);
if (skb->ip_summed == CHECKSUM_COMPLETE) { if (skb->ip_summed == CHECKSUM_COMPLETE) {
__be32 diff[] = { ~(*stack), *mpls_lse }; __be32 diff[] = { ~(*stack), *mpls_lse };
skb->csum = ~csum_partial((char *)diff, sizeof(diff), skb->csum = ~csum_partial((char *)diff, sizeof(diff),
@@ -300,7 +294,6 @@ static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag)) if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag))
return -ENOMEM; return -ENOMEM;
/* Update mac_len for subsequent MPLS actions */ /* Update mac_len for subsequent MPLS actions */
skb->mac_len += VLAN_HLEN; skb->mac_len += VLAN_HLEN;
@@ -629,10 +622,10 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
static int output_userspace(struct datapath *dp, struct sk_buff *skb, static int output_userspace(struct datapath *dp, struct sk_buff *skb,
struct sw_flow_key *key, const struct nlattr *attr) struct sw_flow_key *key, const struct nlattr *attr)
{ {
struct ovs_tunnel_info info;
struct dp_upcall_info upcall; struct dp_upcall_info upcall;
const struct nlattr *a; const struct nlattr *a;
int rem; int rem;
struct ovs_tunnel_info info;
upcall.cmd = OVS_PACKET_CMD_ACTION; upcall.cmd = OVS_PACKET_CMD_ACTION;
upcall.userdata = NULL; upcall.userdata = NULL;

View File

@@ -76,4 +76,14 @@ static inline struct rtable *find_route(struct net *net,
return rt; return rt;
#endif #endif
} }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
static inline bool skb_encapsulation(struct sk_buff *skb)
{
return skb->encapsulation;
}
#else
#define skb_encapsulation(skb) false
#endif
#endif /* compat.h */ #endif /* compat.h */

View File

@@ -32,6 +32,7 @@
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/mpls.h>
#include <linux/sctp.h> #include <linux/sctp.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/tcp.h> #include <linux/tcp.h>
@@ -41,13 +42,13 @@
#include <linux/rculist.h> #include <linux/rculist.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/mpls.h>
#include <net/ndisc.h> #include <net/ndisc.h>
#include "datapath.h" #include "datapath.h"
#include "flow.h" #include "flow.h"
#include "flow_netlink.h" #include "flow_netlink.h"
#include "mpls.h"
#include "vlan.h" #include "vlan.h"
u64 ovs_flow_used_time(unsigned long flow_jiffies) u64 ovs_flow_used_time(unsigned long flow_jiffies)
@@ -609,7 +610,7 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
memcpy(&key->mpls.top_lse, &lse, MPLS_HLEN); memcpy(&key->mpls.top_lse, &lse, MPLS_HLEN);
skb_set_network_header(skb, skb->mac_len + stack_len); skb_set_network_header(skb, skb->mac_len + stack_len);
if (lse & htonl(MPLS_BOS_MASK)) if (lse & htonl(MPLS_LS_S_MASK))
break; break;
stack_len += MPLS_HLEN; stack_len += MPLS_HLEN;

View File

@@ -18,9 +18,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "flow.h"
#include "datapath.h"
#include "mpls.h"
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
@@ -45,10 +42,12 @@
#include <linux/rculist.h> #include <linux/rculist.h>
#include <net/geneve.h> #include <net/geneve.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ip_tunnels.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/ndisc.h> #include <net/ndisc.h>
#include <net/mpls.h>
#include "datapath.h"
#include "flow.h"
#include "flow_netlink.h" #include "flow_netlink.h"
static void update_range(struct sw_flow_match *match, static void update_range(struct sw_flow_match *match,
@@ -1665,6 +1664,9 @@ static int validate_set(const struct nlattr *a,
break; break;
case OVS_KEY_ATTR_TUNNEL: case OVS_KEY_ATTR_TUNNEL:
if (eth_p_mpls(eth_type))
return -EINVAL;
*set_tun = true; *set_tun = true;
err = validate_and_copy_set_tun(a, sfa, log); err = validate_and_copy_set_tun(a, sfa, log);
if (err) if (err)
@@ -1778,6 +1780,7 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr,
__be16 eth_type, __be16 vlan_tci, bool log) __be16 eth_type, __be16 vlan_tci, bool log)
{ {
const struct nlattr *a; const struct nlattr *a;
bool out_tnl_port = false;
int rem, err; int rem, err;
if (depth >= SAMPLE_ACTION_DEPTH) if (depth >= SAMPLE_ACTION_DEPTH)
@@ -1820,6 +1823,7 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr,
case OVS_ACTION_ATTR_OUTPUT: case OVS_ACTION_ATTR_OUTPUT:
if (nla_get_u32(a) >= DP_MAX_PORTS) if (nla_get_u32(a) >= DP_MAX_PORTS)
return -EINVAL; return -EINVAL;
out_tnl_port = false;
break; break;
case OVS_ACTION_ATTR_HASH: { case OVS_ACTION_ATTR_HASH: {
@@ -1854,6 +1858,12 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr,
case OVS_ACTION_ATTR_PUSH_MPLS: { case OVS_ACTION_ATTR_PUSH_MPLS: {
const struct ovs_action_push_mpls *mpls = nla_data(a); const struct ovs_action_push_mpls *mpls = nla_data(a);
/* Networking stack do not allow simultaneous Tunnel
* and MPLS GSO.
*/
if (out_tnl_port)
return -EINVAL;
if (!eth_p_mpls(mpls->mpls_ethertype)) if (!eth_p_mpls(mpls->mpls_ethertype))
return -EINVAL; return -EINVAL;
/* Prohibit push MPLS other than to a white list /* Prohibit push MPLS other than to a white list
@@ -1888,10 +1898,11 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr,
break; break;
case OVS_ACTION_ATTR_SET: case OVS_ACTION_ATTR_SET:
err = validate_set(a, key, sfa, &skip_copy, eth_type, err = validate_set(a, key, sfa,
log); &out_tnl_port, eth_type, log);
if (err) if (err)
return err; return err;
skip_copy = out_tnl_port;
break; break;
case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_SAMPLE:

View File

@@ -41,6 +41,7 @@ openvswitch_headers += \
linux/compat/include/linux/kernel.h \ linux/compat/include/linux/kernel.h \
linux/compat/include/linux/list.h \ linux/compat/include/linux/list.h \
linux/compat/include/linux/log2.h \ linux/compat/include/linux/log2.h \
linux/compat/include/linux/mpls.h \
linux/compat/include/linux/net.h \ linux/compat/include/linux/net.h \
linux/compat/include/linux/random.h \ linux/compat/include/linux/random.h \
linux/compat/include/linux/netdevice.h \ linux/compat/include/linux/netdevice.h \
@@ -70,6 +71,7 @@ openvswitch_headers += \
linux/compat/include/net/ip.h \ linux/compat/include/net/ip.h \
linux/compat/include/net/ip_tunnels.h \ linux/compat/include/net/ip_tunnels.h \
linux/compat/include/net/ipv6.h \ linux/compat/include/net/ipv6.h \
linux/compat/include/net/mpls.h \
linux/compat/include/net/net_namespace.h \ linux/compat/include/net/net_namespace.h \
linux/compat/include/net/netlink.h \ linux/compat/include/net/netlink.h \
linux/compat/include/net/udp.h \ linux/compat/include/net/udp.h \

View File

@@ -34,12 +34,12 @@
#include <net/gre.h> #include <net/gre.h>
#include <net/icmp.h> #include <net/icmp.h>
#include <net/mpls.h>
#include <net/protocol.h> #include <net/protocol.h>
#include <net/route.h> #include <net/route.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include "gso.h" #include "gso.h"
#include "mpls.h"
#include "vlan.h" #include "vlan.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) && \ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) && \

View File

@@ -102,11 +102,19 @@ static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb) {
*/ */
} }
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb, static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
__be16 ethertype) __be16 ethertype)
{ {
skb->inner_protocol = ethertype; skb->inner_protocol = ethertype;
} }
#else
static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
__be16 ethertype)
{
skb_set_inner_protocol(skb, ethertype);
}
#endif
static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb) static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb)
{ {

View File

@@ -0,0 +1,40 @@
#ifndef _UAPI_MPLS_WRAPPER_H
#define _UAPI_MPLS_WRAPPER_H
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
#include_next <linux/mpls.h>
#else
#include <linux/types.h>
#include <asm/byteorder.h>
/* Reference: RFC 5462, RFC 3032
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Label | TC |S| TTL |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Label: Label Value, 20 bits
* TC: Traffic Class field, 3 bits
* S: Bottom of Stack, 1 bit
* TTL: Time to Live, 8 bits
*/
struct mpls_label {
__be32 entry;
};
#define MPLS_LS_LABEL_MASK 0xFFFFF000
#define MPLS_LS_LABEL_SHIFT 12
#define MPLS_LS_TC_MASK 0x00000E00
#define MPLS_LS_TC_SHIFT 9
#define MPLS_LS_S_MASK 0x00000100
#define MPLS_LS_S_SHIFT 8
#define MPLS_LS_TTL_MASK 0x000000FF
#define MPLS_LS_TTL_SHIFT 0
#endif
#endif /* _UAPI_MPLS_WRAPPER_H */

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2014 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#ifndef _NET_MPLS_WRAPPER_H
#define _NET_MPLS_WRAPPER_H 1
#include <linux/if_ether.h>
#include <linux/netdevice.h>
#define MPLS_HLEN 4
static inline bool eth_p_mpls(__be16 eth_type)
{
return eth_type == htons(ETH_P_MPLS_UC) ||
eth_type == htons(ETH_P_MPLS_MC);
}
/*
* For non-MPLS skbs this will correspond to the network header.
* For MPLS skbs it will be before the network_header as the MPLS
* label stack lies between the end of the mac header and the network
* header. That is, for MPLS skbs the end of the mac header
* is the top of the MPLS label stack.
*/
static inline unsigned char *skb_mpls_header(struct sk_buff *skb)
{
return skb_mac_header(skb) + skb->mac_len;
}
#endif /* _NET_MPLS_WRAPPER_H */

View File

@@ -1,7 +1,7 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <net/mpls.h>
#include "mpls.h"
#include "gso.h" #include "gso.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)

View File

@@ -1,15 +0,0 @@
#ifndef MPLS_H
#define MPLS_H 1
#include <linux/if_ether.h>
#define MPLS_BOS_MASK 0x00000100
#define MPLS_HLEN 4
static inline bool eth_p_mpls(__be16 eth_type)
{
return eth_type == htons(ETH_P_MPLS_UC) ||
eth_type == htons(ETH_P_MPLS_MC);
}
#endif