1 /* 2 * IPV4 GSO/GRO offload support 3 * Linux INET implementation 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 8 * 2 of the License, or (at your option) any later version. 9 * 10 * GRE GSO support 11 */ 12 13 #include <linux/skbuff.h> 14 #include <linux/init.h> 15 #include <net/protocol.h> 16 #include <net/gre.h> 17 18 static struct sk_buff *gre_gso_segment(struct sk_buff *skb, 19 netdev_features_t features) 20 { 21 int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb); 22 struct sk_buff *segs = ERR_PTR(-EINVAL); 23 u16 mac_offset = skb->mac_header; 24 __be16 protocol = skb->protocol; 25 u16 mac_len = skb->mac_len; 26 int gre_offset, outer_hlen; 27 bool need_csum, gso_partial; 28 29 if (!skb->encapsulation) 30 goto out; 31 32 if (unlikely(tnl_hlen < sizeof(struct gre_base_hdr))) 33 goto out; 34 35 if (unlikely(!pskb_may_pull(skb, tnl_hlen))) 36 goto out; 37 38 /* setup inner skb. */ 39 skb->encapsulation = 0; 40 SKB_GSO_CB(skb)->encap_level = 0; 41 __skb_pull(skb, tnl_hlen); 42 skb_reset_mac_header(skb); 43 skb_set_network_header(skb, skb_inner_network_offset(skb)); 44 skb->mac_len = skb_inner_network_offset(skb); 45 skb->protocol = skb->inner_protocol; 46 47 need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_GRE_CSUM); 48 skb->encap_hdr_csum = need_csum; 49 50 features &= skb->dev->hw_enc_features; 51 52 /* segment inner packet. */ 53 segs = skb_mac_gso_segment(skb, features); 54 if (IS_ERR_OR_NULL(segs)) { 55 skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset, 56 mac_len); 57 goto out; 58 } 59 60 gso_partial = !!(skb_shinfo(segs)->gso_type & SKB_GSO_PARTIAL); 61 62 outer_hlen = skb_tnl_header_len(skb); 63 gre_offset = outer_hlen - tnl_hlen; 64 skb = segs; 65 do { 66 struct gre_base_hdr *greh; 67 __sum16 *pcsum; 68 69 /* Set up inner headers if we are offloading inner checksum */ 70 if (skb->ip_summed == CHECKSUM_PARTIAL) { 71 skb_reset_inner_headers(skb); 72 skb->encapsulation = 1; 73 } 74 75 skb->mac_len = mac_len; 76 skb->protocol = protocol; 77 78 __skb_push(skb, outer_hlen); 79 skb_reset_mac_header(skb); 80 skb_set_network_header(skb, mac_len); 81 skb_set_transport_header(skb, gre_offset); 82 83 if (!need_csum) 84 continue; 85 86 greh = (struct gre_base_hdr *)skb_transport_header(skb); 87 pcsum = (__sum16 *)(greh + 1); 88 89 if (gso_partial && skb_is_gso(skb)) { 90 unsigned int partial_adj; 91 92 /* Adjust checksum to account for the fact that 93 * the partial checksum is based on actual size 94 * whereas headers should be based on MSS size. 95 */ 96 partial_adj = skb->len + skb_headroom(skb) - 97 SKB_GSO_CB(skb)->data_offset - 98 skb_shinfo(skb)->gso_size; 99 *pcsum = ~csum_fold((__force __wsum)htonl(partial_adj)); 100 } else { 101 *pcsum = 0; 102 } 103 104 *(pcsum + 1) = 0; 105 *pcsum = gso_make_checksum(skb, 0); 106 } while ((skb = skb->next)); 107 out: 108 return segs; 109 } 110 111 static struct sk_buff *gre_gro_receive(struct list_head *head, 112 struct sk_buff *skb) 113 { 114 struct sk_buff *pp = NULL; 115 struct sk_buff *p; 116 const struct gre_base_hdr *greh; 117 unsigned int hlen, grehlen; 118 unsigned int off; 119 int flush = 1; 120 struct packet_offload *ptype; 121 __be16 type; 122 123 if (NAPI_GRO_CB(skb)->encap_mark) 124 goto out; 125 126 NAPI_GRO_CB(skb)->encap_mark = 1; 127 128 off = skb_gro_offset(skb); 129 hlen = off + sizeof(*greh); 130 greh = skb_gro_header_fast(skb, off); 131 if (skb_gro_header_hard(skb, hlen)) { 132 greh = skb_gro_header_slow(skb, hlen, off); 133 if (unlikely(!greh)) 134 goto out; 135 } 136 137 /* Only support version 0 and K (key), C (csum) flags. Note that 138 * although the support for the S (seq#) flag can be added easily 139 * for GRO, this is problematic for GSO hence can not be enabled 140 * here because a GRO pkt may end up in the forwarding path, thus 141 * requiring GSO support to break it up correctly. 142 */ 143 if ((greh->flags & ~(GRE_KEY|GRE_CSUM)) != 0) 144 goto out; 145 146 /* We can only support GRE_CSUM if we can track the location of 147 * the GRE header. In the case of FOU/GUE we cannot because the 148 * outer UDP header displaces the GRE header leaving us in a state 149 * of limbo. 150 */ 151 if ((greh->flags & GRE_CSUM) && NAPI_GRO_CB(skb)->is_fou) 152 goto out; 153 154 type = greh->protocol; 155 156 rcu_read_lock(); 157 ptype = gro_find_receive_by_type(type); 158 if (!ptype) 159 goto out_unlock; 160 161 grehlen = GRE_HEADER_SECTION; 162 163 if (greh->flags & GRE_KEY) 164 grehlen += GRE_HEADER_SECTION; 165 166 if (greh->flags & GRE_CSUM) 167 grehlen += GRE_HEADER_SECTION; 168 169 hlen = off + grehlen; 170 if (skb_gro_header_hard(skb, hlen)) { 171 greh = skb_gro_header_slow(skb, hlen, off); 172 if (unlikely(!greh)) 173 goto out_unlock; 174 } 175 176 /* Don't bother verifying checksum if we're going to flush anyway. */ 177 if ((greh->flags & GRE_CSUM) && !NAPI_GRO_CB(skb)->flush) { 178 if (skb_gro_checksum_simple_validate(skb)) 179 goto out_unlock; 180 181 skb_gro_checksum_try_convert(skb, IPPROTO_GRE, 0, 182 null_compute_pseudo); 183 } 184 185 list_for_each_entry(p, head, list) { 186 const struct gre_base_hdr *greh2; 187 188 if (!NAPI_GRO_CB(p)->same_flow) 189 continue; 190 191 /* The following checks are needed to ensure only pkts 192 * from the same tunnel are considered for aggregation. 193 * The criteria for "the same tunnel" includes: 194 * 1) same version (we only support version 0 here) 195 * 2) same protocol (we only support ETH_P_IP for now) 196 * 3) same set of flags 197 * 4) same key if the key field is present. 198 */ 199 greh2 = (struct gre_base_hdr *)(p->data + off); 200 201 if (greh2->flags != greh->flags || 202 greh2->protocol != greh->protocol) { 203 NAPI_GRO_CB(p)->same_flow = 0; 204 continue; 205 } 206 if (greh->flags & GRE_KEY) { 207 /* compare keys */ 208 if (*(__be32 *)(greh2+1) != *(__be32 *)(greh+1)) { 209 NAPI_GRO_CB(p)->same_flow = 0; 210 continue; 211 } 212 } 213 } 214 215 skb_gro_pull(skb, grehlen); 216 217 /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/ 218 skb_gro_postpull_rcsum(skb, greh, grehlen); 219 220 pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb); 221 flush = 0; 222 223 out_unlock: 224 rcu_read_unlock(); 225 out: 226 skb_gro_flush_final(skb, pp, flush); 227 228 return pp; 229 } 230 231 static int gre_gro_complete(struct sk_buff *skb, int nhoff) 232 { 233 struct gre_base_hdr *greh = (struct gre_base_hdr *)(skb->data + nhoff); 234 struct packet_offload *ptype; 235 unsigned int grehlen = sizeof(*greh); 236 int err = -ENOENT; 237 __be16 type; 238 239 skb->encapsulation = 1; 240 skb_shinfo(skb)->gso_type = SKB_GSO_GRE; 241 242 type = greh->protocol; 243 if (greh->flags & GRE_KEY) 244 grehlen += GRE_HEADER_SECTION; 245 246 if (greh->flags & GRE_CSUM) 247 grehlen += GRE_HEADER_SECTION; 248 249 rcu_read_lock(); 250 ptype = gro_find_complete_by_type(type); 251 if (ptype) 252 err = ptype->callbacks.gro_complete(skb, nhoff + grehlen); 253 254 rcu_read_unlock(); 255 256 skb_set_inner_mac_header(skb, nhoff + grehlen); 257 258 return err; 259 } 260 261 static const struct net_offload gre_offload = { 262 .callbacks = { 263 .gso_segment = gre_gso_segment, 264 .gro_receive = gre_gro_receive, 265 .gro_complete = gre_gro_complete, 266 }, 267 }; 268 269 static int __init gre_offload_init(void) 270 { 271 int err; 272 273 err = inet_add_offload(&gre_offload, IPPROTO_GRE); 274 #if IS_ENABLED(CONFIG_IPV6) 275 if (err) 276 return err; 277 278 err = inet6_add_offload(&gre_offload, IPPROTO_GRE); 279 if (err) 280 inet_del_offload(&gre_offload, IPPROTO_GRE); 281 #endif 282 283 return err; 284 } 285 device_initcall(gre_offload_init); 286