nhc_udp.c 5.7 KB

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