nhc.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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. {
  80. struct lowpan_nhc *nhc;
  81. int ret = 0;
  82. spin_lock_bh(&lowpan_nhc_lock);
  83. nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
  84. if (!(nhc && nhc->compress))
  85. ret = -ENOENT;
  86. spin_unlock_bh(&lowpan_nhc_lock);
  87. return ret;
  88. }
  89. int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
  90. u8 **hc_ptr)
  91. {
  92. int ret;
  93. struct lowpan_nhc *nhc;
  94. spin_lock_bh(&lowpan_nhc_lock);
  95. nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
  96. /* check if the nhc module was removed in unlocked part.
  97. * TODO: this is a workaround we should prevent unloading
  98. * of nhc modules while unlocked part, this will always drop
  99. * the lowpan packet but it's very unlikely.
  100. *
  101. * Solution isn't easy because we need to decide at
  102. * lowpan_nhc_check_compression if we do a compression or not.
  103. * Because the inline data which is added to skb, we can't move this
  104. * handling.
  105. */
  106. if (unlikely(!nhc || !nhc->compress)) {
  107. ret = -EINVAL;
  108. goto out;
  109. }
  110. /* In the case of RAW sockets the transport header is not set by
  111. * the ip6 stack so we must set it ourselves
  112. */
  113. if (skb->transport_header == skb->network_header)
  114. skb_set_transport_header(skb, sizeof(struct ipv6hdr));
  115. ret = nhc->compress(skb, hc_ptr);
  116. if (ret < 0)
  117. goto out;
  118. /* skip the transport header */
  119. skb_pull(skb, nhc->nexthdrlen);
  120. out:
  121. spin_unlock_bh(&lowpan_nhc_lock);
  122. return ret;
  123. }
  124. int lowpan_nhc_do_uncompression(struct sk_buff *skb,
  125. const struct net_device *dev,
  126. struct ipv6hdr *hdr)
  127. {
  128. struct lowpan_nhc *nhc;
  129. int ret;
  130. spin_lock_bh(&lowpan_nhc_lock);
  131. nhc = lowpan_nhc_by_nhcid(skb);
  132. if (nhc) {
  133. if (nhc->uncompress) {
  134. ret = nhc->uncompress(skb, sizeof(struct ipv6hdr) +
  135. nhc->nexthdrlen);
  136. if (ret < 0) {
  137. spin_unlock_bh(&lowpan_nhc_lock);
  138. return ret;
  139. }
  140. } else {
  141. spin_unlock_bh(&lowpan_nhc_lock);
  142. netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
  143. nhc->name);
  144. return -ENOTSUPP;
  145. }
  146. } else {
  147. spin_unlock_bh(&lowpan_nhc_lock);
  148. netdev_warn(dev, "received unknown nhc id which was not found.\n");
  149. return -ENOENT;
  150. }
  151. hdr->nexthdr = nhc->nexthdr;
  152. skb_reset_transport_header(skb);
  153. raw_dump_table(__func__, "raw transport header dump",
  154. skb_transport_header(skb), nhc->nexthdrlen);
  155. spin_unlock_bh(&lowpan_nhc_lock);
  156. return 0;
  157. }
  158. int lowpan_nhc_add(struct lowpan_nhc *nhc)
  159. {
  160. int ret;
  161. if (!nhc->idlen || !nhc->idsetup)
  162. return -EINVAL;
  163. WARN_ONCE(nhc->idlen > LOWPAN_NHC_MAX_ID_LEN,
  164. "LOWPAN_NHC_MAX_ID_LEN should be updated to %zd.\n",
  165. nhc->idlen);
  166. nhc->idsetup(nhc);
  167. spin_lock_bh(&lowpan_nhc_lock);
  168. if (lowpan_nexthdr_nhcs[nhc->nexthdr]) {
  169. ret = -EEXIST;
  170. goto out;
  171. }
  172. ret = lowpan_nhc_insert(nhc);
  173. if (ret < 0)
  174. goto out;
  175. lowpan_nexthdr_nhcs[nhc->nexthdr] = nhc;
  176. out:
  177. spin_unlock_bh(&lowpan_nhc_lock);
  178. return ret;
  179. }
  180. EXPORT_SYMBOL(lowpan_nhc_add);
  181. void lowpan_nhc_del(struct lowpan_nhc *nhc)
  182. {
  183. spin_lock_bh(&lowpan_nhc_lock);
  184. lowpan_nhc_remove(nhc);
  185. lowpan_nexthdr_nhcs[nhc->nexthdr] = NULL;
  186. spin_unlock_bh(&lowpan_nhc_lock);
  187. synchronize_net();
  188. }
  189. EXPORT_SYMBOL(lowpan_nhc_del);