ila_common.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #include <linux/errno.h>
  2. #include <linux/ip.h>
  3. #include <linux/kernel.h>
  4. #include <linux/module.h>
  5. #include <linux/skbuff.h>
  6. #include <linux/socket.h>
  7. #include <linux/types.h>
  8. #include <net/checksum.h>
  9. #include <net/ip.h>
  10. #include <net/ip6_fib.h>
  11. #include <net/lwtunnel.h>
  12. #include <net/protocol.h>
  13. #include <uapi/linux/ila.h>
  14. #include "ila.h"
  15. void ila_init_saved_csum(struct ila_params *p)
  16. {
  17. if (!p->locator_match.v64)
  18. return;
  19. p->csum_diff = compute_csum_diff8(
  20. (__be32 *)&p->locator,
  21. (__be32 *)&p->locator_match);
  22. }
  23. static __wsum get_csum_diff_iaddr(struct ila_addr *iaddr, struct ila_params *p)
  24. {
  25. if (p->locator_match.v64)
  26. return p->csum_diff;
  27. else
  28. return compute_csum_diff8((__be32 *)&p->locator,
  29. (__be32 *)&iaddr->loc);
  30. }
  31. static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
  32. {
  33. return get_csum_diff_iaddr(ila_a2i(&ip6h->daddr), p);
  34. }
  35. static void ila_csum_do_neutral_fmt(struct ila_addr *iaddr,
  36. struct ila_params *p)
  37. {
  38. __sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3];
  39. __wsum diff, fval;
  40. diff = get_csum_diff_iaddr(iaddr, p);
  41. fval = (__force __wsum)(ila_csum_neutral_set(iaddr->ident) ?
  42. CSUM_NEUTRAL_FLAG : ~CSUM_NEUTRAL_FLAG);
  43. diff = csum_add(diff, fval);
  44. *adjust = ~csum_fold(csum_add(diff, csum_unfold(*adjust)));
  45. /* Flip the csum-neutral bit. Either we are doing a SIR->ILA
  46. * translation with ILA_CSUM_NEUTRAL_MAP as the csum_method
  47. * and the C-bit is not set, or we are doing an ILA-SIR
  48. * tranlsation and the C-bit is set.
  49. */
  50. iaddr->ident.csum_neutral ^= 1;
  51. }
  52. static void ila_csum_do_neutral_nofmt(struct ila_addr *iaddr,
  53. struct ila_params *p)
  54. {
  55. __sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3];
  56. __wsum diff;
  57. diff = get_csum_diff_iaddr(iaddr, p);
  58. *adjust = ~csum_fold(csum_add(diff, csum_unfold(*adjust)));
  59. }
  60. static void ila_csum_adjust_transport(struct sk_buff *skb,
  61. struct ila_params *p)
  62. {
  63. size_t nhoff = sizeof(struct ipv6hdr);
  64. struct ipv6hdr *ip6h = ipv6_hdr(skb);
  65. __wsum diff;
  66. switch (ip6h->nexthdr) {
  67. case NEXTHDR_TCP:
  68. if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
  69. struct tcphdr *th = (struct tcphdr *)
  70. (skb_network_header(skb) + nhoff);
  71. diff = get_csum_diff(ip6h, p);
  72. inet_proto_csum_replace_by_diff(&th->check, skb,
  73. diff, true);
  74. }
  75. break;
  76. case NEXTHDR_UDP:
  77. if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) {
  78. struct udphdr *uh = (struct udphdr *)
  79. (skb_network_header(skb) + nhoff);
  80. if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
  81. diff = get_csum_diff(ip6h, p);
  82. inet_proto_csum_replace_by_diff(&uh->check, skb,
  83. diff, true);
  84. if (!uh->check)
  85. uh->check = CSUM_MANGLED_0;
  86. }
  87. }
  88. break;
  89. case NEXTHDR_ICMP:
  90. if (likely(pskb_may_pull(skb,
  91. nhoff + sizeof(struct icmp6hdr)))) {
  92. struct icmp6hdr *ih = (struct icmp6hdr *)
  93. (skb_network_header(skb) + nhoff);
  94. diff = get_csum_diff(ip6h, p);
  95. inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
  96. diff, true);
  97. }
  98. break;
  99. }
  100. }
  101. void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p,
  102. bool sir2ila)
  103. {
  104. struct ipv6hdr *ip6h = ipv6_hdr(skb);
  105. struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
  106. switch (p->csum_mode) {
  107. case ILA_CSUM_ADJUST_TRANSPORT:
  108. ila_csum_adjust_transport(skb, p);
  109. break;
  110. case ILA_CSUM_NEUTRAL_MAP:
  111. if (sir2ila) {
  112. if (WARN_ON(ila_csum_neutral_set(iaddr->ident))) {
  113. /* Checksum flag should never be
  114. * set in a formatted SIR address.
  115. */
  116. break;
  117. }
  118. } else if (!ila_csum_neutral_set(iaddr->ident)) {
  119. /* ILA to SIR translation and C-bit isn't
  120. * set so we're good.
  121. */
  122. break;
  123. }
  124. ila_csum_do_neutral_fmt(iaddr, p);
  125. break;
  126. case ILA_CSUM_NEUTRAL_MAP_AUTO:
  127. ila_csum_do_neutral_nofmt(iaddr, p);
  128. break;
  129. case ILA_CSUM_NO_ACTION:
  130. break;
  131. }
  132. /* Now change destination address */
  133. iaddr->loc = p->locator;
  134. }