flow.h \
flow_netlink.h \
flow_table.h \
- mpls.h \
vlan.h \
vport.h \
vport-internal_dev.h \
#include <net/ipv6.h>
#include <net/checksum.h>
#include <net/dsfield.h>
+#include <net/mpls.h>
#include <net/sctp/checksum.h>
#include "datapath.h"
#include "gso.h"
-#include "mpls.h"
#include "vlan.h"
#include "vport.h"
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,
const struct ovs_action_push_mpls *mpls)
{
__be32 *new_mpls_lse;
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)
return -ENOMEM;
skb->mac_len);
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;
if (skb->ip_summed == CHECKSUM_COMPLETE)
if (!ovs_skb_get_inner_protocol(skb))
ovs_skb_set_inner_protocol(skb, skb->protocol);
skb->protocol = mpls->mpls_ethertype;
+
invalidate_flow_key(key);
return 0;
}
if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->csum = csum_sub(skb->csum,
- csum_partial(mac_header_end(skb),
+ csum_partial(skb_mpls_header(skb),
MPLS_HLEN, 0));
memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb),
__skb_pull(skb, MPLS_HLEN);
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.
*/
- hdr = (struct ethhdr *)(mac_header_end(skb) - ETH_HLEN);
+ hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN);
hdr->h_proto = ethertype;
if (eth_p_mpls(skb->protocol))
skb->protocol = ethertype;
+
invalidate_flow_key(key);
return 0;
}
static int set_mpls(struct sk_buff *skb, struct sw_flow_key *key,
const __be32 *mpls_lse)
{
- __be32 *stack = (__be32 *)mac_header_end(skb);
+ __be32 *stack;
int err;
err = make_writable(skb, skb->mac_len + MPLS_HLEN);
if (unlikely(err))
return err;
+ stack = (__be32 *)skb_mpls_header(skb);
if (skb->ip_summed == CHECKSUM_COMPLETE) {
__be32 diff[] = { ~(*stack), *mpls_lse };
skb->csum = ~csum_partial((char *)diff, sizeof(diff),
if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag))
return -ENOMEM;
-
/* Update mac_len for subsequent MPLS actions */
skb->mac_len += VLAN_HLEN;
static int output_userspace(struct datapath *dp, struct sk_buff *skb,
struct sw_flow_key *key, const struct nlattr *attr)
{
+ struct ovs_tunnel_info info;
struct dp_upcall_info upcall;
const struct nlattr *a;
int rem;
- struct ovs_tunnel_info info;
upcall.cmd = OVS_PACKET_CMD_ACTION;
upcall.userdata = NULL;
return rt;
#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 */
#include <linux/if_arp.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
+#include <linux/mpls.h>
#include <linux/sctp.h>
#include <linux/smp.h>
#include <linux/tcp.h>
#include <linux/rculist.h>
#include <net/ip.h>
#include <net/ipv6.h>
+#include <net/mpls.h>
#include <net/ndisc.h>
#include "datapath.h"
#include "flow.h"
#include "flow_netlink.h"
-#include "mpls.h"
#include "vlan.h"
u64 ovs_flow_used_time(unsigned long flow_jiffies)
memcpy(&key->mpls.top_lse, &lse, MPLS_HLEN);
skb_set_network_header(skb, skb->mac_len + stack_len);
- if (lse & htonl(MPLS_BOS_MASK))
+ if (lse & htonl(MPLS_LS_S_MASK))
break;
stack_len += MPLS_HLEN;
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include "flow.h"
-#include "datapath.h"
-#include "mpls.h"
#include <linux/uaccess.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/rculist.h>
#include <net/geneve.h>
#include <net/ip.h>
-#include <net/ip_tunnels.h>
#include <net/ipv6.h>
#include <net/ndisc.h>
+#include <net/mpls.h>
+#include "datapath.h"
+#include "flow.h"
#include "flow_netlink.h"
static void update_range(struct sw_flow_match *match,
break;
case OVS_KEY_ATTR_TUNNEL:
+ if (eth_p_mpls(eth_type))
+ return -EINVAL;
+
*set_tun = true;
err = validate_and_copy_set_tun(a, sfa, log);
if (err)
__be16 eth_type, __be16 vlan_tci, bool log)
{
const struct nlattr *a;
+ bool out_tnl_port = false;
int rem, err;
if (depth >= SAMPLE_ACTION_DEPTH)
case OVS_ACTION_ATTR_OUTPUT:
if (nla_get_u32(a) >= DP_MAX_PORTS)
return -EINVAL;
+ out_tnl_port = false;
break;
case OVS_ACTION_ATTR_HASH: {
case OVS_ACTION_ATTR_PUSH_MPLS: {
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))
return -EINVAL;
/* Prohibit push MPLS other than to a white list
break;
case OVS_ACTION_ATTR_SET:
- err = validate_set(a, key, sfa, &skip_copy, eth_type,
- log);
+ err = validate_set(a, key, sfa,
+ &out_tnl_port, eth_type, log);
if (err)
return err;
+ skip_copy = out_tnl_port;
break;
case OVS_ACTION_ATTR_SAMPLE:
linux/compat/include/linux/kernel.h \
linux/compat/include/linux/list.h \
linux/compat/include/linux/log2.h \
+ linux/compat/include/linux/mpls.h \
linux/compat/include/linux/net.h \
linux/compat/include/linux/random.h \
linux/compat/include/linux/netdevice.h \
linux/compat/include/net/ip.h \
linux/compat/include/net/ip_tunnels.h \
linux/compat/include/net/ipv6.h \
+ linux/compat/include/net/mpls.h \
linux/compat/include/net/net_namespace.h \
linux/compat/include/net/netlink.h \
linux/compat/include/net/udp.h \
#include <net/gre.h>
#include <net/icmp.h>
+#include <net/mpls.h>
#include <net/protocol.h>
#include <net/route.h>
#include <net/xfrm.h>
#include "gso.h"
-#include "mpls.h"
#include "vlan.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) && \
*/
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
__be16 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)
{
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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 */
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
+#include <net/mpls.h>
-#include "mpls.h"
#include "gso.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+++ /dev/null
-#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