]>
Commit | Line | Data |
---|---|---|
cc6ed268 AA |
1 | /* |
2 | * 6LoWPAN IPv6 UDP compression according to RFC6282 | |
3 | * | |
4 | * | |
5 | * Authors: | |
6 | * Alexander Aring <[email protected]> | |
7 | * | |
8 | * Orignal written by: | |
9 | * Alexander Smirnov <[email protected]> | |
10 | * Jon Smirl <[email protected]> | |
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 | ||
18 | #include "nhc.h" | |
19 | ||
6350047e AA |
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 | /* values for port compression, _with checksum_ ie bit 5 set to 0 */ | |
30 | ||
31 | /* all inline */ | |
32 | #define LOWPAN_NHC_UDP_CS_P_00 0xF0 | |
33 | /* source 16bit inline, dest = 0xF0 + 8 bit inline */ | |
34 | #define LOWPAN_NHC_UDP_CS_P_01 0xF1 | |
35 | /* source = 0xF0 + 8bit inline, dest = 16 bit inline */ | |
36 | #define LOWPAN_NHC_UDP_CS_P_10 0xF2 | |
37 | /* source & dest = 0xF0B + 4bit inline */ | |
38 | #define LOWPAN_NHC_UDP_CS_P_11 0xF3 | |
39 | /* checksum elided */ | |
40 | #define LOWPAN_NHC_UDP_CS_C 0x04 | |
cc6ed268 AA |
41 | |
42 | static 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 | /* checksum */ | |
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 | /* UDP length needs to be infered from the lower layers | |
91 | * here, we obtain the hint from the remaining size of the | |
92 | * frame | |
93 | */ | |
2e4d60cb | 94 | switch (lowpan_dev(skb->dev)->lltype) { |
72a5e6bb AA |
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 | } | |
cc6ed268 AA |
106 | pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len)); |
107 | ||
108 | /* replace the compressed UDP head by the uncompressed UDP | |
109 | * header | |
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 | ||
121 | static 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 | /* compression value */ | |
132 | tmp = LOWPAN_NHC_UDP_CS_P_11; | |
133 | lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp)); | |
134 | /* source and destination port */ | |
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 | /* compression value */ | |
142 | tmp = LOWPAN_NHC_UDP_CS_P_01; | |
143 | lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp)); | |
144 | /* source port */ | |
145 | lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source)); | |
146 | /* destination port */ | |
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 | /* compression value */ | |
153 | tmp = LOWPAN_NHC_UDP_CS_P_10; | |
154 | lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp)); | |
155 | /* source port */ | |
156 | tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT; | |
157 | lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp)); | |
158 | /* destination port */ | |
159 | lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest)); | |
160 | } else { | |
161 | pr_debug("UDP header: can't compress\n"); | |
162 | /* compression value */ | |
163 | tmp = LOWPAN_NHC_UDP_CS_P_00; | |
164 | lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp)); | |
165 | /* source port */ | |
166 | lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source)); | |
167 | /* destination port */ | |
168 | lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest)); | |
169 | } | |
170 | ||
171 | /* checksum is always inline */ | |
172 | lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check)); | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
177 | static 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 | ||
183 | LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr), | |
184 | udp_nhid_setup, LOWPAN_NHC_UDP_IDLEN, udp_uncompress, udp_compress); | |
185 | ||
186 | module_lowpan_nhc(nhc_udp); | |
187 | MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression"); | |
188 | MODULE_LICENSE("GPL"); |