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_IPIP | 56 SKB_GSO_GRE | SKB_GSO_MPLS) || 57 !(type & (SKB_GSO_UDP)))) 58 goto out; 59 60 skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); 61 62 segs = NULL; 63 goto out; 64 } 65 66 /* Fragment the skb. IP headers of the fragments are updated in 67 * inet_gso_segment() 68 */ 69 if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) 70 segs = skb_udp_tunnel_segment(skb, features); 71 else { 72 int offset; 73 __wsum csum; 74 75 /* Do software UFO. Complete and fill in the UDP checksum as 76 * HW cannot do checksum of UDP packets sent as multiple 77 * IP fragments. 78 */ 79 offset = skb_checksum_start_offset(skb); 80 csum = skb_checksum(skb, offset, skb->len - offset, 0); 81 offset += skb->csum_offset; 82 *(__sum16 *)(skb->data + offset) = csum_fold(csum); 83 skb->ip_summed = CHECKSUM_NONE; 84 85 segs = skb_segment(skb, features); 86 } 87 out: 88 return segs; 89 } 90 91 static const struct net_offload udpv4_offload = { 92 .callbacks = { 93 .gso_send_check = udp4_ufo_send_check, 94 .gso_segment = udp4_ufo_fragment, 95 }, 96 }; 97 98 int __init udpv4_offload_init(void) 99 { 100 return inet_add_offload(&udpv4_offload, IPPROTO_UDP); 101 } 102