nhc.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*
  2. * 6LoWPAN next header compression
  3. *
  4. *
  5. * Authors:
  6. * Alexander Aring <aar@pengutronix.de>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version
  11. * 2 of the License, or (at your option) any later version.
  12. */
  13. #include <linux/netdevice.h>
  14. #include <net/ipv6.h>
  15. #include "nhc.h"
  16. static struct rb_root rb_root = RB_ROOT;
  17. static struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX];
  18. static DEFINE_SPINLOCK(lowpan_nhc_lock);
  19. static int lowpan_nhc_insert(struct lowpan_nhc *nhc)
  20. {
  21. struct rb_node **new = &rb_root.rb_node, *parent = NULL;
  22. /* Figure out where to put new node */
  23. while (*new) {
  24. struct lowpan_nhc *this = container_of(*new, struct lowpan_nhc,
  25. node);
  26. int result, len_dif, len;
  27. len_dif = nhc->idlen - this->idlen;
  28. if (nhc->idlen < this->idlen)
  29. len = nhc->idlen;
  30. else
  31. len = this->idlen;
  32. result = memcmp(nhc->id, this->id, len);
  33. if (!result)
  34. result = len_dif;
  35. parent = *new;
  36. if (result < 0)
  37. new = &((*new)->rb_left);
  38. else if (result > 0)
  39. new = &((*new)->rb_right);
  40. else
  41. return -EEXIST;
  42. }
  43. /* Add new node and rebalance tree. */
  44. rb_link_node(&nhc->node, parent, new);
  45. rb_insert_color(&nhc->node, &rb_root);
  46. return 0;
  47. }
  48. static void lowpan_nhc_remove(struct lowpan_nhc *nhc)
  49. {
  50. rb_erase(&nhc->node, &rb_root);
  51. }
  52. static struct lowpan_nhc *lowpan_nhc_by_nhcid(const struct sk_buff *skb)
  53. {
  54. struct rb_node *node = rb_root.rb_node;
  55. const u8 *nhcid_skb_ptr = skb->data;
  56. while (node) {
  57. struct lowpan_nhc *nhc = container_of(node, struct lowpan_nhc,
  58. node);
  59. u8 nhcid_skb_ptr_masked[LOWPAN_NHC_MAX_ID_LEN];
  60. int result, i;
  61. if (nhcid_skb_ptr + nhc->idlen > skb->data + skb->len)
  62. return NULL;
  63. /* copy and mask afterwards the nhid value from skb */
  64. memcpy(nhcid_skb_ptr_masked, nhcid_skb_ptr, nhc->idlen);
  65. for (i = 0; i < nhc->idlen; i++)
  66. nhcid_skb_ptr_masked[i] &= nhc->idmask[i];
  67. result = memcmp(nhcid_skb_ptr_masked, nhc->id, nhc->idlen);
  68. if (result < 0)
  69. node = node->rb_left;
  70. else if (result > 0)
  71. node = node->rb_right;
  72. else
  73. return nhc;
  74. }
  75. return NULL;
  76. }
  77. int lowpan_nhc_check_compression(struct sk_buff *skb,
  78. const struct ipv6hdr *hdr, u8 **hc_ptr,
  79. u8 *iphc0)
  80. {
  81. struct lowpan_nhc *nhc;
  82. spin_lock_bh(&lowpan_nhc_lock);
  83. nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
  84. if (nhc && nhc->compress)
  85. *iphc0 |= LOWPAN_IPHC_NH_C;
  86. else
  87. lowpan_push_hc_data(hc_ptr, &hdr->nexthdr,
  88. sizeof(hdr->nexthdr));
  89. spin_unlock_bh(&lowpan_nhc_lock);
  90. return 0;
  91. }
  92. int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
  93. u8 **hc_ptr)
  94. {
  95. int ret;
  96. struct lowpan_nhc *nhc;
  97. spin_lock_bh(&lowpan_nhc_lock);
  98. nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
  99. /* check if the nhc module was removed in unlocked part.
  100. * TODO: this is a workaround we should prevent unloading
  101. * of nhc modules while unlocked part, this will always drop
  102. * the lowpan packet but it's very unlikely.
  103. *
  104. * Solution isn't easy because we need to decide at
  105. * lowpan_nhc_check_compression if we do a compression or not.
  106. * Because the inline data which is added to skb, we can't move this
  107. * handling.
  108. */
  109. if (unlikely(!nhc || !nhc->compress)) {
  110. ret = -EINVAL;
  111. goto out;
  112. }
  113. /* In the case of RAW sockets the transport header is not set by
  114. * the ip6 stack so we must set it ourselves
  115. */
  116. if (skb->transport_header == skb->network_header)
  117. skb_set_transport_header(skb, sizeof(struct ipv6hdr));
  118. ret = nhc->compress(skb, hc_ptr);
  119. if (ret < 0)
  120. goto out;
  121. /* skip the transport header */
  122. skb_pull(skb, nhc->nexthdrlen);
  123. out:
  124. spin_unlock_bh(&lowpan_nhc_lock);
  125. return ret;
  126. }
  127. int lowpan_nhc_do_uncompression(struct sk_buff *skb, struct net_device *dev,
  128. struct ipv6hdr *hdr)
  129. {
  130. struct lowpan_nhc *nhc;
  131. int ret;
  132. spin_lock_bh(&lowpan_nhc_lock);
  133. nhc = lowpan_nhc_by_nhcid(skb);
  134. if (nhc) {
  135. if (nhc->uncompress) {
  136. ret = nhc->uncompress(skb, sizeof(struct ipv6hdr) +
  137. nhc->nexthdrlen);
  138. if (ret < 0) {
  139. spin_unlock_bh(&lowpan_nhc_lock);
  140. return ret;
  141. }
  142. } else {
  143. spin_unlock_bh(&lowpan_nhc_lock);
  144. netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
  145. nhc->name);
  146. return -ENOTSUPP;
  147. }
  148. } else {
  149. spin_unlock_bh(&lowpan_nhc_lock);
  150. netdev_warn(dev, "received unknown nhc id which was not found.\n");
  151. return -ENOENT;
  152. }
  153. hdr->nexthdr = nhc->nexthdr;
  154. skb_reset_transport_header(skb);
  155. raw_dump_table(__func__, "raw transport header dump",
  156. skb_transport_header(skb), nhc->nexthdrlen);
  157. spin_unlock_bh(&lowpan_nhc_lock);
  158. return 0;
  159. }
  160. int lowpan_nhc_add(struct lowpan_nhc *nhc)
  161. {
  162. int ret;
  163. if (!nhc->idlen || !nhc->idsetup)
  164. return -EINVAL;
  165. WARN_ONCE(nhc->idlen > LOWPAN_NHC_MAX_ID_LEN,
  166. "LOWPAN_NHC_MAX_ID_LEN should be updated to %zd.\n",
  167. nhc->idlen);
  168. nhc->idsetup(nhc);
  169. spin_lock_bh(&lowpan_nhc_lock);
  170. if (lowpan_nexthdr_nhcs[nhc->nexthdr]) {
  171. ret = -EEXIST;
  172. goto out;
  173. }
  174. ret = lowpan_nhc_insert(nhc);
  175. if (ret < 0)
  176. goto out;
  177. lowpan_nexthdr_nhcs[nhc->nexthdr] = nhc;
  178. out:
  179. spin_unlock_bh(&lowpan_nhc_lock);
  180. return ret;
  181. }
  182. EXPORT_SYMBOL(lowpan_nhc_add);
  183. void lowpan_nhc_del(struct lowpan_nhc *nhc)
  184. {
  185. spin_lock_bh(&lowpan_nhc_lock);
  186. lowpan_nhc_remove(nhc);
  187. lowpan_nexthdr_nhcs[nhc->nexthdr] = NULL;
  188. spin_unlock_bh(&lowpan_nhc_lock);
  189. synchronize_net();
  190. }
  191. EXPORT_SYMBOL(lowpan_nhc_del);