nhc.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * 6LoWPAN next header compression
  4. *
  5. * Authors:
  6. * Alexander Aring <[email protected]>
  7. */
  8. #include <linux/netdevice.h>
  9. #include <net/ipv6.h>
  10. #include "nhc.h"
  11. static const struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX + 1];
  12. static DEFINE_SPINLOCK(lowpan_nhc_lock);
  13. static const struct lowpan_nhc *lowpan_nhc_by_nhcid(struct sk_buff *skb)
  14. {
  15. const struct lowpan_nhc *nhc;
  16. int i;
  17. u8 id;
  18. if (!pskb_may_pull(skb, 1))
  19. return NULL;
  20. id = *skb->data;
  21. for (i = 0; i < NEXTHDR_MAX + 1; i++) {
  22. nhc = lowpan_nexthdr_nhcs[i];
  23. if (!nhc)
  24. continue;
  25. if ((id & nhc->idmask) == nhc->id)
  26. return nhc;
  27. }
  28. return NULL;
  29. }
  30. int lowpan_nhc_check_compression(struct sk_buff *skb,
  31. const struct ipv6hdr *hdr, u8 **hc_ptr)
  32. {
  33. const struct lowpan_nhc *nhc;
  34. int ret = 0;
  35. spin_lock_bh(&lowpan_nhc_lock);
  36. nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
  37. if (!(nhc && nhc->compress))
  38. ret = -ENOENT;
  39. spin_unlock_bh(&lowpan_nhc_lock);
  40. return ret;
  41. }
  42. int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
  43. u8 **hc_ptr)
  44. {
  45. int ret;
  46. const struct lowpan_nhc *nhc;
  47. spin_lock_bh(&lowpan_nhc_lock);
  48. nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
  49. /* check if the nhc module was removed in unlocked part.
  50. * TODO: this is a workaround we should prevent unloading
  51. * of nhc modules while unlocked part, this will always drop
  52. * the lowpan packet but it's very unlikely.
  53. *
  54. * Solution isn't easy because we need to decide at
  55. * lowpan_nhc_check_compression if we do a compression or not.
  56. * Because the inline data which is added to skb, we can't move this
  57. * handling.
  58. */
  59. if (unlikely(!nhc || !nhc->compress)) {
  60. ret = -EINVAL;
  61. goto out;
  62. }
  63. /* In the case of RAW sockets the transport header is not set by
  64. * the ip6 stack so we must set it ourselves
  65. */
  66. if (skb->transport_header == skb->network_header)
  67. skb_set_transport_header(skb, sizeof(struct ipv6hdr));
  68. ret = nhc->compress(skb, hc_ptr);
  69. if (ret < 0)
  70. goto out;
  71. /* skip the transport header */
  72. skb_pull(skb, nhc->nexthdrlen);
  73. out:
  74. spin_unlock_bh(&lowpan_nhc_lock);
  75. return ret;
  76. }
  77. int lowpan_nhc_do_uncompression(struct sk_buff *skb,
  78. const struct net_device *dev,
  79. struct ipv6hdr *hdr)
  80. {
  81. const struct lowpan_nhc *nhc;
  82. int ret;
  83. spin_lock_bh(&lowpan_nhc_lock);
  84. nhc = lowpan_nhc_by_nhcid(skb);
  85. if (nhc) {
  86. if (nhc->uncompress) {
  87. ret = nhc->uncompress(skb, sizeof(struct ipv6hdr) +
  88. nhc->nexthdrlen);
  89. if (ret < 0) {
  90. spin_unlock_bh(&lowpan_nhc_lock);
  91. return ret;
  92. }
  93. } else {
  94. spin_unlock_bh(&lowpan_nhc_lock);
  95. netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
  96. nhc->name);
  97. return -ENOTSUPP;
  98. }
  99. } else {
  100. spin_unlock_bh(&lowpan_nhc_lock);
  101. netdev_warn(dev, "received unknown nhc id which was not found.\n");
  102. return -ENOENT;
  103. }
  104. hdr->nexthdr = nhc->nexthdr;
  105. skb_reset_transport_header(skb);
  106. raw_dump_table(__func__, "raw transport header dump",
  107. skb_transport_header(skb), nhc->nexthdrlen);
  108. spin_unlock_bh(&lowpan_nhc_lock);
  109. return 0;
  110. }
  111. int lowpan_nhc_add(const struct lowpan_nhc *nhc)
  112. {
  113. int ret = 0;
  114. spin_lock_bh(&lowpan_nhc_lock);
  115. if (lowpan_nexthdr_nhcs[nhc->nexthdr]) {
  116. ret = -EEXIST;
  117. goto out;
  118. }
  119. lowpan_nexthdr_nhcs[nhc->nexthdr] = nhc;
  120. out:
  121. spin_unlock_bh(&lowpan_nhc_lock);
  122. return ret;
  123. }
  124. EXPORT_SYMBOL(lowpan_nhc_add);
  125. void lowpan_nhc_del(const struct lowpan_nhc *nhc)
  126. {
  127. spin_lock_bh(&lowpan_nhc_lock);
  128. lowpan_nexthdr_nhcs[nhc->nexthdr] = NULL;
  129. spin_unlock_bh(&lowpan_nhc_lock);
  130. synchronize_net();
  131. }
  132. EXPORT_SYMBOL(lowpan_nhc_del);