diff options
Diffstat (limited to 'datapath/linux/compat/gso.c')
-rw-r--r-- | datapath/linux/compat/gso.c | 317 |
1 files changed, 0 insertions, 317 deletions
diff --git a/datapath/linux/compat/gso.c b/datapath/linux/compat/gso.c deleted file mode 100644 index 65da5d876..000000000 --- a/datapath/linux/compat/gso.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * 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 - */ - -#include <linux/version.h> - -#include <linux/module.h> -#include <linux/if.h> -#include <linux/if_tunnel.h> -#include <linux/if_vlan.h> -#include <linux/icmp.h> -#include <linux/in.h> -#include <linux/ip.h> -#include <linux/ipv6.h> -#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> -#include <net/mpls.h> -#include <net/protocol.h> -#include <net/route.h> -#include <net/xfrm.h> - -#include "gso.h" - -#ifdef OVS_USE_COMPAT_GSO_SEGMENTATION -/* Strictly this is not needed and will be optimised out - * as this code is guarded by if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0). - * It is here to make things explicit should the compatibility - * code be extended in some way prior extending its life-span - * beyond v3.19. - */ -static bool supports_mpls_gso(void) -{ -/* MPLS GSO was introduced in v3.11, however it was not correctly - * activated using mpls_features until v3.19. */ -#ifdef OVS_USE_COMPAT_GSO_SEGMENTATION - return true; -#else - return false; -#endif -} - -int rpl_dev_queue_xmit(struct sk_buff *skb) -{ -#undef dev_queue_xmit - int err = -ENOMEM; - bool mpls; - - mpls = false; - - /* 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; - - if (mpls) { - int features; - - features = netif_skb_features(skb); - - /* 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 - * NETIF_F_SG and as of v3.19. - * - * This compatibility code is intended for kernels older - * than v3.19 that do not support MPLS GSO and do not - * use mpls_features. Thus this code uses NETIF_F_SG - * directly in place of mpls_features. - */ - if (mpls) - features &= NETIF_F_SG; - - if (netif_needs_gso(skb, features)) { - 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; -} -EXPORT_SYMBOL_GPL(rpl_dev_queue_xmit); -#endif /* OVS_USE_COMPAT_GSO_SEGMENTATION */ - -#ifndef USE_UPSTREAM_TUNNEL_GSO -static __be16 __skb_network_protocol(struct sk_buff *skb) -{ - __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; - } - - if (eth_p_mpls(type)) - type = ovs_skb_get_inner_protocol(skb); - - return type; -} - -static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb, - netdev_features_t features, - bool tx_path, - sa_family_t sa_family) -{ - void *iph = skb_network_header(skb); - int pkt_hlen = skb_inner_network_offset(skb); /* inner l2 + tunnel hdr. */ - int mac_offset = skb_inner_mac_offset(skb); - int outer_l3_offset = skb_network_offset(skb); - int outer_l4_offset = skb_transport_offset(skb); - struct sk_buff *skb1 = skb; - struct dst_entry *dst = skb_dst(skb); - struct sk_buff *segs; - __be16 proto = skb->protocol; - char cb[sizeof(skb->cb)]; - - BUILD_BUG_ON(sizeof(struct ovs_gso_cb) > sizeof_field(struct sk_buff, cb)); - OVS_GSO_CB(skb)->ipv6 = (sa_family == AF_INET6); - /* setup whole inner packet to get protocol. */ - __skb_pull(skb, mac_offset); - skb->protocol = __skb_network_protocol(skb); - - /* 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); - - /* 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)); - - /* 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 - segs = __skb_gso_segment(skb, 0, tx_path); -#else -#undef skb_gso_segment - segs = skb_gso_segment(skb, 0); -#endif - - if (!segs || IS_ERR(segs)) - goto free; - - skb = segs; - while (skb) { - __skb_push(skb, pkt_hlen); - skb_reset_mac_header(skb); - skb_set_network_header(skb, outer_l3_offset); - skb_set_transport_header(skb, outer_l4_offset); - skb->mac_len = 0; - - memcpy(skb_network_header(skb), iph, pkt_hlen); - memcpy(skb->cb, cb, sizeof(cb)); - - skb->protocol = proto; - if (skb->next) - dst = dst_clone(dst); - - skb_dst_set(skb, dst); - OVS_GSO_CB(skb)->fix_segment(skb); - - skb = skb->next; - } -free: - consume_skb(skb1); - return segs; -} - -static int output_ip(struct sk_buff *skb) -{ - memset(IPCB(skb), 0, sizeof(*IPCB(skb))); - -#undef ip_local_out - return ip_local_out(skb); -} - -int rpl_ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) -{ - if (!OVS_GSO_CB(skb)->fix_segment) - return output_ip(skb); - - /* This bit set can confuse some drivers on old kernel. */ - skb->encapsulation = 0; - - if (skb_is_gso(skb)) { - int ret; - int id; - - skb = tnl_skb_gso_segment(skb, 0, false, AF_INET); - if (!skb || IS_ERR(skb)) - 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; - } else if (skb->ip_summed == CHECKSUM_PARTIAL) { - int err; - - err = skb_checksum_help(skb); - if (unlikely(err)) - return NET_XMIT_DROP; - } - - return output_ip(skb); -} -EXPORT_SYMBOL_GPL(rpl_ip_local_out); - -static int output_ipv6(struct sk_buff *skb) -{ - memset(IP6CB(skb), 0, sizeof (*IP6CB(skb))); -#undef ip6_local_out - return ip6_local_out(skb); -} - -int rpl_ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) -{ - if (!OVS_GSO_CB(skb)->fix_segment) - return output_ipv6(skb); - - /* This bit set can confuse some drivers on old kernel. */ - skb->encapsulation = 0; - - if (skb_is_gso(skb)) { - int ret; - - skb = tnl_skb_gso_segment(skb, 0, false, AF_INET6); - if (!skb || IS_ERR(skb)) - 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; - } else if (skb->ip_summed == CHECKSUM_PARTIAL) { - int err; - - err = skb_checksum_help(skb); - if (unlikely(err)) - return NET_XMIT_DROP; - } - - return output_ipv6(skb); -} -EXPORT_SYMBOL_GPL(rpl_ip6_local_out); -#endif /* USE_UPSTREAM_TUNNEL_GSO */ |