datapath: Drop support for kernel older than 3.10
[cascardo/ovs.git] / datapath / linux / compat / gso.c
1 /*
2  * Copyright (c) 2007-2013 Nicira, Inc.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of version 2 of the GNU General Public
6  * License as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16  * 02110-1301, USA
17  */
18
19 #include <linux/version.h>
20
21 #include <linux/module.h>
22 #include <linux/if.h>
23 #include <linux/if_tunnel.h>
24 #include <linux/if_vlan.h>
25 #include <linux/icmp.h>
26 #include <linux/in.h>
27 #include <linux/ip.h>
28 #include <linux/kernel.h>
29 #include <linux/kmod.h>
30 #include <linux/netdevice.h>
31 #include <linux/skbuff.h>
32 #include <linux/spinlock.h>
33
34 #include <net/gre.h>
35 #include <net/icmp.h>
36 #include <net/mpls.h>
37 #include <net/protocol.h>
38 #include <net/route.h>
39 #include <net/xfrm.h>
40
41 #include "gso.h"
42
43 #ifdef OVS_USE_COMPAT_GSO_SEGMENTATION
44 static bool dev_supports_vlan_tx(struct net_device *dev)
45 {
46 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
47         return true;
48 #elif defined(HAVE_VLAN_BUG_WORKAROUND)
49         return dev->features & NETIF_F_HW_VLAN_TX;
50 #else
51         /* Assume that the driver is buggy. */
52         return false;
53 #endif
54 }
55
56 /* Strictly this is not needed and will be optimised out
57  * as this code is guarded by if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0).
58  * It is here to make things explicit should the compatibility
59  * code be extended in some way prior extending its life-span
60  * beyond v3.19.
61  */
62 static bool supports_mpls_gso(void)
63 {
64 /* MPLS GSO was introduced in v3.11, however it was not correctly
65  * activated using mpls_features until v3.19. */
66 #ifdef OVS_USE_COMPAT_GSO_SEGMENTATION
67         return true;
68 #else
69         return false;
70 #endif
71 }
72
73 int rpl_dev_queue_xmit(struct sk_buff *skb)
74 {
75 #undef dev_queue_xmit
76         int err = -ENOMEM;
77         bool vlan, mpls;
78
79         vlan = mpls = false;
80
81         /* Avoid traversing any VLAN tags that are present to determine if
82          * the ethtype is MPLS. Instead compare the mac_len (end of L2) and
83          * skb_network_offset() (beginning of L3) whose inequality will
84          * indicate the presence of an MPLS label stack. */
85         if (skb->mac_len != skb_network_offset(skb) && !supports_mpls_gso())
86                 mpls = true;
87
88         if (skb_vlan_tag_present(skb) && !dev_supports_vlan_tx(skb->dev))
89                 vlan = true;
90
91         if (vlan || mpls) {
92                 int features;
93
94                 features = netif_skb_features(skb);
95
96                 if (vlan) {
97                         skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto,
98                                                         skb_vlan_tag_get(skb));
99                         if (unlikely(!skb))
100                                 return err;
101                         skb->vlan_tci = 0;
102                 }
103
104                 /* As of v3.11 the kernel provides an mpls_features field in
105                  * struct net_device which allows devices to advertise which
106                  * features its supports for MPLS. This value defaults to
107                  * NETIF_F_SG and as of v3.19.
108                  *
109                  * This compatibility code is intended for kernels older
110                  * than v3.19 that do not support MPLS GSO and do not
111                  * use mpls_features. Thus this code uses NETIF_F_SG
112                  * directly in place of mpls_features.
113                  */
114                 if (mpls)
115                         features &= NETIF_F_SG;
116
117                 if (netif_needs_gso(skb, features)) {
118                         struct sk_buff *nskb;
119
120                         nskb = skb_gso_segment(skb, features);
121                         if (!nskb) {
122                                 if (unlikely(skb_cloned(skb) &&
123                                     pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
124                                         goto drop;
125
126                                 skb_shinfo(skb)->gso_type &= ~SKB_GSO_DODGY;
127                                 goto xmit;
128                         }
129
130                         if (IS_ERR(nskb)) {
131                                 err = PTR_ERR(nskb);
132                                 goto drop;
133                         }
134                         consume_skb(skb);
135                         skb = nskb;
136
137                         do {
138                                 nskb = skb->next;
139                                 skb->next = NULL;
140                                 err = dev_queue_xmit(skb);
141                                 skb = nskb;
142                         } while (skb);
143
144                         return err;
145                 }
146         }
147 xmit:
148         return dev_queue_xmit(skb);
149
150 drop:
151         kfree_skb(skb);
152         return err;
153 }
154 EXPORT_SYMBOL_GPL(rpl_dev_queue_xmit);
155 #endif /* OVS_USE_COMPAT_GSO_SEGMENTATION */
156
157 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
158 static __be16 __skb_network_protocol(struct sk_buff *skb)
159 {
160         __be16 type = skb->protocol;
161         int vlan_depth = ETH_HLEN;
162
163         while (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
164                 struct vlan_hdr *vh;
165
166                 if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN)))
167                         return 0;
168
169                 vh = (struct vlan_hdr *)(skb->data + vlan_depth);
170                 type = vh->h_vlan_encapsulated_proto;
171                 vlan_depth += VLAN_HLEN;
172         }
173
174         if (eth_p_mpls(type))
175                 type = ovs_skb_get_inner_protocol(skb);
176
177         return type;
178 }
179
180 static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,
181                                            netdev_features_t features,
182                                            bool tx_path)
183 {
184         struct iphdr *iph = ip_hdr(skb);
185         int pkt_hlen = skb_inner_network_offset(skb); /* inner l2 + tunnel hdr. */
186         int mac_offset = skb_inner_mac_offset(skb);
187         struct sk_buff *skb1 = skb;
188         struct sk_buff *segs;
189         __be16 proto = skb->protocol;
190         char cb[sizeof(skb->cb)];
191
192         /* setup whole inner packet to get protocol. */
193         __skb_pull(skb, mac_offset);
194         skb->protocol = __skb_network_protocol(skb);
195
196         /* setup l3 packet to gso, to get around segmentation bug on older kernel.*/
197         __skb_pull(skb, (pkt_hlen - mac_offset));
198         skb_reset_mac_header(skb);
199         skb_reset_network_header(skb);
200         skb_reset_transport_header(skb);
201
202         /* From 3.9 kernel skb->cb is used by skb gso. Therefore
203          * make copy of it to restore it back. */
204         memcpy(cb, skb->cb, sizeof(cb));
205
206         /* We are handling offloads by segmenting l3 packet, so
207          * no need to call OVS compat segmentation function. */
208
209 #ifdef HAVE___SKB_GSO_SEGMENT
210 #undef __skb_gso_segment
211         segs = __skb_gso_segment(skb, 0, tx_path);
212 #else
213 #undef skb_gso_segment
214         segs = skb_gso_segment(skb, 0);
215 #endif
216
217         if (!segs || IS_ERR(segs))
218                 goto free;
219
220         skb = segs;
221         while (skb) {
222                 __skb_push(skb, pkt_hlen);
223                 skb_reset_mac_header(skb);
224                 skb_reset_network_header(skb);
225                 skb_set_transport_header(skb, sizeof(struct iphdr));
226                 skb->mac_len = 0;
227
228                 memcpy(ip_hdr(skb), iph, pkt_hlen);
229                 memcpy(skb->cb, cb, sizeof(cb));
230                 OVS_GSO_CB(skb)->fix_segment(skb);
231
232                 skb->protocol = proto;
233                 skb = skb->next;
234         }
235 free:
236         consume_skb(skb1);
237         return segs;
238 }
239
240 static int output_ip(struct sk_buff *skb)
241 {
242         int ret = NETDEV_TX_OK;
243         int err;
244
245         memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
246
247 #undef ip_local_out
248         err = ip_local_out(skb);
249         if (unlikely(net_xmit_eval(err)))
250                 ret = err;
251
252         return ret;
253 }
254
255 int rpl_ip_local_out(struct sk_buff *skb)
256 {
257         int ret = NETDEV_TX_OK;
258         int id = -1;
259
260         if (!OVS_GSO_CB(skb)->fix_segment)
261                 return output_ip(skb);
262
263         if (skb_is_gso(skb)) {
264                 struct iphdr *iph;
265
266                 iph = ip_hdr(skb);
267                 id = ntohs(iph->id);
268                 skb = tnl_skb_gso_segment(skb, 0, false);
269                 if (!skb || IS_ERR(skb))
270                         return 0;
271         }  else if (skb->ip_summed == CHECKSUM_PARTIAL) {
272                 int err;
273
274                 err = skb_checksum_help(skb);
275                 if (unlikely(err))
276                         return 0;
277         }
278
279         while (skb) {
280                 struct sk_buff *next_skb = skb->next;
281                 struct iphdr *iph;
282
283                 skb->next = NULL;
284
285                 iph = ip_hdr(skb);
286                 if (id >= 0)
287                         iph->id = htons(id++);
288
289                 ret = output_ip(skb);
290                 skb = next_skb;
291         }
292         return ret;
293 }
294 EXPORT_SYMBOL_GPL(rpl_ip_local_out);
295
296 #endif /* 3.18 */