2013-06-20 17:11:43 -07:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2007-2013 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.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
|
* 02110-1301, USA
|
|
|
|
|
*/
|
|
|
|
|
|
2014-02-07 10:46:53 -08:00
|
|
|
#include <linux/version.h>
|
|
|
|
|
|
2013-06-20 17:11:43 -07:00
|
|
|
#include <linux/module.h>
|
|
|
|
|
#include <linux/if.h>
|
|
|
|
|
#include <linux/if_tunnel.h>
|
2014-06-24 20:56:57 +09:00
|
|
|
#include <linux/if_vlan.h>
|
2013-06-20 17:11:43 -07:00
|
|
|
#include <linux/icmp.h>
|
|
|
|
|
#include <linux/in.h>
|
|
|
|
|
#include <linux/ip.h>
|
2016-07-08 18:18:39 -07:00
|
|
|
#include <linux/ipv6.h>
|
2013-06-20 17:11:43 -07:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
#include <linux/kmod.h>
|
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
|
|
|
|
|
|
#include <net/gre.h>
|
|
|
|
|
#include <net/icmp.h>
|
2014-11-08 08:14:21 -08:00
|
|
|
#include <net/mpls.h>
|
2013-06-20 17:11:43 -07:00
|
|
|
#include <net/protocol.h>
|
|
|
|
|
#include <net/route.h>
|
|
|
|
|
#include <net/xfrm.h>
|
|
|
|
|
|
|
|
|
|
#include "gso.h"
|
2013-09-24 16:44:06 +09:00
|
|
|
|
2015-05-07 10:17:26 -07:00
|
|
|
#ifdef OVS_USE_COMPAT_GSO_SEGMENTATION
|
2014-06-24 20:56:57 +09:00
|
|
|
/* Strictly this is not needed and will be optimised out
|
2015-05-07 10:17:26 -07:00
|
|
|
* as this code is guarded by if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0).
|
2014-06-24 20:56:57 +09:00
|
|
|
* It is here to make things explicit should the compatibility
|
|
|
|
|
* code be extended in some way prior extending its life-span
|
2015-05-07 10:17:26 -07:00
|
|
|
* beyond v3.19.
|
2014-06-24 20:56:57 +09:00
|
|
|
*/
|
|
|
|
|
static bool supports_mpls_gso(void)
|
|
|
|
|
{
|
|
|
|
|
/* MPLS GSO was introduced in v3.11, however it was not correctly
|
2015-05-07 10:17:26 -07:00
|
|
|
* activated using mpls_features until v3.19. */
|
|
|
|
|
#ifdef OVS_USE_COMPAT_GSO_SEGMENTATION
|
2014-06-24 20:56:57 +09:00
|
|
|
return true;
|
|
|
|
|
#else
|
|
|
|
|
return false;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-24 16:44:06 +09:00
|
|
|
int rpl_dev_queue_xmit(struct sk_buff *skb)
|
|
|
|
|
{
|
|
|
|
|
#undef dev_queue_xmit
|
|
|
|
|
int err = -ENOMEM;
|
2016-07-21 11:14:17 -07:00
|
|
|
bool mpls;
|
2014-06-24 20:56:57 +09:00
|
|
|
|
2016-07-21 11:14:17 -07:00
|
|
|
mpls = false;
|
2014-06-24 20:56:57 +09:00
|
|
|
|
|
|
|
|
/* Avoid traversing any VLAN tags that are present to determine if
|
|
|
|
|
* the ethtype is MPLS. Instead compare the mac_len (end of L2) and
|
|
|
|
|
* skb_network_offset() (beginning of L3) whose inequality will
|
|
|
|
|
* indicate the presence of an MPLS label stack. */
|
|
|
|
|
if (skb->mac_len != skb_network_offset(skb) && !supports_mpls_gso())
|
|
|
|
|
mpls = true;
|
|
|
|
|
|
2016-07-21 11:14:17 -07:00
|
|
|
if (mpls) {
|
2013-09-24 16:44:06 +09:00
|
|
|
int features;
|
|
|
|
|
|
|
|
|
|
features = netif_skb_features(skb);
|
|
|
|
|
|
2014-06-24 20:56:57 +09:00
|
|
|
/* As of v3.11 the kernel provides an mpls_features field in
|
|
|
|
|
* struct net_device which allows devices to advertise which
|
|
|
|
|
* features its supports for MPLS. This value defaults to
|
2015-05-07 10:17:26 -07:00
|
|
|
* NETIF_F_SG and as of v3.19.
|
2014-06-24 20:56:57 +09:00
|
|
|
*
|
|
|
|
|
* This compatibility code is intended for kernels older
|
2015-05-07 10:17:26 -07:00
|
|
|
* than v3.19 that do not support MPLS GSO and do not
|
2014-06-24 20:56:57 +09:00
|
|
|
* use mpls_features. Thus this code uses NETIF_F_SG
|
|
|
|
|
* directly in place of mpls_features.
|
|
|
|
|
*/
|
|
|
|
|
if (mpls)
|
|
|
|
|
features &= NETIF_F_SG;
|
2013-09-24 16:44:06 +09:00
|
|
|
|
2015-12-03 11:40:53 -08:00
|
|
|
if (netif_needs_gso(skb, features)) {
|
2013-09-24 16:44:06 +09:00
|
|
|
struct sk_buff *nskb;
|
|
|
|
|
|
|
|
|
|
nskb = skb_gso_segment(skb, features);
|
|
|
|
|
if (!nskb) {
|
|
|
|
|
if (unlikely(skb_cloned(skb) &&
|
|
|
|
|
pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
|
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
|
|
skb_shinfo(skb)->gso_type &= ~SKB_GSO_DODGY;
|
|
|
|
|
goto xmit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IS_ERR(nskb)) {
|
|
|
|
|
err = PTR_ERR(nskb);
|
|
|
|
|
goto drop;
|
|
|
|
|
}
|
|
|
|
|
consume_skb(skb);
|
|
|
|
|
skb = nskb;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
nskb = skb->next;
|
|
|
|
|
skb->next = NULL;
|
|
|
|
|
err = dev_queue_xmit(skb);
|
|
|
|
|
skb = nskb;
|
|
|
|
|
} while (skb);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
xmit:
|
|
|
|
|
return dev_queue_xmit(skb);
|
|
|
|
|
|
|
|
|
|
drop:
|
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2015-04-20 18:19:53 -07:00
|
|
|
EXPORT_SYMBOL_GPL(rpl_dev_queue_xmit);
|
2015-05-07 10:17:26 -07:00
|
|
|
#endif /* OVS_USE_COMPAT_GSO_SEGMENTATION */
|
2013-09-24 16:44:06 +09:00
|
|
|
|
2016-08-01 20:12:06 -07:00
|
|
|
#ifndef USE_UPSTREAM_TUNNEL_GSO
|
2013-07-09 18:00:25 +02:00
|
|
|
static __be16 __skb_network_protocol(struct sk_buff *skb)
|
2013-06-20 17:11:43 -07:00
|
|
|
{
|
|
|
|
|
__be16 type = skb->protocol;
|
|
|
|
|
int vlan_depth = ETH_HLEN;
|
|
|
|
|
|
|
|
|
|
while (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
|
|
|
|
|
struct vlan_hdr *vh;
|
|
|
|
|
|
|
|
|
|
if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN)))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
vh = (struct vlan_hdr *)(skb->data + vlan_depth);
|
|
|
|
|
type = vh->h_vlan_encapsulated_proto;
|
|
|
|
|
vlan_depth += VLAN_HLEN;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-24 20:56:57 +09:00
|
|
|
if (eth_p_mpls(type))
|
|
|
|
|
type = ovs_skb_get_inner_protocol(skb);
|
|
|
|
|
|
2013-06-20 17:11:43 -07:00
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,
|
|
|
|
|
netdev_features_t features,
|
2016-07-08 18:18:39 -07:00
|
|
|
bool tx_path,
|
|
|
|
|
sa_family_t sa_family)
|
2013-06-20 17:11:43 -07:00
|
|
|
{
|
2016-07-07 21:52:34 -07:00
|
|
|
void *iph = skb_network_header(skb);
|
2013-06-20 17:11:43 -07:00
|
|
|
int pkt_hlen = skb_inner_network_offset(skb); /* inner l2 + tunnel hdr. */
|
|
|
|
|
int mac_offset = skb_inner_mac_offset(skb);
|
2016-07-07 21:52:34 -07:00
|
|
|
int outer_l3_offset = skb_network_offset(skb);
|
|
|
|
|
int outer_l4_offset = skb_transport_offset(skb);
|
2013-06-20 17:11:43 -07:00
|
|
|
struct sk_buff *skb1 = skb;
|
2016-07-17 09:52:02 -07:00
|
|
|
struct dst_entry *dst = skb_dst(skb);
|
2013-06-20 17:11:43 -07:00
|
|
|
struct sk_buff *segs;
|
|
|
|
|
__be16 proto = skb->protocol;
|
2013-07-31 17:18:19 -04:00
|
|
|
char cb[sizeof(skb->cb)];
|
2013-06-20 17:11:43 -07:00
|
|
|
|
2016-08-02 17:03:41 -07:00
|
|
|
BUILD_BUG_ON(sizeof(struct ovs_gso_cb) > FIELD_SIZEOF(struct sk_buff, cb));
|
2016-07-08 18:18:39 -07:00
|
|
|
OVS_GSO_CB(skb)->ipv6 = (sa_family == AF_INET6);
|
2013-06-20 17:11:43 -07:00
|
|
|
/* setup whole inner packet to get protocol. */
|
|
|
|
|
__skb_pull(skb, mac_offset);
|
2013-07-09 18:00:25 +02:00
|
|
|
skb->protocol = __skb_network_protocol(skb);
|
2013-06-20 17:11:43 -07:00
|
|
|
|
|
|
|
|
/* setup l3 packet to gso, to get around segmentation bug on older kernel.*/
|
|
|
|
|
__skb_pull(skb, (pkt_hlen - mac_offset));
|
|
|
|
|
skb_reset_mac_header(skb);
|
|
|
|
|
skb_reset_network_header(skb);
|
|
|
|
|
skb_reset_transport_header(skb);
|
|
|
|
|
|
2013-07-31 17:18:19 -04:00
|
|
|
/* From 3.9 kernel skb->cb is used by skb gso. Therefore
|
|
|
|
|
* make copy of it to restore it back. */
|
|
|
|
|
memcpy(cb, skb->cb, sizeof(cb));
|
|
|
|
|
|
2015-05-07 10:17:26 -07:00
|
|
|
/* We are handling offloads by segmenting l3 packet, so
|
|
|
|
|
* no need to call OVS compat segmentation function. */
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE___SKB_GSO_SEGMENT
|
|
|
|
|
#undef __skb_gso_segment
|
2013-06-20 17:11:43 -07:00
|
|
|
segs = __skb_gso_segment(skb, 0, tx_path);
|
2015-05-07 10:17:26 -07:00
|
|
|
#else
|
|
|
|
|
#undef skb_gso_segment
|
|
|
|
|
segs = skb_gso_segment(skb, 0);
|
|
|
|
|
#endif
|
|
|
|
|
|
2013-06-20 17:11:43 -07:00
|
|
|
if (!segs || IS_ERR(segs))
|
|
|
|
|
goto free;
|
|
|
|
|
|
|
|
|
|
skb = segs;
|
|
|
|
|
while (skb) {
|
|
|
|
|
__skb_push(skb, pkt_hlen);
|
|
|
|
|
skb_reset_mac_header(skb);
|
2016-07-07 21:52:34 -07:00
|
|
|
skb_set_network_header(skb, outer_l3_offset);
|
|
|
|
|
skb_set_transport_header(skb, outer_l4_offset);
|
2013-06-20 17:11:43 -07:00
|
|
|
skb->mac_len = 0;
|
|
|
|
|
|
2016-07-07 21:52:34 -07:00
|
|
|
memcpy(skb_network_header(skb), iph, pkt_hlen);
|
2013-07-31 17:18:19 -04:00
|
|
|
memcpy(skb->cb, cb, sizeof(cb));
|
2013-06-20 17:11:43 -07:00
|
|
|
|
|
|
|
|
skb->protocol = proto;
|
2016-07-17 09:52:02 -07:00
|
|
|
if (skb->next)
|
|
|
|
|
dst = dst_clone(dst);
|
|
|
|
|
|
|
|
|
|
skb_dst_set(skb, dst);
|
|
|
|
|
OVS_GSO_CB(skb)->fix_segment(skb);
|
|
|
|
|
|
2013-06-20 17:11:43 -07:00
|
|
|
skb = skb->next;
|
|
|
|
|
}
|
|
|
|
|
free:
|
|
|
|
|
consume_skb(skb1);
|
|
|
|
|
return segs;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-18 14:27:17 -08:00
|
|
|
static int output_ip(struct sk_buff *skb)
|
|
|
|
|
{
|
|
|
|
|
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
|
|
|
|
|
|
|
|
|
|
#undef ip_local_out
|
2016-07-26 13:37:46 -07:00
|
|
|
return ip_local_out(skb);
|
2015-02-18 14:27:17 -08:00
|
|
|
}
|
|
|
|
|
|
2016-07-18 15:13:15 -07:00
|
|
|
int rpl_ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
|
2013-06-20 17:11:43 -07:00
|
|
|
{
|
2015-02-18 14:27:17 -08:00
|
|
|
if (!OVS_GSO_CB(skb)->fix_segment)
|
|
|
|
|
return output_ip(skb);
|
|
|
|
|
|
2016-08-17 10:23:01 -07:00
|
|
|
/* This bit set can confuse some drivers on old kernel. */
|
|
|
|
|
skb->encapsulation = 0;
|
|
|
|
|
|
2013-06-20 17:11:43 -07:00
|
|
|
if (skb_is_gso(skb)) {
|
2016-07-26 13:37:46 -07:00
|
|
|
int ret;
|
|
|
|
|
int id;
|
2013-06-20 17:11:43 -07:00
|
|
|
|
2016-07-08 18:18:39 -07:00
|
|
|
skb = tnl_skb_gso_segment(skb, 0, false, AF_INET);
|
2013-06-20 17:11:43 -07:00
|
|
|
if (!skb || IS_ERR(skb))
|
2016-07-26 13:37:46 -07:00
|
|
|
return NET_XMIT_DROP;
|
|
|
|
|
|
|
|
|
|
id = ntohs(ip_hdr(skb)->id);
|
|
|
|
|
do {
|
|
|
|
|
struct sk_buff *next_skb = skb->next;
|
|
|
|
|
|
|
|
|
|
skb->next = NULL;
|
|
|
|
|
ip_hdr(skb)->id = htons(id++);
|
|
|
|
|
|
|
|
|
|
ret = output_ip(skb);
|
|
|
|
|
skb = next_skb;
|
|
|
|
|
} while (skb);
|
|
|
|
|
return ret;
|
2013-06-20 17:11:43 -07:00
|
|
|
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = skb_checksum_help(skb);
|
|
|
|
|
if (unlikely(err))
|
2016-07-26 13:37:46 -07:00
|
|
|
return NET_XMIT_DROP;
|
2013-06-20 17:11:43 -07:00
|
|
|
}
|
|
|
|
|
|
2016-07-26 13:37:46 -07:00
|
|
|
return output_ip(skb);
|
2013-06-20 17:11:43 -07:00
|
|
|
}
|
2015-04-20 18:19:53 -07:00
|
|
|
EXPORT_SYMBOL_GPL(rpl_ip_local_out);
|
|
|
|
|
|
2016-07-08 18:18:39 -07:00
|
|
|
static int output_ipv6(struct sk_buff *skb)
|
|
|
|
|
{
|
|
|
|
|
memset(IP6CB(skb), 0, sizeof (*IP6CB(skb)));
|
|
|
|
|
#undef ip6_local_out
|
2016-07-26 13:37:46 -07:00
|
|
|
return ip6_local_out(skb);
|
2016-07-08 18:18:39 -07:00
|
|
|
}
|
|
|
|
|
|
2016-07-18 15:13:15 -07:00
|
|
|
int rpl_ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
|
2016-07-08 18:18:39 -07:00
|
|
|
{
|
|
|
|
|
if (!OVS_GSO_CB(skb)->fix_segment)
|
|
|
|
|
return output_ipv6(skb);
|
|
|
|
|
|
2016-08-17 10:23:01 -07:00
|
|
|
/* This bit set can confuse some drivers on old kernel. */
|
|
|
|
|
skb->encapsulation = 0;
|
|
|
|
|
|
2016-07-08 18:18:39 -07:00
|
|
|
if (skb_is_gso(skb)) {
|
2016-07-26 13:37:46 -07:00
|
|
|
int ret;
|
|
|
|
|
|
2016-07-08 18:18:39 -07:00
|
|
|
skb = tnl_skb_gso_segment(skb, 0, false, AF_INET6);
|
|
|
|
|
if (!skb || IS_ERR(skb))
|
2016-07-26 13:37:46 -07:00
|
|
|
return NET_XMIT_DROP;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
struct sk_buff *next_skb = skb->next;
|
|
|
|
|
|
|
|
|
|
skb->next = NULL;
|
|
|
|
|
ret = output_ipv6(skb);
|
|
|
|
|
skb = next_skb;
|
|
|
|
|
} while (skb);
|
|
|
|
|
return ret;
|
2016-07-08 18:18:39 -07:00
|
|
|
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = skb_checksum_help(skb);
|
|
|
|
|
if (unlikely(err))
|
2016-07-26 13:37:46 -07:00
|
|
|
return NET_XMIT_DROP;
|
2016-07-08 18:18:39 -07:00
|
|
|
}
|
|
|
|
|
|
2016-07-26 13:37:46 -07:00
|
|
|
return output_ipv6(skb);
|
2016-07-08 18:18:39 -07:00
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL_GPL(rpl_ip6_local_out);
|
2016-08-01 20:12:06 -07:00
|
|
|
#endif /* USE_UPSTREAM_TUNNEL_GSO */
|