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