1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include "nhc.h"
19
20#define LOWPAN_NHC_UDP_MASK 0xF8
21#define LOWPAN_NHC_UDP_ID 0xF0
22#define LOWPAN_NHC_UDP_IDLEN 1
23
24#define LOWPAN_NHC_UDP_4BIT_PORT 0xF0B0
25#define LOWPAN_NHC_UDP_4BIT_MASK 0xFFF0
26#define LOWPAN_NHC_UDP_8BIT_PORT 0xF000
27#define LOWPAN_NHC_UDP_8BIT_MASK 0xFF00
28
29
30
31
32#define LOWPAN_NHC_UDP_CS_P_00 0xF0
33
34#define LOWPAN_NHC_UDP_CS_P_01 0xF1
35
36#define LOWPAN_NHC_UDP_CS_P_10 0xF2
37
38#define LOWPAN_NHC_UDP_CS_P_11 0xF3
39
40#define LOWPAN_NHC_UDP_CS_C 0x04
41
42static int udp_uncompress(struct sk_buff *skb, size_t needed)
43{
44 u8 tmp = 0, val = 0;
45 struct udphdr uh;
46 bool fail;
47 int err;
48
49 fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
50
51 pr_debug("UDP header uncompression\n");
52 switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
53 case LOWPAN_NHC_UDP_CS_P_00:
54 fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
55 fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
56 break;
57 case LOWPAN_NHC_UDP_CS_P_01:
58 fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
59 fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
60 uh.dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
61 break;
62 case LOWPAN_NHC_UDP_CS_P_10:
63 fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
64 uh.source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
65 fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
66 break;
67 case LOWPAN_NHC_UDP_CS_P_11:
68 fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
69 uh.source = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val >> 4));
70 uh.dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val & 0x0f));
71 break;
72 default:
73 BUG();
74 }
75
76 pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
77 ntohs(uh.source), ntohs(uh.dest));
78
79
80 if (tmp & LOWPAN_NHC_UDP_CS_C) {
81 pr_debug_ratelimited("checksum elided currently not supported\n");
82 fail = true;
83 } else {
84 fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check));
85 }
86
87 if (fail)
88 return -EINVAL;
89
90
91
92
93
94 switch (lowpan_dev(skb->dev)->lltype) {
95 case LOWPAN_LLTYPE_IEEE802154:
96 if (lowpan_802154_cb(skb)->d_size)
97 uh.len = htons(lowpan_802154_cb(skb)->d_size -
98 sizeof(struct ipv6hdr));
99 else
100 uh.len = htons(skb->len + sizeof(struct udphdr));
101 break;
102 default:
103 uh.len = htons(skb->len + sizeof(struct udphdr));
104 break;
105 }
106 pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
107
108
109
110
111 err = skb_cow(skb, needed);
112 if (unlikely(err))
113 return err;
114
115 skb_push(skb, sizeof(struct udphdr));
116 skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
117
118 return 0;
119}
120
121static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
122{
123 const struct udphdr *uh = udp_hdr(skb);
124 u8 tmp;
125
126 if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
127 LOWPAN_NHC_UDP_4BIT_PORT) &&
128 ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
129 LOWPAN_NHC_UDP_4BIT_PORT)) {
130 pr_debug("UDP header: both ports compression to 4 bits\n");
131
132 tmp = LOWPAN_NHC_UDP_CS_P_11;
133 lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
134
135 tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
136 ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
137 lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
138 } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
139 LOWPAN_NHC_UDP_8BIT_PORT) {
140 pr_debug("UDP header: remove 8 bits of dest\n");
141
142 tmp = LOWPAN_NHC_UDP_CS_P_01;
143 lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
144
145 lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
146
147 tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
148 lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
149 } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
150 LOWPAN_NHC_UDP_8BIT_PORT) {
151 pr_debug("UDP header: remove 8 bits of source\n");
152
153 tmp = LOWPAN_NHC_UDP_CS_P_10;
154 lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
155
156 tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
157 lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
158
159 lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
160 } else {
161 pr_debug("UDP header: can't compress\n");
162
163 tmp = LOWPAN_NHC_UDP_CS_P_00;
164 lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
165
166 lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
167
168 lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
169 }
170
171
172 lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
173
174 return 0;
175}
176
177static void udp_nhid_setup(struct lowpan_nhc *nhc)
178{
179 nhc->id[0] = LOWPAN_NHC_UDP_ID;
180 nhc->idmask[0] = LOWPAN_NHC_UDP_MASK;
181}
182
183LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr),
184 udp_nhid_setup, LOWPAN_NHC_UDP_IDLEN, udp_uncompress, udp_compress);
185
186module_lowpan_nhc(nhc_udp);
187MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
188MODULE_LICENSE("GPL");
189