ila_common.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
  16. {
  17. struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
  18. if (p->locator_match.v64)
  19. return p->csum_diff;
  20. else
  21. return compute_csum_diff8((__be32 *)&iaddr->loc,
  22. (__be32 *)&p->locator);
  23. }
  24. static void ila_csum_do_neutral(struct ila_addr *iaddr,
  25. struct ila_params *p)
  26. {
  27. __sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3];
  28. __wsum diff, fval;
  29. /* Check if checksum adjust value has been cached */
  30. if (p->locator_match.v64) {
  31. diff = p->csum_diff;
  32. } else {
  33. diff = compute_csum_diff8((__be32 *)&p->locator,
  34. (__be32 *)iaddr);
  35. }
  36. fval = (__force __wsum)(ila_csum_neutral_set(iaddr->ident) ?
  37. CSUM_NEUTRAL_FLAG : ~CSUM_NEUTRAL_FLAG);
  38. diff = csum_add(diff, fval);
  39. *adjust = ~csum_fold(csum_add(diff, csum_unfold(*adjust)));
  40. /* Flip the csum-neutral bit. Either we are doing a SIR->ILA
  41. * translation with ILA_CSUM_NEUTRAL_MAP as the csum_method
  42. * and the C-bit is not set, or we are doing an ILA-SIR
  43. * tranlsation and the C-bit is set.
  44. */
  45. iaddr->ident.csum_neutral ^= 1;
  46. }
  47. static void ila_csum_adjust_transport(struct sk_buff *skb,
  48. struct ila_params *p)
  49. {
  50. __wsum diff;
  51. struct ipv6hdr *ip6h = ipv6_hdr(skb);
  52. struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
  53. size_t nhoff = sizeof(struct ipv6hdr);
  54. switch (ip6h->nexthdr) {
  55. case NEXTHDR_TCP:
  56. if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
  57. struct tcphdr *th = (struct tcphdr *)
  58. (skb_network_header(skb) + nhoff);
  59. diff = get_csum_diff(ip6h, p);
  60. inet_proto_csum_replace_by_diff(&th->check, skb,
  61. diff, true);
  62. }
  63. break;
  64. case NEXTHDR_UDP:
  65. if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) {
  66. struct udphdr *uh = (struct udphdr *)
  67. (skb_network_header(skb) + nhoff);
  68. if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
  69. diff = get_csum_diff(ip6h, p);
  70. inet_proto_csum_replace_by_diff(&uh->check, skb,
  71. diff, true);
  72. if (!uh->check)
  73. uh->check = CSUM_MANGLED_0;
  74. }
  75. }
  76. break;
  77. case NEXTHDR_ICMP:
  78. if (likely(pskb_may_pull(skb,
  79. nhoff + sizeof(struct icmp6hdr)))) {
  80. struct icmp6hdr *ih = (struct icmp6hdr *)
  81. (skb_network_header(skb) + nhoff);
  82. diff = get_csum_diff(ip6h, p);
  83. inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
  84. diff, true);
  85. }
  86. break;
  87. }
  88. /* Now change destination address */
  89. iaddr->loc = p->locator;
  90. }
  91. void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p,
  92. bool set_csum_neutral)
  93. {
  94. struct ipv6hdr *ip6h = ipv6_hdr(skb);
  95. struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
  96. /* First deal with the transport checksum */
  97. if (ila_csum_neutral_set(iaddr->ident)) {
  98. /* C-bit is set in the locator indicating that this
  99. * is a locator being translated to a SIR address.
  100. * Perform (receiver) checksum-neutral translation.
  101. */
  102. if (!set_csum_neutral)
  103. ila_csum_do_neutral(iaddr, p);
  104. } else {
  105. switch (p->csum_mode) {
  106. case ILA_CSUM_ADJUST_TRANSPORT:
  107. ila_csum_adjust_transport(skb, p);
  108. break;
  109. case ILA_CSUM_NEUTRAL_MAP:
  110. ila_csum_do_neutral(iaddr, p);
  111. break;
  112. case ILA_CSUM_NO_ACTION:
  113. break;
  114. }
  115. }
  116. /* Now change destination address */
  117. iaddr->loc = p->locator;
  118. }
  119. void ila_init_saved_csum(struct ila_params *p)
  120. {
  121. if (!p->locator_match.v64)
  122. return;
  123. p->csum_diff = compute_csum_diff8(
  124. (__be32 *)&p->locator,
  125. (__be32 *)&p->locator_match);
  126. }
  127. static int __init ila_init(void)
  128. {
  129. int ret;
  130. ret = ila_lwt_init();
  131. if (ret)
  132. goto fail_lwt;
  133. ret = ila_xlat_init();
  134. if (ret)
  135. goto fail_xlat;
  136. return 0;
  137. fail_xlat:
  138. ila_lwt_fini();
  139. fail_lwt:
  140. return ret;
  141. }
  142. static void __exit ila_fini(void)
  143. {
  144. ila_xlat_fini();
  145. ila_lwt_fini();
  146. }
  147. module_init(ila_init);
  148. module_exit(ila_fini);
  149. MODULE_AUTHOR("Tom Herbert <tom@herbertland.com>");
  150. MODULE_LICENSE("GPL");