gre_offload.c (438e38fadca2f6e57eeecc08326c8a95758594d4) | gre_offload.c (bf5a755f5e9186406bbf50f4087100af5bd68e40) |
---|---|
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. --- 102 unchanged lines hidden (view full) --- 111 skb_set_network_header(skb, mac_len); 112 skb->mac_len = mac_len; 113 skb->protocol = protocol; 114 } while ((skb = skb->next)); 115out: 116 return segs; 117} 118 | 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. --- 102 unchanged lines hidden (view full) --- 111 skb_set_network_header(skb, mac_len); 112 skb->mac_len = mac_len; 113 skb->protocol = protocol; 114 } while ((skb = skb->next)); 115out: 116 return segs; 117} 118 |
119/* Compute the whole skb csum in s/w and store it, then verify GRO csum 120 * starting from gro_offset. 121 */ 122static __sum16 gro_skb_checksum(struct sk_buff *skb) 123{ 124 __sum16 sum; 125 126 skb->csum = skb_checksum(skb, 0, skb->len, 0); 127 NAPI_GRO_CB(skb)->csum = csum_sub(skb->csum, 128 csum_partial(skb->data, skb_gro_offset(skb), 0)); 129 sum = csum_fold(NAPI_GRO_CB(skb)->csum); 130 if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) { 131 if (unlikely(!sum)) 132 netdev_rx_csum_fault(skb->dev); 133 } else 134 skb->ip_summed = CHECKSUM_COMPLETE; 135 136 return sum; 137} 138 139static struct sk_buff **gre_gro_receive(struct sk_buff **head, 140 struct sk_buff *skb) 141{ 142 struct sk_buff **pp = NULL; 143 struct sk_buff *p; 144 const struct gre_base_hdr *greh; 145 unsigned int hlen, grehlen; 146 unsigned int off; 147 int flush = 1; 148 struct packet_offload *ptype; 149 __be16 type; 150 151 off = skb_gro_offset(skb); 152 hlen = off + sizeof(*greh); 153 greh = skb_gro_header_fast(skb, off); 154 if (skb_gro_header_hard(skb, hlen)) { 155 greh = skb_gro_header_slow(skb, hlen, off); 156 if (unlikely(!greh)) 157 goto out; 158 } 159 160 /* Only support version 0 and K (key), C (csum) flags. Note that 161 * although the support for the S (seq#) flag can be added easily 162 * for GRO, this is problematic for GSO hence can not be enabled 163 * here because a GRO pkt may end up in the forwarding path, thus 164 * requiring GSO support to break it up correctly. 165 */ 166 if ((greh->flags & ~(GRE_KEY|GRE_CSUM)) != 0) 167 goto out; 168 169 type = greh->protocol; 170 171 rcu_read_lock(); 172 ptype = gro_find_receive_by_type(type); 173 if (ptype == NULL) 174 goto out_unlock; 175 176 grehlen = GRE_HEADER_SECTION; 177 178 if (greh->flags & GRE_KEY) 179 grehlen += GRE_HEADER_SECTION; 180 181 if (greh->flags & GRE_CSUM) 182 grehlen += GRE_HEADER_SECTION; 183 184 hlen = off + grehlen; 185 if (skb_gro_header_hard(skb, hlen)) { 186 greh = skb_gro_header_slow(skb, hlen, off); 187 if (unlikely(!greh)) 188 goto out_unlock; 189 } 190 if (greh->flags & GRE_CSUM) { /* Need to verify GRE csum first */ 191 __sum16 csum = 0; 192 193 if (skb->ip_summed == CHECKSUM_COMPLETE) 194 csum = csum_fold(NAPI_GRO_CB(skb)->csum); 195 /* Don't trust csum error calculated/reported by h/w */ 196 if (skb->ip_summed == CHECKSUM_NONE || csum != 0) 197 csum = gro_skb_checksum(skb); 198 199 /* GRE CSUM is the 1's complement of the 1's complement sum 200 * of the GRE hdr plus payload so it should add up to 0xffff 201 * (and 0 after csum_fold()) just like the IPv4 hdr csum. 202 */ 203 if (csum) 204 goto out_unlock; 205 } 206 flush = 0; 207 208 for (p = *head; p; p = p->next) { 209 const struct gre_base_hdr *greh2; 210 211 if (!NAPI_GRO_CB(p)->same_flow) 212 continue; 213 214 /* The following checks are needed to ensure only pkts 215 * from the same tunnel are considered for aggregation. 216 * The criteria for "the same tunnel" includes: 217 * 1) same version (we only support version 0 here) 218 * 2) same protocol (we only support ETH_P_IP for now) 219 * 3) same set of flags 220 * 4) same key if the key field is present. 221 */ 222 greh2 = (struct gre_base_hdr *)(p->data + off); 223 224 if (greh2->flags != greh->flags || 225 greh2->protocol != greh->protocol) { 226 NAPI_GRO_CB(p)->same_flow = 0; 227 continue; 228 } 229 if (greh->flags & GRE_KEY) { 230 /* compare keys */ 231 if (*(__be32 *)(greh2+1) != *(__be32 *)(greh+1)) { 232 NAPI_GRO_CB(p)->same_flow = 0; 233 continue; 234 } 235 } 236 } 237 238 skb_gro_pull(skb, grehlen); 239 240 /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/ 241 skb_gro_postpull_rcsum(skb, greh, grehlen); 242 243 pp = ptype->callbacks.gro_receive(head, skb); 244 245out_unlock: 246 rcu_read_unlock(); 247out: 248 NAPI_GRO_CB(skb)->flush |= flush; 249 250 return pp; 251} 252 253int gre_gro_complete(struct sk_buff *skb, int nhoff) 254{ 255 struct gre_base_hdr *greh = (struct gre_base_hdr *)(skb->data + nhoff); 256 struct packet_offload *ptype; 257 unsigned int grehlen = sizeof(*greh); 258 int err = -ENOENT; 259 __be16 type; 260 261 type = greh->protocol; 262 if (greh->flags & GRE_KEY) 263 grehlen += GRE_HEADER_SECTION; 264 265 if (greh->flags & GRE_CSUM) 266 grehlen += GRE_HEADER_SECTION; 267 268 rcu_read_lock(); 269 ptype = gro_find_complete_by_type(type); 270 if (ptype != NULL) 271 err = ptype->callbacks.gro_complete(skb, nhoff + grehlen); 272 273 rcu_read_unlock(); 274 return err; 275} 276 |
|
119static const struct net_offload gre_offload = { 120 .callbacks = { 121 .gso_send_check = gre_gso_send_check, 122 .gso_segment = gre_gso_segment, | 277static const struct net_offload gre_offload = { 278 .callbacks = { 279 .gso_send_check = gre_gso_send_check, 280 .gso_segment = gre_gso_segment, |
281 .gro_receive = gre_gro_receive, 282 .gro_complete = gre_gro_complete, |
|
123 }, 124}; 125 126static int __init gre_offload_init(void) 127{ 128 return inet_add_offload(&gre_offload, IPPROTO_GRE); 129} 130 131static void __exit gre_offload_exit(void) 132{ 133 inet_del_offload(&gre_offload, IPPROTO_GRE); 134} 135 136module_init(gre_offload_init); 137module_exit(gre_offload_exit); | 283 }, 284}; 285 286static int __init gre_offload_init(void) 287{ 288 return inet_add_offload(&gre_offload, IPPROTO_GRE); 289} 290 291static void __exit gre_offload_exit(void) 292{ 293 inet_del_offload(&gre_offload, IPPROTO_GRE); 294} 295 296module_init(gre_offload_init); 297module_exit(gre_offload_exit); |