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