udp_offload.c 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /*
  2. * IPV6 GSO/GRO offload support
  3. * Linux INET6 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. * UDPv6 GSO support
  11. */
  12. #include <linux/skbuff.h>
  13. #include <linux/netdevice.h>
  14. #include <net/protocol.h>
  15. #include <net/ipv6.h>
  16. #include <net/udp.h>
  17. #include <net/ip6_checksum.h>
  18. #include "ip6_offload.h"
  19. static struct sk_buff *udp6_tunnel_segment(struct sk_buff *skb,
  20. netdev_features_t features)
  21. {
  22. struct sk_buff *segs = ERR_PTR(-EINVAL);
  23. if (skb->encapsulation && skb_shinfo(skb)->gso_type &
  24. (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))
  25. segs = skb_udp_tunnel_segment(skb, features, true);
  26. return segs;
  27. }
  28. static struct sk_buff **udp6_gro_receive(struct sk_buff **head,
  29. struct sk_buff *skb)
  30. {
  31. struct udphdr *uh = udp_gro_udphdr(skb);
  32. if (unlikely(!uh))
  33. goto flush;
  34. /* Don't bother verifying checksum if we're going to flush anyway. */
  35. if (NAPI_GRO_CB(skb)->flush)
  36. goto skip;
  37. if (skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check,
  38. ip6_gro_compute_pseudo))
  39. goto flush;
  40. else if (uh->check)
  41. skb_gro_checksum_try_convert(skb, IPPROTO_UDP, uh->check,
  42. ip6_gro_compute_pseudo);
  43. skip:
  44. NAPI_GRO_CB(skb)->is_ipv6 = 1;
  45. return udp_gro_receive(head, skb, uh, udp6_lib_lookup_skb);
  46. flush:
  47. NAPI_GRO_CB(skb)->flush = 1;
  48. return NULL;
  49. }
  50. static int udp6_gro_complete(struct sk_buff *skb, int nhoff)
  51. {
  52. const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
  53. struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
  54. if (uh->check) {
  55. skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM;
  56. uh->check = ~udp_v6_check(skb->len - nhoff, &ipv6h->saddr,
  57. &ipv6h->daddr, 0);
  58. } else {
  59. skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
  60. }
  61. return udp_gro_complete(skb, nhoff, udp6_lib_lookup_skb);
  62. }
  63. static const struct net_offload udpv6_offload = {
  64. .callbacks = {
  65. .gso_segment = udp6_tunnel_segment,
  66. .gro_receive = udp6_gro_receive,
  67. .gro_complete = udp6_gro_complete,
  68. },
  69. };
  70. int udpv6_offload_init(void)
  71. {
  72. return inet6_add_offload(&udpv6_offload, IPPROTO_UDP);
  73. }
  74. int udpv6_offload_exit(void)
  75. {
  76. return inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
  77. }