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 * UDPv4 GSO support 11 */ 12 13 #include <linux/skbuff.h> 14 #include <net/udp.h> 15 #include <net/protocol.h> 16 17 static int udp4_ufo_send_check(struct sk_buff *skb) 18 { 19 if (!pskb_may_pull(skb, sizeof(struct udphdr))) 20 return -EINVAL; 21 22 if (likely(!skb->encapsulation)) { 23 const struct iphdr *iph; 24 struct udphdr *uh; 25 26 iph = ip_hdr(skb); 27 uh = udp_hdr(skb); 28 29 uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, 30 IPPROTO_UDP, 0); 31 skb->csum_start = skb_transport_header(skb) - skb->head; 32 skb->csum_offset = offsetof(struct udphdr, check); 33 skb->ip_summed = CHECKSUM_PARTIAL; 34 } 35 36 return 0; 37 } 38 39 static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, 40 netdev_features_t features) 41 { 42 struct sk_buff *segs = ERR_PTR(-EINVAL); 43 unsigned int mss; 44 45 mss = skb_shinfo(skb)->gso_size; 46 if (unlikely(skb->len <= mss)) 47 goto out; 48 49 if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { 50 /* Packet is from an untrusted source, reset gso_segs. */ 51 int type = skb_shinfo(skb)->gso_type; 52 53 if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | 54 SKB_GSO_UDP_TUNNEL | 55 SKB_GSO_GRE | SKB_GSO_MPLS) || 56 !(type & (SKB_GSO_UDP)))) 57 goto out; 58 59 skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); 60 61 segs = NULL; 62 goto out; 63 } 64 65 /* Fragment the skb. IP headers of the fragments are updated in 66 * inet_gso_segment() 67 */ 68 if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) 69 segs = skb_udp_tunnel_segment(skb, features); 70 else { 71 int offset; 72 __wsum csum; 73 74 /* Do software UFO. Complete and fill in the UDP checksum as 75 * HW cannot do checksum of UDP packets sent as multiple 76 * IP fragments. 77 */ 78 offset = skb_checksum_start_offset(skb); 79 csum = skb_checksum(skb, offset, skb->len - offset, 0); 80 offset += skb->csum_offset; 81 *(__sum16 *)(skb->data + offset) = csum_fold(csum); 82 skb->ip_summed = CHECKSUM_NONE; 83 84 segs = skb_segment(skb, features); 85 } 86 out: 87 return segs; 88 } 89 90 static const struct net_offload udpv4_offload = { 91 .callbacks = { 92 .gso_send_check = udp4_ufo_send_check, 93 .gso_segment = udp4_ufo_fragment, 94 }, 95 }; 96 97 int __init udpv4_offload_init(void) 98 { 99 return inet_add_offload(&udpv4_offload, IPPROTO_UDP); 100 } 101