nhc_udp.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * 6LoWPAN IPv6 UDP compression according to RFC6282
  4. *
  5. * Authors:
  6. * Alexander Aring <[email protected]>
  7. *
  8. * Original written by:
  9. * Alexander Smirnov <[email protected]>
  10. * Jon Smirl <[email protected]>
  11. */
  12. #include "nhc.h"
  13. #define LOWPAN_NHC_UDP_MASK 0xF8
  14. #define LOWPAN_NHC_UDP_ID 0xF0
  15. #define LOWPAN_NHC_UDP_4BIT_PORT 0xF0B0
  16. #define LOWPAN_NHC_UDP_4BIT_MASK 0xFFF0
  17. #define LOWPAN_NHC_UDP_8BIT_PORT 0xF000
  18. #define LOWPAN_NHC_UDP_8BIT_MASK 0xFF00
  19. /* values for port compression, _with checksum_ ie bit 5 set to 0 */
  20. /* all inline */
  21. #define LOWPAN_NHC_UDP_CS_P_00 0xF0
  22. /* source 16bit inline, dest = 0xF0 + 8 bit inline */
  23. #define LOWPAN_NHC_UDP_CS_P_01 0xF1
  24. /* source = 0xF0 + 8bit inline, dest = 16 bit inline */
  25. #define LOWPAN_NHC_UDP_CS_P_10 0xF2
  26. /* source & dest = 0xF0B + 4bit inline */
  27. #define LOWPAN_NHC_UDP_CS_P_11 0xF3
  28. /* checksum elided */
  29. #define LOWPAN_NHC_UDP_CS_C 0x04
  30. static int udp_uncompress(struct sk_buff *skb, size_t needed)
  31. {
  32. u8 tmp = 0, val = 0;
  33. struct udphdr uh;
  34. bool fail;
  35. int err;
  36. fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
  37. pr_debug("UDP header uncompression\n");
  38. switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
  39. case LOWPAN_NHC_UDP_CS_P_00:
  40. fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
  41. fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
  42. break;
  43. case LOWPAN_NHC_UDP_CS_P_01:
  44. fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
  45. fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
  46. uh.dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
  47. break;
  48. case LOWPAN_NHC_UDP_CS_P_10:
  49. fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
  50. uh.source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
  51. fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
  52. break;
  53. case LOWPAN_NHC_UDP_CS_P_11:
  54. fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
  55. uh.source = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val >> 4));
  56. uh.dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val & 0x0f));
  57. break;
  58. default:
  59. BUG();
  60. }
  61. pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
  62. ntohs(uh.source), ntohs(uh.dest));
  63. /* checksum */
  64. if (tmp & LOWPAN_NHC_UDP_CS_C) {
  65. pr_debug_ratelimited("checksum elided currently not supported\n");
  66. fail = true;
  67. } else {
  68. fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check));
  69. }
  70. if (fail)
  71. return -EINVAL;
  72. /* UDP length needs to be inferred from the lower layers
  73. * here, we obtain the hint from the remaining size of the
  74. * frame
  75. */
  76. switch (lowpan_dev(skb->dev)->lltype) {
  77. case LOWPAN_LLTYPE_IEEE802154:
  78. if (lowpan_802154_cb(skb)->d_size)
  79. uh.len = htons(lowpan_802154_cb(skb)->d_size -
  80. sizeof(struct ipv6hdr));
  81. else
  82. uh.len = htons(skb->len + sizeof(struct udphdr));
  83. break;
  84. default:
  85. uh.len = htons(skb->len + sizeof(struct udphdr));
  86. break;
  87. }
  88. pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
  89. /* replace the compressed UDP head by the uncompressed UDP
  90. * header
  91. */
  92. err = skb_cow(skb, needed);
  93. if (unlikely(err))
  94. return err;
  95. skb_push(skb, sizeof(struct udphdr));
  96. skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
  97. return 0;
  98. }
  99. static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
  100. {
  101. const struct udphdr *uh = udp_hdr(skb);
  102. u8 tmp;
  103. if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
  104. LOWPAN_NHC_UDP_4BIT_PORT) &&
  105. ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
  106. LOWPAN_NHC_UDP_4BIT_PORT)) {
  107. pr_debug("UDP header: both ports compression to 4 bits\n");
  108. /* compression value */
  109. tmp = LOWPAN_NHC_UDP_CS_P_11;
  110. lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
  111. /* source and destination port */
  112. tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
  113. ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
  114. lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
  115. } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
  116. LOWPAN_NHC_UDP_8BIT_PORT) {
  117. pr_debug("UDP header: remove 8 bits of dest\n");
  118. /* compression value */
  119. tmp = LOWPAN_NHC_UDP_CS_P_01;
  120. lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
  121. /* source port */
  122. lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
  123. /* destination port */
  124. tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
  125. lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
  126. } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
  127. LOWPAN_NHC_UDP_8BIT_PORT) {
  128. pr_debug("UDP header: remove 8 bits of source\n");
  129. /* compression value */
  130. tmp = LOWPAN_NHC_UDP_CS_P_10;
  131. lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
  132. /* source port */
  133. tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
  134. lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
  135. /* destination port */
  136. lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
  137. } else {
  138. pr_debug("UDP header: can't compress\n");
  139. /* compression value */
  140. tmp = LOWPAN_NHC_UDP_CS_P_00;
  141. lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
  142. /* source port */
  143. lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
  144. /* destination port */
  145. lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
  146. }
  147. /* checksum is always inline */
  148. lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
  149. return 0;
  150. }
  151. LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr),
  152. LOWPAN_NHC_UDP_ID, LOWPAN_NHC_UDP_MASK, udp_uncompress, udp_compress);
  153. module_lowpan_nhc(nhc_udp);
  154. MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
  155. MODULE_LICENSE("GPL");