tcpv6_offload.c 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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. * TCPv6 GSO/GRO support
  11. */
  12. #include <linux/skbuff.h>
  13. #include <net/protocol.h>
  14. #include <net/tcp.h>
  15. #include <net/ip6_checksum.h>
  16. #include "ip6_offload.h"
  17. static int tcp_v6_gso_send_check(struct sk_buff *skb)
  18. {
  19. const struct ipv6hdr *ipv6h;
  20. struct tcphdr *th;
  21. if (!pskb_may_pull(skb, sizeof(*th)))
  22. return -EINVAL;
  23. ipv6h = ipv6_hdr(skb);
  24. th = tcp_hdr(skb);
  25. th->check = 0;
  26. skb->ip_summed = CHECKSUM_PARTIAL;
  27. __tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
  28. return 0;
  29. }
  30. static struct sk_buff **tcp6_gro_receive(struct sk_buff **head,
  31. struct sk_buff *skb)
  32. {
  33. const struct ipv6hdr *iph = skb_gro_network_header(skb);
  34. __wsum wsum;
  35. __sum16 sum;
  36. switch (skb->ip_summed) {
  37. case CHECKSUM_COMPLETE:
  38. if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
  39. skb->csum)) {
  40. skb->ip_summed = CHECKSUM_UNNECESSARY;
  41. break;
  42. }
  43. flush:
  44. NAPI_GRO_CB(skb)->flush = 1;
  45. return NULL;
  46. case CHECKSUM_NONE:
  47. wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr,
  48. skb_gro_len(skb),
  49. IPPROTO_TCP, 0));
  50. sum = csum_fold(skb_checksum(skb,
  51. skb_gro_offset(skb),
  52. skb_gro_len(skb),
  53. wsum));
  54. if (sum)
  55. goto flush;
  56. skb->ip_summed = CHECKSUM_UNNECESSARY;
  57. break;
  58. }
  59. return tcp_gro_receive(head, skb);
  60. }
  61. static int tcp6_gro_complete(struct sk_buff *skb)
  62. {
  63. const struct ipv6hdr *iph = ipv6_hdr(skb);
  64. struct tcphdr *th = tcp_hdr(skb);
  65. th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
  66. &iph->saddr, &iph->daddr, 0);
  67. skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
  68. return tcp_gro_complete(skb);
  69. }
  70. static const struct net_offload tcpv6_offload = {
  71. .callbacks = {
  72. .gso_send_check = tcp_v6_gso_send_check,
  73. .gso_segment = tcp_tso_segment,
  74. .gro_receive = tcp6_gro_receive,
  75. .gro_complete = tcp6_gro_complete,
  76. },
  77. };
  78. int __init tcpv6_offload_init(void)
  79. {
  80. return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
  81. }