diff options
Diffstat (limited to 'net/ieee802154/6lowpan.c')
-rw-r--r-- | net/ieee802154/6lowpan.c | 885 |
1 files changed, 885 insertions, 0 deletions
diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c new file mode 100644 index 000000000000..cf304cc8c8ef --- /dev/null +++ b/net/ieee802154/6lowpan.c | |||
@@ -0,0 +1,885 @@ | |||
1 | /* | ||
2 | * Copyright 2011, Siemens AG | ||
3 | * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com> | ||
4 | */ | ||
5 | |||
6 | /* | ||
7 | * Based on patches from Jon Smirl <jonsmirl@gmail.com> | ||
8 | * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 | ||
12 | * as published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
21 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
22 | */ | ||
23 | |||
24 | /* Jon's code is based on 6lowpan implementation for Contiki which is: | ||
25 | * Copyright (c) 2008, Swedish Institute of Computer Science. | ||
26 | * All rights reserved. | ||
27 | * | ||
28 | * Redistribution and use in source and binary forms, with or without | ||
29 | * modification, are permitted provided that the following conditions | ||
30 | * are met: | ||
31 | * 1. Redistributions of source code must retain the above copyright | ||
32 | * notice, this list of conditions and the following disclaimer. | ||
33 | * 2. Redistributions in binary form must reproduce the above copyright | ||
34 | * notice, this list of conditions and the following disclaimer in the | ||
35 | * documentation and/or other materials provided with the distribution. | ||
36 | * 3. Neither the name of the Institute nor the names of its contributors | ||
37 | * may be used to endorse or promote products derived from this software | ||
38 | * without specific prior written permission. | ||
39 | * | ||
40 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND | ||
41 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
42 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
43 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE | ||
44 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
45 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
46 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
47 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
48 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
49 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
50 | * SUCH DAMAGE. | ||
51 | */ | ||
52 | |||
53 | #define DEBUG | ||
54 | |||
55 | #include <linux/bitops.h> | ||
56 | #include <linux/if_arp.h> | ||
57 | #include <linux/module.h> | ||
58 | #include <linux/moduleparam.h> | ||
59 | #include <linux/netdevice.h> | ||
60 | #include <net/af_ieee802154.h> | ||
61 | #include <net/ieee802154.h> | ||
62 | #include <net/ieee802154_netdev.h> | ||
63 | #include <net/ipv6.h> | ||
64 | |||
65 | #include "6lowpan.h" | ||
66 | |||
67 | /* TTL uncompression values */ | ||
68 | static const u8 lowpan_ttl_values[] = {0, 1, 64, 255}; | ||
69 | |||
70 | static LIST_HEAD(lowpan_devices); | ||
71 | |||
72 | /* | ||
73 | * Uncompression of linklocal: | ||
74 | * 0 -> 16 bytes from packet | ||
75 | * 1 -> 2 bytes from prefix - bunch of zeroes and 8 from packet | ||
76 | * 2 -> 2 bytes from prefix - zeroes + 2 from packet | ||
77 | * 3 -> 2 bytes from prefix - infer 8 bytes from lladdr | ||
78 | * | ||
79 | * NOTE: => the uncompress function does change 0xf to 0x10 | ||
80 | * NOTE: 0x00 => no-autoconfig => unspecified | ||
81 | */ | ||
82 | static const u8 lowpan_unc_llconf[] = {0x0f, 0x28, 0x22, 0x20}; | ||
83 | |||
84 | /* | ||
85 | * Uncompression of ctx-based: | ||
86 | * 0 -> 0 bits from packet [unspecified / reserved] | ||
87 | * 1 -> 8 bytes from prefix - bunch of zeroes and 8 from packet | ||
88 | * 2 -> 8 bytes from prefix - zeroes + 2 from packet | ||
89 | * 3 -> 8 bytes from prefix - infer 8 bytes from lladdr | ||
90 | */ | ||
91 | static const u8 lowpan_unc_ctxconf[] = {0x00, 0x88, 0x82, 0x80}; | ||
92 | |||
93 | /* | ||
94 | * Uncompression of ctx-base | ||
95 | * 0 -> 0 bits from packet | ||
96 | * 1 -> 2 bytes from prefix - bunch of zeroes 5 from packet | ||
97 | * 2 -> 2 bytes from prefix - zeroes + 3 from packet | ||
98 | * 3 -> 2 bytes from prefix - infer 1 bytes from lladdr | ||
99 | */ | ||
100 | static const u8 lowpan_unc_mxconf[] = {0x0f, 0x25, 0x23, 0x21}; | ||
101 | |||
102 | /* Link local prefix */ | ||
103 | static const u8 lowpan_llprefix[] = {0xfe, 0x80}; | ||
104 | |||
105 | /* private device info */ | ||
106 | struct lowpan_dev_info { | ||
107 | struct net_device *real_dev; /* real WPAN device ptr */ | ||
108 | struct mutex dev_list_mtx; /* mutex for list ops */ | ||
109 | }; | ||
110 | |||
111 | struct lowpan_dev_record { | ||
112 | struct net_device *ldev; | ||
113 | struct list_head list; | ||
114 | }; | ||
115 | |||
116 | static inline struct | ||
117 | lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) | ||
118 | { | ||
119 | return netdev_priv(dev); | ||
120 | } | ||
121 | |||
122 | static inline void lowpan_address_flip(u8 *src, u8 *dest) | ||
123 | { | ||
124 | int i; | ||
125 | for (i = 0; i < IEEE802154_ADDR_LEN; i++) | ||
126 | (dest)[IEEE802154_ADDR_LEN - i - 1] = (src)[i]; | ||
127 | } | ||
128 | |||
129 | /* list of all 6lowpan devices, uses for package delivering */ | ||
130 | /* print data in line */ | ||
131 | static inline void lowpan_raw_dump_inline(const char *caller, char *msg, | ||
132 | unsigned char *buf, int len) | ||
133 | { | ||
134 | #ifdef DEBUG | ||
135 | if (msg) | ||
136 | pr_debug("(%s) %s: ", caller, msg); | ||
137 | print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, | ||
138 | 16, 1, buf, len, false); | ||
139 | #endif /* DEBUG */ | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * print data in a table format: | ||
144 | * | ||
145 | * addr: xx xx xx xx xx xx | ||
146 | * addr: xx xx xx xx xx xx | ||
147 | * ... | ||
148 | */ | ||
149 | static inline void lowpan_raw_dump_table(const char *caller, char *msg, | ||
150 | unsigned char *buf, int len) | ||
151 | { | ||
152 | #ifdef DEBUG | ||
153 | if (msg) | ||
154 | pr_debug("(%s) %s:\n", caller, msg); | ||
155 | print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_OFFSET, | ||
156 | 16, 1, buf, len, false); | ||
157 | #endif /* DEBUG */ | ||
158 | } | ||
159 | |||
160 | static u8 | ||
161 | lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift, const struct in6_addr *ipaddr, | ||
162 | const unsigned char *lladdr) | ||
163 | { | ||
164 | u8 val = 0; | ||
165 | |||
166 | if (is_addr_mac_addr_based(ipaddr, lladdr)) | ||
167 | val = 3; /* 0-bits */ | ||
168 | else if (lowpan_is_iid_16_bit_compressable(ipaddr)) { | ||
169 | /* compress IID to 16 bits xxxx::XXXX */ | ||
170 | memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2); | ||
171 | *hc06_ptr += 2; | ||
172 | val = 2; /* 16-bits */ | ||
173 | } else { | ||
174 | /* do not compress IID => xxxx::IID */ | ||
175 | memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8); | ||
176 | *hc06_ptr += 8; | ||
177 | val = 1; /* 64-bits */ | ||
178 | } | ||
179 | |||
180 | return rol8(val, shift); | ||
181 | } | ||
182 | |||
183 | static void | ||
184 | lowpan_uip_ds6_set_addr_iid(struct in6_addr *ipaddr, unsigned char *lladdr) | ||
185 | { | ||
186 | memcpy(&ipaddr->s6_addr[8], lladdr, IEEE802154_ALEN); | ||
187 | /* second bit-flip (Universe/Local) is done according RFC2464 */ | ||
188 | ipaddr->s6_addr[8] ^= 0x02; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * Uncompress addresses based on a prefix and a postfix with zeroes in | ||
193 | * between. If the postfix is zero in length it will use the link address | ||
194 | * to configure the IP address (autoconf style). | ||
195 | * pref_post_count takes a byte where the first nibble specify prefix count | ||
196 | * and the second postfix count (NOTE: 15/0xf => 16 bytes copy). | ||
197 | */ | ||
198 | static int | ||
199 | lowpan_uncompress_addr(struct sk_buff *skb, struct in6_addr *ipaddr, | ||
200 | u8 const *prefix, u8 pref_post_count, unsigned char *lladdr) | ||
201 | { | ||
202 | u8 prefcount = pref_post_count >> 4; | ||
203 | u8 postcount = pref_post_count & 0x0f; | ||
204 | |||
205 | /* full nibble 15 => 16 */ | ||
206 | prefcount = (prefcount == 15 ? 16 : prefcount); | ||
207 | postcount = (postcount == 15 ? 16 : postcount); | ||
208 | |||
209 | if (lladdr) | ||
210 | lowpan_raw_dump_inline(__func__, "linklocal address", | ||
211 | lladdr, IEEE802154_ALEN); | ||
212 | if (prefcount > 0) | ||
213 | memcpy(ipaddr, prefix, prefcount); | ||
214 | |||
215 | if (prefcount + postcount < 16) | ||
216 | memset(&ipaddr->s6_addr[prefcount], 0, | ||
217 | 16 - (prefcount + postcount)); | ||
218 | |||
219 | if (postcount > 0) { | ||
220 | memcpy(&ipaddr->s6_addr[16 - postcount], skb->data, postcount); | ||
221 | skb_pull(skb, postcount); | ||
222 | } else if (prefcount > 0) { | ||
223 | if (lladdr == NULL) | ||
224 | return -EINVAL; | ||
225 | |||
226 | /* no IID based configuration if no prefix and no data */ | ||
227 | lowpan_uip_ds6_set_addr_iid(ipaddr, lladdr); | ||
228 | } | ||
229 | |||
230 | pr_debug("(%s): uncompressing %d + %d => ", __func__, prefcount, | ||
231 | postcount); | ||
232 | lowpan_raw_dump_inline(NULL, NULL, ipaddr->s6_addr, 16); | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static u8 lowpan_fetch_skb_u8(struct sk_buff *skb) | ||
238 | { | ||
239 | u8 ret; | ||
240 | |||
241 | ret = skb->data[0]; | ||
242 | skb_pull(skb, 1); | ||
243 | |||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | static int lowpan_header_create(struct sk_buff *skb, | ||
248 | struct net_device *dev, | ||
249 | unsigned short type, const void *_daddr, | ||
250 | const void *_saddr, unsigned len) | ||
251 | { | ||
252 | u8 tmp, iphc0, iphc1, *hc06_ptr; | ||
253 | struct ipv6hdr *hdr; | ||
254 | const u8 *saddr = _saddr; | ||
255 | const u8 *daddr = _daddr; | ||
256 | u8 *head; | ||
257 | struct ieee802154_addr sa, da; | ||
258 | |||
259 | if (type != ETH_P_IPV6) | ||
260 | return 0; | ||
261 | /* TODO: | ||
262 | * if this package isn't ipv6 one, where should it be routed? | ||
263 | */ | ||
264 | head = kzalloc(100, GFP_KERNEL); | ||
265 | if (head == NULL) | ||
266 | return -ENOMEM; | ||
267 | |||
268 | hdr = ipv6_hdr(skb); | ||
269 | hc06_ptr = head + 2; | ||
270 | |||
271 | pr_debug("(%s): IPv6 header dump:\n\tversion = %d\n\tlength = %d\n" | ||
272 | "\tnexthdr = 0x%02x\n\thop_lim = %d\n", __func__, | ||
273 | hdr->version, ntohs(hdr->payload_len), hdr->nexthdr, | ||
274 | hdr->hop_limit); | ||
275 | |||
276 | lowpan_raw_dump_table(__func__, "raw skb network header dump", | ||
277 | skb_network_header(skb), sizeof(struct ipv6hdr)); | ||
278 | |||
279 | if (!saddr) | ||
280 | saddr = dev->dev_addr; | ||
281 | |||
282 | lowpan_raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); | ||
283 | |||
284 | /* | ||
285 | * As we copy some bit-length fields, in the IPHC encoding bytes, | ||
286 | * we sometimes use |= | ||
287 | * If the field is 0, and the current bit value in memory is 1, | ||
288 | * this does not work. We therefore reset the IPHC encoding here | ||
289 | */ | ||
290 | iphc0 = LOWPAN_DISPATCH_IPHC; | ||
291 | iphc1 = 0; | ||
292 | |||
293 | /* TODO: context lookup */ | ||
294 | |||
295 | lowpan_raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); | ||
296 | |||
297 | /* | ||
298 | * Traffic class, flow label | ||
299 | * If flow label is 0, compress it. If traffic class is 0, compress it | ||
300 | * We have to process both in the same time as the offset of traffic | ||
301 | * class depends on the presence of version and flow label | ||
302 | */ | ||
303 | |||
304 | /* hc06 format of TC is ECN | DSCP , original one is DSCP | ECN */ | ||
305 | tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4); | ||
306 | tmp = ((tmp & 0x03) << 6) | (tmp >> 2); | ||
307 | |||
308 | if (((hdr->flow_lbl[0] & 0x0F) == 0) && | ||
309 | (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) { | ||
310 | /* flow label can be compressed */ | ||
311 | iphc0 |= LOWPAN_IPHC_FL_C; | ||
312 | if ((hdr->priority == 0) && | ||
313 | ((hdr->flow_lbl[0] & 0xF0) == 0)) { | ||
314 | /* compress (elide) all */ | ||
315 | iphc0 |= LOWPAN_IPHC_TC_C; | ||
316 | } else { | ||
317 | /* compress only the flow label */ | ||
318 | *hc06_ptr = tmp; | ||
319 | hc06_ptr += 1; | ||
320 | } | ||
321 | } else { | ||
322 | /* Flow label cannot be compressed */ | ||
323 | if ((hdr->priority == 0) && | ||
324 | ((hdr->flow_lbl[0] & 0xF0) == 0)) { | ||
325 | /* compress only traffic class */ | ||
326 | iphc0 |= LOWPAN_IPHC_TC_C; | ||
327 | *hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F); | ||
328 | memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2); | ||
329 | hc06_ptr += 3; | ||
330 | } else { | ||
331 | /* compress nothing */ | ||
332 | memcpy(hc06_ptr, &hdr, 4); | ||
333 | /* replace the top byte with new ECN | DSCP format */ | ||
334 | *hc06_ptr = tmp; | ||
335 | hc06_ptr += 4; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | /* NOTE: payload length is always compressed */ | ||
340 | |||
341 | /* Next Header is compress if UDP */ | ||
342 | if (hdr->nexthdr == UIP_PROTO_UDP) | ||
343 | iphc0 |= LOWPAN_IPHC_NH_C; | ||
344 | |||
345 | /* TODO: next header compression */ | ||
346 | |||
347 | if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) { | ||
348 | *hc06_ptr = hdr->nexthdr; | ||
349 | hc06_ptr += 1; | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * Hop limit | ||
354 | * if 1: compress, encoding is 01 | ||
355 | * if 64: compress, encoding is 10 | ||
356 | * if 255: compress, encoding is 11 | ||
357 | * else do not compress | ||
358 | */ | ||
359 | switch (hdr->hop_limit) { | ||
360 | case 1: | ||
361 | iphc0 |= LOWPAN_IPHC_TTL_1; | ||
362 | break; | ||
363 | case 64: | ||
364 | iphc0 |= LOWPAN_IPHC_TTL_64; | ||
365 | break; | ||
366 | case 255: | ||
367 | iphc0 |= LOWPAN_IPHC_TTL_255; | ||
368 | break; | ||
369 | default: | ||
370 | *hc06_ptr = hdr->hop_limit; | ||
371 | break; | ||
372 | } | ||
373 | |||
374 | /* source address compression */ | ||
375 | if (is_addr_unspecified(&hdr->saddr)) { | ||
376 | pr_debug("(%s): source address is unspecified, setting SAC\n", | ||
377 | __func__); | ||
378 | iphc1 |= LOWPAN_IPHC_SAC; | ||
379 | /* TODO: context lookup */ | ||
380 | } else if (is_addr_link_local(&hdr->saddr)) { | ||
381 | pr_debug("(%s): source address is link-local\n", __func__); | ||
382 | iphc1 |= lowpan_compress_addr_64(&hc06_ptr, | ||
383 | LOWPAN_IPHC_SAM_BIT, &hdr->saddr, saddr); | ||
384 | } else { | ||
385 | pr_debug("(%s): send the full source address\n", __func__); | ||
386 | memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16); | ||
387 | hc06_ptr += 16; | ||
388 | } | ||
389 | |||
390 | /* destination address compression */ | ||
391 | if (is_addr_mcast(&hdr->daddr)) { | ||
392 | pr_debug("(%s): destination address is multicast", __func__); | ||
393 | iphc1 |= LOWPAN_IPHC_M; | ||
394 | if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) { | ||
395 | pr_debug("compressed to 1 octet\n"); | ||
396 | iphc1 |= LOWPAN_IPHC_DAM_11; | ||
397 | /* use last byte */ | ||
398 | *hc06_ptr = hdr->daddr.s6_addr[15]; | ||
399 | hc06_ptr += 1; | ||
400 | } else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) { | ||
401 | pr_debug("compressed to 4 octets\n"); | ||
402 | iphc1 |= LOWPAN_IPHC_DAM_10; | ||
403 | /* second byte + the last three */ | ||
404 | *hc06_ptr = hdr->daddr.s6_addr[1]; | ||
405 | memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3); | ||
406 | hc06_ptr += 4; | ||
407 | } else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) { | ||
408 | pr_debug("compressed to 6 octets\n"); | ||
409 | iphc1 |= LOWPAN_IPHC_DAM_01; | ||
410 | /* second byte + the last five */ | ||
411 | *hc06_ptr = hdr->daddr.s6_addr[1]; | ||
412 | memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5); | ||
413 | hc06_ptr += 6; | ||
414 | } else { | ||
415 | pr_debug("using full address\n"); | ||
416 | iphc1 |= LOWPAN_IPHC_DAM_00; | ||
417 | memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16); | ||
418 | hc06_ptr += 16; | ||
419 | } | ||
420 | } else { | ||
421 | pr_debug("(%s): destination address is unicast: ", __func__); | ||
422 | /* TODO: context lookup */ | ||
423 | if (is_addr_link_local(&hdr->daddr)) { | ||
424 | pr_debug("destination address is link-local\n"); | ||
425 | iphc1 |= lowpan_compress_addr_64(&hc06_ptr, | ||
426 | LOWPAN_IPHC_DAM_BIT, &hdr->daddr, daddr); | ||
427 | } else { | ||
428 | pr_debug("using full address\n"); | ||
429 | memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16); | ||
430 | hc06_ptr += 16; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | /* TODO: UDP header compression */ | ||
435 | /* TODO: Next Header compression */ | ||
436 | |||
437 | head[0] = iphc0; | ||
438 | head[1] = iphc1; | ||
439 | |||
440 | skb_pull(skb, sizeof(struct ipv6hdr)); | ||
441 | memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head); | ||
442 | |||
443 | kfree(head); | ||
444 | |||
445 | lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data, | ||
446 | skb->len); | ||
447 | |||
448 | /* | ||
449 | * NOTE1: I'm still unsure about the fact that compression and WPAN | ||
450 | * header are created here and not later in the xmit. So wait for | ||
451 | * an opinion of net maintainers. | ||
452 | */ | ||
453 | /* | ||
454 | * NOTE2: to be absolutely correct, we must derive PANid information | ||
455 | * from MAC subif of the 'dev' and 'real_dev' network devices, but | ||
456 | * this isn't implemented in mainline yet, so currently we assign 0xff | ||
457 | */ | ||
458 | { | ||
459 | /* prepare wpan address data */ | ||
460 | sa.addr_type = IEEE802154_ADDR_LONG; | ||
461 | sa.pan_id = 0xff; | ||
462 | |||
463 | da.addr_type = IEEE802154_ADDR_LONG; | ||
464 | da.pan_id = 0xff; | ||
465 | |||
466 | memcpy(&(da.hwaddr), daddr, 8); | ||
467 | memcpy(&(sa.hwaddr), saddr, 8); | ||
468 | |||
469 | mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; | ||
470 | return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, | ||
471 | type, (void *)&da, (void *)&sa, skb->len); | ||
472 | } | ||
473 | } | ||
474 | |||
475 | static int lowpan_skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr) | ||
476 | { | ||
477 | struct sk_buff *new; | ||
478 | struct lowpan_dev_record *entry; | ||
479 | int stat = NET_RX_SUCCESS; | ||
480 | |||
481 | new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb), | ||
482 | GFP_KERNEL); | ||
483 | kfree_skb(skb); | ||
484 | |||
485 | if (NULL == new) | ||
486 | return -ENOMEM; | ||
487 | |||
488 | skb_push(new, sizeof(struct ipv6hdr)); | ||
489 | skb_reset_network_header(new); | ||
490 | skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr)); | ||
491 | |||
492 | new->protocol = htons(ETH_P_IPV6); | ||
493 | new->pkt_type = PACKET_HOST; | ||
494 | |||
495 | rcu_read_lock(); | ||
496 | list_for_each_entry_rcu(entry, &lowpan_devices, list) | ||
497 | if (lowpan_dev_info(entry->ldev)->real_dev == new->dev) { | ||
498 | skb = skb_copy(new, GFP_KERNEL); | ||
499 | skb->dev = entry->ldev; | ||
500 | |||
501 | if (in_interrupt()) | ||
502 | stat = netif_rx(skb); | ||
503 | else | ||
504 | stat = netif_rx_ni(skb); | ||
505 | } | ||
506 | rcu_read_unlock(); | ||
507 | |||
508 | kfree_skb(new); | ||
509 | |||
510 | return stat; | ||
511 | } | ||
512 | |||
513 | static int | ||
514 | lowpan_process_data(struct sk_buff *skb) | ||
515 | { | ||
516 | struct ipv6hdr hdr; | ||
517 | u8 tmp, iphc0, iphc1, num_context = 0; | ||
518 | u8 *_saddr, *_daddr; | ||
519 | int err; | ||
520 | |||
521 | lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data, | ||
522 | skb->len); | ||
523 | /* at least two bytes will be used for the encoding */ | ||
524 | if (skb->len < 2) | ||
525 | goto drop; | ||
526 | iphc0 = lowpan_fetch_skb_u8(skb); | ||
527 | iphc1 = lowpan_fetch_skb_u8(skb); | ||
528 | |||
529 | _saddr = mac_cb(skb)->sa.hwaddr; | ||
530 | _daddr = mac_cb(skb)->da.hwaddr; | ||
531 | |||
532 | pr_debug("(%s): iphc0 = %02x, iphc1 = %02x\n", __func__, iphc0, iphc1); | ||
533 | |||
534 | /* another if the CID flag is set */ | ||
535 | if (iphc1 & LOWPAN_IPHC_CID) { | ||
536 | pr_debug("(%s): CID flag is set, increase header with one\n", | ||
537 | __func__); | ||
538 | if (!skb->len) | ||
539 | goto drop; | ||
540 | num_context = lowpan_fetch_skb_u8(skb); | ||
541 | } | ||
542 | |||
543 | hdr.version = 6; | ||
544 | |||
545 | /* Traffic Class and Flow Label */ | ||
546 | switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) { | ||
547 | /* | ||
548 | * Traffic Class and FLow Label carried in-line | ||
549 | * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes) | ||
550 | */ | ||
551 | case 0: /* 00b */ | ||
552 | if (!skb->len) | ||
553 | goto drop; | ||
554 | tmp = lowpan_fetch_skb_u8(skb); | ||
555 | memcpy(&hdr.flow_lbl, &skb->data[0], 3); | ||
556 | skb_pull(skb, 3); | ||
557 | hdr.priority = ((tmp >> 2) & 0x0f); | ||
558 | hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) | | ||
559 | (hdr.flow_lbl[0] & 0x0f); | ||
560 | break; | ||
561 | /* | ||
562 | * Traffic class carried in-line | ||
563 | * ECN + DSCP (1 byte), Flow Label is elided | ||
564 | */ | ||
565 | case 1: /* 10b */ | ||
566 | if (!skb->len) | ||
567 | goto drop; | ||
568 | tmp = lowpan_fetch_skb_u8(skb); | ||
569 | hdr.priority = ((tmp >> 2) & 0x0f); | ||
570 | hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30); | ||
571 | hdr.flow_lbl[1] = 0; | ||
572 | hdr.flow_lbl[2] = 0; | ||
573 | break; | ||
574 | /* | ||
575 | * Flow Label carried in-line | ||
576 | * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided | ||
577 | */ | ||
578 | case 2: /* 01b */ | ||
579 | if (!skb->len) | ||
580 | goto drop; | ||
581 | tmp = lowpan_fetch_skb_u8(skb); | ||
582 | hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30); | ||
583 | memcpy(&hdr.flow_lbl[1], &skb->data[0], 2); | ||
584 | skb_pull(skb, 2); | ||
585 | break; | ||
586 | /* Traffic Class and Flow Label are elided */ | ||
587 | case 3: /* 11b */ | ||
588 | hdr.priority = 0; | ||
589 | hdr.flow_lbl[0] = 0; | ||
590 | hdr.flow_lbl[1] = 0; | ||
591 | hdr.flow_lbl[2] = 0; | ||
592 | break; | ||
593 | default: | ||
594 | break; | ||
595 | } | ||
596 | |||
597 | /* Next Header */ | ||
598 | if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) { | ||
599 | /* Next header is carried inline */ | ||
600 | if (!skb->len) | ||
601 | goto drop; | ||
602 | hdr.nexthdr = lowpan_fetch_skb_u8(skb); | ||
603 | pr_debug("(%s): NH flag is set, next header is carried " | ||
604 | "inline: %02x\n", __func__, hdr.nexthdr); | ||
605 | } | ||
606 | |||
607 | /* Hop Limit */ | ||
608 | if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I) | ||
609 | hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03]; | ||
610 | else { | ||
611 | if (!skb->len) | ||
612 | goto drop; | ||
613 | hdr.hop_limit = lowpan_fetch_skb_u8(skb); | ||
614 | } | ||
615 | |||
616 | /* Extract SAM to the tmp variable */ | ||
617 | tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03; | ||
618 | |||
619 | /* Source address uncompression */ | ||
620 | pr_debug("(%s): source address stateless compression\n", __func__); | ||
621 | err = lowpan_uncompress_addr(skb, &hdr.saddr, lowpan_llprefix, | ||
622 | lowpan_unc_llconf[tmp], skb->data); | ||
623 | if (err) | ||
624 | goto drop; | ||
625 | |||
626 | /* Extract DAM to the tmp variable */ | ||
627 | tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03; | ||
628 | |||
629 | /* check for Multicast Compression */ | ||
630 | if (iphc1 & LOWPAN_IPHC_M) { | ||
631 | if (iphc1 & LOWPAN_IPHC_DAC) { | ||
632 | pr_debug("(%s): destination address context-based " | ||
633 | "multicast compression\n", __func__); | ||
634 | /* TODO: implement this */ | ||
635 | } else { | ||
636 | u8 prefix[] = {0xff, 0x02}; | ||
637 | |||
638 | pr_debug("(%s): destination address non-context-based" | ||
639 | " multicast compression\n", __func__); | ||
640 | if (0 < tmp && tmp < 3) { | ||
641 | if (!skb->len) | ||
642 | goto drop; | ||
643 | else | ||
644 | prefix[1] = lowpan_fetch_skb_u8(skb); | ||
645 | } | ||
646 | |||
647 | err = lowpan_uncompress_addr(skb, &hdr.daddr, prefix, | ||
648 | lowpan_unc_mxconf[tmp], NULL); | ||
649 | if (err) | ||
650 | goto drop; | ||
651 | } | ||
652 | } else { | ||
653 | pr_debug("(%s): destination address stateless compression\n", | ||
654 | __func__); | ||
655 | err = lowpan_uncompress_addr(skb, &hdr.daddr, lowpan_llprefix, | ||
656 | lowpan_unc_llconf[tmp], skb->data); | ||
657 | if (err) | ||
658 | goto drop; | ||
659 | } | ||
660 | |||
661 | /* TODO: UDP header parse */ | ||
662 | |||
663 | /* Not fragmented package */ | ||
664 | hdr.payload_len = htons(skb->len); | ||
665 | |||
666 | pr_debug("(%s): skb headroom size = %d, data length = %d\n", __func__, | ||
667 | skb_headroom(skb), skb->len); | ||
668 | |||
669 | pr_debug("(%s): IPv6 header dump:\n\tversion = %d\n\tlength = %d\n\t" | ||
670 | "nexthdr = 0x%02x\n\thop_lim = %d\n", __func__, hdr.version, | ||
671 | ntohs(hdr.payload_len), hdr.nexthdr, hdr.hop_limit); | ||
672 | |||
673 | lowpan_raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, | ||
674 | sizeof(hdr)); | ||
675 | return lowpan_skb_deliver(skb, &hdr); | ||
676 | drop: | ||
677 | kfree(skb); | ||
678 | return -EINVAL; | ||
679 | } | ||
680 | |||
681 | static int lowpan_set_address(struct net_device *dev, void *p) | ||
682 | { | ||
683 | struct sockaddr *sa = p; | ||
684 | |||
685 | if (netif_running(dev)) | ||
686 | return -EBUSY; | ||
687 | |||
688 | /* TODO: validate addr */ | ||
689 | memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) | ||
695 | { | ||
696 | int err = 0; | ||
697 | |||
698 | pr_debug("(%s): package xmit\n", __func__); | ||
699 | |||
700 | skb->dev = lowpan_dev_info(dev)->real_dev; | ||
701 | if (skb->dev == NULL) { | ||
702 | pr_debug("(%s) ERROR: no real wpan device found\n", __func__); | ||
703 | dev_kfree_skb(skb); | ||
704 | } else | ||
705 | err = dev_queue_xmit(skb); | ||
706 | |||
707 | return (err < 0 ? NETDEV_TX_BUSY : NETDEV_TX_OK); | ||
708 | } | ||
709 | |||
710 | static void lowpan_dev_free(struct net_device *dev) | ||
711 | { | ||
712 | dev_put(lowpan_dev_info(dev)->real_dev); | ||
713 | free_netdev(dev); | ||
714 | } | ||
715 | |||
716 | static struct header_ops lowpan_header_ops = { | ||
717 | .create = lowpan_header_create, | ||
718 | }; | ||
719 | |||
720 | static const struct net_device_ops lowpan_netdev_ops = { | ||
721 | .ndo_start_xmit = lowpan_xmit, | ||
722 | .ndo_set_mac_address = lowpan_set_address, | ||
723 | }; | ||
724 | |||
725 | static void lowpan_setup(struct net_device *dev) | ||
726 | { | ||
727 | pr_debug("(%s)\n", __func__); | ||
728 | |||
729 | dev->addr_len = IEEE802154_ADDR_LEN; | ||
730 | memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); | ||
731 | dev->type = ARPHRD_IEEE802154; | ||
732 | dev->features = NETIF_F_NO_CSUM; | ||
733 | /* Frame Control + Sequence Number + Address fields + Security Header */ | ||
734 | dev->hard_header_len = 2 + 1 + 20 + 14; | ||
735 | dev->needed_tailroom = 2; /* FCS */ | ||
736 | dev->mtu = 1281; | ||
737 | dev->tx_queue_len = 0; | ||
738 | dev->flags = IFF_NOARP | IFF_BROADCAST; | ||
739 | dev->watchdog_timeo = 0; | ||
740 | |||
741 | dev->netdev_ops = &lowpan_netdev_ops; | ||
742 | dev->header_ops = &lowpan_header_ops; | ||
743 | dev->destructor = lowpan_dev_free; | ||
744 | } | ||
745 | |||
746 | static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) | ||
747 | { | ||
748 | pr_debug("(%s)\n", __func__); | ||
749 | |||
750 | if (tb[IFLA_ADDRESS]) { | ||
751 | if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) | ||
752 | return -EINVAL; | ||
753 | } | ||
754 | return 0; | ||
755 | } | ||
756 | |||
757 | static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, | ||
758 | struct packet_type *pt, struct net_device *orig_dev) | ||
759 | { | ||
760 | if (!netif_running(dev)) | ||
761 | goto drop; | ||
762 | |||
763 | if (dev->type != ARPHRD_IEEE802154) | ||
764 | goto drop; | ||
765 | |||
766 | /* check that it's our buffer */ | ||
767 | if ((skb->data[0] & 0xe0) == 0x60) | ||
768 | lowpan_process_data(skb); | ||
769 | |||
770 | return NET_RX_SUCCESS; | ||
771 | |||
772 | drop: | ||
773 | kfree_skb(skb); | ||
774 | return NET_RX_DROP; | ||
775 | } | ||
776 | |||
777 | static int lowpan_newlink(struct net *src_net, struct net_device *dev, | ||
778 | struct nlattr *tb[], struct nlattr *data[]) | ||
779 | { | ||
780 | struct net_device *real_dev; | ||
781 | struct lowpan_dev_record *entry; | ||
782 | |||
783 | pr_debug("(%s)\n", __func__); | ||
784 | |||
785 | if (!tb[IFLA_LINK]) | ||
786 | return -EINVAL; | ||
787 | /* find and hold real wpan device */ | ||
788 | real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); | ||
789 | if (!real_dev) | ||
790 | return -ENODEV; | ||
791 | |||
792 | lowpan_dev_info(dev)->real_dev = real_dev; | ||
793 | mutex_init(&lowpan_dev_info(dev)->dev_list_mtx); | ||
794 | |||
795 | entry = kzalloc(sizeof(struct lowpan_dev_record), GFP_KERNEL); | ||
796 | if (!entry) | ||
797 | return -ENOMEM; | ||
798 | |||
799 | entry->ldev = dev; | ||
800 | |||
801 | mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); | ||
802 | INIT_LIST_HEAD(&entry->list); | ||
803 | list_add_tail(&entry->list, &lowpan_devices); | ||
804 | mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); | ||
805 | |||
806 | register_netdevice(dev); | ||
807 | |||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | static void lowpan_dellink(struct net_device *dev, struct list_head *head) | ||
812 | { | ||
813 | struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); | ||
814 | struct net_device *real_dev = lowpan_dev->real_dev; | ||
815 | struct lowpan_dev_record *entry; | ||
816 | |||
817 | ASSERT_RTNL(); | ||
818 | |||
819 | mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); | ||
820 | list_for_each_entry(entry, &lowpan_devices, list) | ||
821 | if (entry->ldev == dev) { | ||
822 | list_del(&entry->list); | ||
823 | kfree(entry); | ||
824 | } | ||
825 | mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); | ||
826 | |||
827 | mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx); | ||
828 | |||
829 | unregister_netdevice_queue(dev, head); | ||
830 | |||
831 | dev_put(real_dev); | ||
832 | } | ||
833 | |||
834 | static struct rtnl_link_ops lowpan_link_ops __read_mostly = { | ||
835 | .kind = "lowpan", | ||
836 | .priv_size = sizeof(struct lowpan_dev_info), | ||
837 | .setup = lowpan_setup, | ||
838 | .newlink = lowpan_newlink, | ||
839 | .dellink = lowpan_dellink, | ||
840 | .validate = lowpan_validate, | ||
841 | }; | ||
842 | |||
843 | static inline int __init lowpan_netlink_init(void) | ||
844 | { | ||
845 | return rtnl_link_register(&lowpan_link_ops); | ||
846 | } | ||
847 | |||
848 | static inline void __init lowpan_netlink_fini(void) | ||
849 | { | ||
850 | rtnl_link_unregister(&lowpan_link_ops); | ||
851 | } | ||
852 | |||
853 | static struct packet_type lowpan_packet_type = { | ||
854 | .type = __constant_htons(ETH_P_IEEE802154), | ||
855 | .func = lowpan_rcv, | ||
856 | }; | ||
857 | |||
858 | static int __init lowpan_init_module(void) | ||
859 | { | ||
860 | int err = 0; | ||
861 | |||
862 | pr_debug("(%s)\n", __func__); | ||
863 | |||
864 | err = lowpan_netlink_init(); | ||
865 | if (err < 0) | ||
866 | goto out; | ||
867 | |||
868 | dev_add_pack(&lowpan_packet_type); | ||
869 | out: | ||
870 | return err; | ||
871 | } | ||
872 | |||
873 | static void __exit lowpan_cleanup_module(void) | ||
874 | { | ||
875 | pr_debug("(%s)\n", __func__); | ||
876 | |||
877 | lowpan_netlink_fini(); | ||
878 | |||
879 | dev_remove_pack(&lowpan_packet_type); | ||
880 | } | ||
881 | |||
882 | module_init(lowpan_init_module); | ||
883 | module_exit(lowpan_cleanup_module); | ||
884 | MODULE_LICENSE("GPL"); | ||
885 | MODULE_ALIAS_RTNL_LINK("lowpan"); | ||