]>
Commit | Line | Data |
---|---|---|
4662a0da AA |
1 | /* This program is free software; you can redistribute it and/or modify |
2 | * it under the terms of the GNU General Public License version 2 | |
3 | * as published by the Free Software Foundation. | |
4 | * | |
5 | * This program is distributed in the hope that it will be useful, | |
6 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
7 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
8 | * GNU General Public License for more details. | |
9 | */ | |
10 | ||
11 | #include <linux/if_arp.h> | |
12 | ||
13 | #include <net/6lowpan.h> | |
54552d03 | 14 | #include <net/mac802154.h> |
4662a0da AA |
15 | #include <net/ieee802154_netdev.h> |
16 | ||
17 | #include "6lowpan_i.h" | |
18 | ||
faf7d36e | 19 | #define LOWPAN_DISPATCH_FIRST 0xc0 |
72a5e6bb AA |
20 | #define LOWPAN_DISPATCH_FRAG_MASK 0xf8 |
21 | ||
faf7d36e | 22 | #define LOWPAN_DISPATCH_NALP 0x00 |
a1d8d9a5 | 23 | #define LOWPAN_DISPATCH_ESC 0x40 |
ad663600 AA |
24 | #define LOWPAN_DISPATCH_HC1 0x42 |
25 | #define LOWPAN_DISPATCH_DFF 0x43 | |
26 | #define LOWPAN_DISPATCH_BC0 0x50 | |
27 | #define LOWPAN_DISPATCH_MESH 0x80 | |
faf7d36e | 28 | |
72a5e6bb | 29 | static int lowpan_give_skb_to_device(struct sk_buff *skb) |
4662a0da | 30 | { |
4662a0da | 31 | skb->protocol = htons(ETH_P_IPV6); |
1c64f147 AA |
32 | skb->dev->stats.rx_packets++; |
33 | skb->dev->stats.rx_bytes += skb->len; | |
4662a0da | 34 | |
51e0e5d8 | 35 | return netif_rx(skb); |
4662a0da AA |
36 | } |
37 | ||
72a5e6bb AA |
38 | static int lowpan_rx_handlers_result(struct sk_buff *skb, lowpan_rx_result res) |
39 | { | |
40 | switch (res) { | |
41 | case RX_CONTINUE: | |
42 | /* nobody cared about this packet */ | |
43 | net_warn_ratelimited("%s: received unknown dispatch\n", | |
44 | __func__); | |
45 | ||
46 | /* fall-through */ | |
47 | case RX_DROP_UNUSABLE: | |
48 | kfree_skb(skb); | |
49 | ||
50 | /* fall-through */ | |
51 | case RX_DROP: | |
52 | return NET_RX_DROP; | |
53 | case RX_QUEUED: | |
54 | return lowpan_give_skb_to_device(skb); | |
55 | default: | |
56 | break; | |
57 | } | |
58 | ||
59 | return NET_RX_DROP; | |
60 | } | |
61 | ||
62 | static inline bool lowpan_is_frag1(u8 dispatch) | |
63 | { | |
64 | return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAG1; | |
65 | } | |
66 | ||
67 | static inline bool lowpan_is_fragn(u8 dispatch) | |
68 | { | |
69 | return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAGN; | |
70 | } | |
71 | ||
72 | static lowpan_rx_result lowpan_rx_h_frag(struct sk_buff *skb) | |
73 | { | |
74 | int ret; | |
75 | ||
76 | if (!(lowpan_is_frag1(*skb_network_header(skb)) || | |
77 | lowpan_is_fragn(*skb_network_header(skb)))) | |
78 | return RX_CONTINUE; | |
79 | ||
80 | ret = lowpan_frag_rcv(skb, *skb_network_header(skb) & | |
81 | LOWPAN_DISPATCH_FRAG_MASK); | |
82 | if (ret == 1) | |
83 | return RX_QUEUED; | |
84 | ||
85 | /* Packet is freed by lowpan_frag_rcv on error or put into the frag | |
86 | * bucket. | |
87 | */ | |
88 | return RX_DROP; | |
89 | } | |
90 | ||
91 | int lowpan_iphc_decompress(struct sk_buff *skb) | |
4662a0da | 92 | { |
72a5e6bb | 93 | struct ieee802154_hdr hdr; |
4662a0da | 94 | |
72a5e6bb AA |
95 | if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) |
96 | return -EINVAL; | |
97 | ||
8911d774 | 98 | return lowpan_header_decompress(skb, skb->dev, &hdr.dest, &hdr.source); |
4662a0da AA |
99 | } |
100 | ||
72a5e6bb AA |
101 | static lowpan_rx_result lowpan_rx_h_iphc(struct sk_buff *skb) |
102 | { | |
103 | int ret; | |
104 | ||
105 | if (!lowpan_is_iphc(*skb_network_header(skb))) | |
106 | return RX_CONTINUE; | |
107 | ||
108 | /* Setting datagram_offset to zero indicates non frag handling | |
109 | * while doing lowpan_header_decompress. | |
110 | */ | |
111 | lowpan_802154_cb(skb)->d_size = 0; | |
112 | ||
113 | ret = lowpan_iphc_decompress(skb); | |
114 | if (ret < 0) | |
115 | return RX_DROP_UNUSABLE; | |
116 | ||
117 | return RX_QUEUED; | |
118 | } | |
119 | ||
120 | lowpan_rx_result lowpan_rx_h_ipv6(struct sk_buff *skb) | |
121 | { | |
122 | if (!lowpan_is_ipv6(*skb_network_header(skb))) | |
123 | return RX_CONTINUE; | |
124 | ||
125 | /* Pull off the 1-byte of 6lowpan header. */ | |
126 | skb_pull(skb, 1); | |
127 | return RX_QUEUED; | |
128 | } | |
129 | ||
ad663600 AA |
130 | static inline bool lowpan_is_esc(u8 dispatch) |
131 | { | |
132 | return dispatch == LOWPAN_DISPATCH_ESC; | |
133 | } | |
134 | ||
135 | static lowpan_rx_result lowpan_rx_h_esc(struct sk_buff *skb) | |
136 | { | |
137 | if (!lowpan_is_esc(*skb_network_header(skb))) | |
138 | return RX_CONTINUE; | |
139 | ||
140 | net_warn_ratelimited("%s: %s\n", skb->dev->name, | |
141 | "6LoWPAN ESC not supported\n"); | |
142 | ||
143 | return RX_DROP_UNUSABLE; | |
144 | } | |
145 | ||
146 | static inline bool lowpan_is_hc1(u8 dispatch) | |
147 | { | |
148 | return dispatch == LOWPAN_DISPATCH_HC1; | |
149 | } | |
150 | ||
151 | static lowpan_rx_result lowpan_rx_h_hc1(struct sk_buff *skb) | |
152 | { | |
153 | if (!lowpan_is_hc1(*skb_network_header(skb))) | |
154 | return RX_CONTINUE; | |
155 | ||
156 | net_warn_ratelimited("%s: %s\n", skb->dev->name, | |
157 | "6LoWPAN HC1 not supported\n"); | |
158 | ||
159 | return RX_DROP_UNUSABLE; | |
160 | } | |
161 | ||
162 | static inline bool lowpan_is_dff(u8 dispatch) | |
163 | { | |
164 | return dispatch == LOWPAN_DISPATCH_DFF; | |
165 | } | |
166 | ||
167 | static lowpan_rx_result lowpan_rx_h_dff(struct sk_buff *skb) | |
168 | { | |
169 | if (!lowpan_is_dff(*skb_network_header(skb))) | |
170 | return RX_CONTINUE; | |
171 | ||
172 | net_warn_ratelimited("%s: %s\n", skb->dev->name, | |
173 | "6LoWPAN DFF not supported\n"); | |
174 | ||
175 | return RX_DROP_UNUSABLE; | |
176 | } | |
177 | ||
178 | static inline bool lowpan_is_bc0(u8 dispatch) | |
179 | { | |
180 | return dispatch == LOWPAN_DISPATCH_BC0; | |
181 | } | |
182 | ||
183 | static lowpan_rx_result lowpan_rx_h_bc0(struct sk_buff *skb) | |
184 | { | |
185 | if (!lowpan_is_bc0(*skb_network_header(skb))) | |
186 | return RX_CONTINUE; | |
187 | ||
188 | net_warn_ratelimited("%s: %s\n", skb->dev->name, | |
189 | "6LoWPAN BC0 not supported\n"); | |
190 | ||
191 | return RX_DROP_UNUSABLE; | |
192 | } | |
193 | ||
194 | static inline bool lowpan_is_mesh(u8 dispatch) | |
195 | { | |
196 | return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_MESH; | |
197 | } | |
198 | ||
199 | static lowpan_rx_result lowpan_rx_h_mesh(struct sk_buff *skb) | |
200 | { | |
201 | if (!lowpan_is_mesh(*skb_network_header(skb))) | |
202 | return RX_CONTINUE; | |
203 | ||
204 | net_warn_ratelimited("%s: %s\n", skb->dev->name, | |
205 | "6LoWPAN MESH not supported\n"); | |
206 | ||
207 | return RX_DROP_UNUSABLE; | |
208 | } | |
209 | ||
72a5e6bb AA |
210 | static int lowpan_invoke_rx_handlers(struct sk_buff *skb) |
211 | { | |
212 | lowpan_rx_result res; | |
213 | ||
214 | #define CALL_RXH(rxh) \ | |
215 | do { \ | |
216 | res = rxh(skb); \ | |
217 | if (res != RX_CONTINUE) \ | |
218 | goto rxh_next; \ | |
219 | } while (0) | |
220 | ||
221 | /* likely at first */ | |
222 | CALL_RXH(lowpan_rx_h_iphc); | |
223 | CALL_RXH(lowpan_rx_h_frag); | |
224 | CALL_RXH(lowpan_rx_h_ipv6); | |
ad663600 AA |
225 | CALL_RXH(lowpan_rx_h_esc); |
226 | CALL_RXH(lowpan_rx_h_hc1); | |
227 | CALL_RXH(lowpan_rx_h_dff); | |
228 | CALL_RXH(lowpan_rx_h_bc0); | |
229 | CALL_RXH(lowpan_rx_h_mesh); | |
72a5e6bb AA |
230 | |
231 | rxh_next: | |
232 | return lowpan_rx_handlers_result(skb, res); | |
233 | #undef CALL_RXH | |
234 | } | |
235 | ||
faf7d36e AA |
236 | static inline bool lowpan_is_nalp(u8 dispatch) |
237 | { | |
238 | return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_NALP; | |
239 | } | |
240 | ||
c6fdbba3 AA |
241 | /* Lookup for reserved dispatch values at: |
242 | * https://www.iana.org/assignments/_6lowpan-parameters/_6lowpan-parameters.xhtml#_6lowpan-parameters-1 | |
243 | * | |
244 | * Last Updated: 2015-01-22 | |
245 | */ | |
246 | static inline bool lowpan_is_reserved(u8 dispatch) | |
247 | { | |
248 | return ((dispatch >= 0x44 && dispatch <= 0x4F) || | |
249 | (dispatch >= 0x51 && dispatch <= 0x5F) || | |
250 | (dispatch >= 0xc8 && dispatch <= 0xdf) || | |
251 | (dispatch >= 0xe8 && dispatch <= 0xff)); | |
252 | } | |
253 | ||
faf7d36e AA |
254 | /* lowpan_rx_h_check checks on generic 6LoWPAN requirements |
255 | * in MAC and 6LoWPAN header. | |
256 | * | |
257 | * Don't manipulate the skb here, it could be shared buffer. | |
258 | */ | |
259 | static inline bool lowpan_rx_h_check(struct sk_buff *skb) | |
260 | { | |
54552d03 AA |
261 | __le16 fc = ieee802154_get_fc_from_skb(skb); |
262 | ||
263 | /* check on ieee802154 conform 6LoWPAN header */ | |
264 | if (!ieee802154_is_data(fc) || | |
0ea0b9af | 265 | !ieee802154_skb_is_intra_pan_addressing(fc, skb)) |
54552d03 AA |
266 | return false; |
267 | ||
faf7d36e AA |
268 | /* check if we can dereference the dispatch */ |
269 | if (unlikely(!skb->len)) | |
270 | return false; | |
271 | ||
c6fdbba3 AA |
272 | if (lowpan_is_nalp(*skb_network_header(skb)) || |
273 | lowpan_is_reserved(*skb_network_header(skb))) | |
faf7d36e AA |
274 | return false; |
275 | ||
276 | return true; | |
277 | } | |
278 | ||
f4606583 AA |
279 | static int lowpan_rcv(struct sk_buff *skb, struct net_device *wdev, |
280 | struct packet_type *pt, struct net_device *orig_wdev) | |
4662a0da | 281 | { |
989d433d | 282 | struct net_device *ldev; |
4662a0da | 283 | |
742c3afe | 284 | if (wdev->type != ARPHRD_IEEE802154 || |
faf7d36e AA |
285 | skb->pkt_type == PACKET_OTHERHOST || |
286 | !lowpan_rx_h_check(skb)) | |
aeedebff | 287 | goto drop; |
989d433d AA |
288 | |
289 | ldev = wdev->ieee802154_ptr->lowpan_dev; | |
290 | if (!ldev || !netif_running(ldev)) | |
aeedebff | 291 | goto drop; |
c0015bf3 | 292 | |
f801cf40 | 293 | /* Replacing skb->dev and followed rx handlers will manipulate skb. */ |
4662a0da AA |
294 | skb = skb_share_check(skb, GFP_ATOMIC); |
295 | if (!skb) | |
aeedebff | 296 | goto out; |
f801cf40 | 297 | skb->dev = ldev; |
4662a0da | 298 | |
72a5e6bb AA |
299 | /* When receive frag1 it's likely that we manipulate the buffer. |
300 | * When recevie iphc we manipulate the data buffer. So we need | |
301 | * to unshare the buffer. | |
302 | */ | |
303 | if (lowpan_is_frag1(*skb_network_header(skb)) || | |
304 | lowpan_is_iphc(*skb_network_header(skb))) { | |
305 | skb = skb_unshare(skb, GFP_ATOMIC); | |
306 | if (!skb) | |
aeedebff | 307 | goto out; |
4662a0da AA |
308 | } |
309 | ||
72a5e6bb | 310 | return lowpan_invoke_rx_handlers(skb); |
aeedebff AA |
311 | |
312 | drop: | |
313 | kfree_skb(skb); | |
314 | out: | |
315 | return NET_RX_DROP; | |
4662a0da AA |
316 | } |
317 | ||
318 | static struct packet_type lowpan_packet_type = { | |
319 | .type = htons(ETH_P_IEEE802154), | |
320 | .func = lowpan_rcv, | |
321 | }; | |
322 | ||
323 | void lowpan_rx_init(void) | |
324 | { | |
325 | dev_add_pack(&lowpan_packet_type); | |
326 | } | |
327 | ||
328 | void lowpan_rx_exit(void) | |
329 | { | |
330 | dev_remove_pack(&lowpan_packet_type); | |
331 | } |