]>
Commit | Line | Data |
---|---|---|
cb323159 A |
1 | /* |
2 | * Copyright (c) 2017-2019 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | /* | |
29 | * Copyright 1998 Massachusetts Institute of Technology | |
30 | * | |
31 | * Permission to use, copy, modify, and distribute this software and | |
32 | * its documentation for any purpose and without fee is hereby | |
33 | * granted, provided that both the above copyright notice and this | |
34 | * permission notice appear in all copies, that both the above | |
35 | * copyright notice and this permission notice appear in all | |
36 | * supporting documentation, and that the name of M.I.T. not be used | |
37 | * in advertising or publicity pertaining to distribution of the | |
38 | * software without specific, written prior permission. M.I.T. makes | |
39 | * no representations about the suitability of this software for any | |
40 | * purpose. It is provided "as is" without express or implied | |
41 | * warranty. | |
42 | * | |
43 | * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS | |
44 | * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
45 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
46 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT | |
47 | * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
48 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
49 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
50 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
51 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
52 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
53 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
54 | * SUCH DAMAGE. | |
55 | * | |
56 | */ | |
57 | /* | |
58 | * if_6lowpan.c - pseudo-device driver for IEEE 802.15.4 . | |
59 | */ | |
60 | #include <sys/param.h> | |
61 | #include <sys/kernel.h> | |
62 | #include <sys/malloc.h> | |
63 | #include <sys/mbuf.h> | |
64 | #include <sys/queue.h> | |
65 | #include <sys/socket.h> | |
66 | #include <sys/sockio.h> | |
67 | #include <sys/sysctl.h> | |
68 | #include <sys/systm.h> | |
69 | #include <sys/kern_event.h> | |
70 | #include <sys/mcache.h> | |
71 | ||
72 | #include <net/bpf.h> | |
73 | #include <net/ethernet.h> | |
74 | #include <net/if.h> | |
75 | #include <net/if_arp.h> | |
76 | #include <net/if_dl.h> | |
77 | #include <net/if_ether.h> | |
78 | #include <net/if_types.h> | |
79 | #include <net/if_6lowpan_var.h> | |
80 | #include <net/frame802154.h> | |
81 | #include <net/sixxlowpan.h> | |
82 | #include <libkern/OSAtomic.h> | |
83 | ||
84 | #include <net/dlil.h> | |
85 | ||
86 | #include <net/kpi_interface.h> | |
87 | #include <net/kpi_protocol.h> | |
88 | ||
89 | #include <kern/locks.h> | |
90 | ||
91 | #ifdef INET | |
92 | #include <netinet/in.h> | |
93 | #include <netinet/if_ether.h> | |
94 | #endif | |
95 | ||
96 | #include <net/if_media.h> | |
97 | #include <net/multicast_list.h> | |
98 | #include <net/ether_if_module.h> | |
99 | ||
100 | #define SIXLOWPANNAME "6lowpan" | |
101 | ||
102 | struct ifnet *p_6lowpan_ifnet = NULL; | |
103 | ||
104 | extern errno_t nd6_lookup_ipv6(ifnet_t interface, | |
105 | const struct sockaddr_in6 *ip6_dest, struct sockaddr_dl *ll_dest, | |
106 | size_t ll_dest_len, route_t hint, mbuf_t packet); | |
107 | ||
108 | ||
109 | typedef int (bpf_callback_func)(struct ifnet *, struct mbuf *); | |
110 | typedef int (if_set_bpf_tap_func)(struct ifnet *ifp, int mode, bpf_callback_func * func); | |
111 | ||
112 | static __inline__ lck_grp_t * | |
113 | my_lck_grp_alloc_init(const char * grp_name) | |
114 | { | |
115 | lck_grp_t * grp; | |
116 | lck_grp_attr_t * grp_attrs; | |
117 | ||
118 | grp_attrs = lck_grp_attr_alloc_init(); | |
119 | grp = lck_grp_alloc_init(grp_name, grp_attrs); | |
120 | lck_grp_attr_free(grp_attrs); | |
121 | return grp; | |
122 | } | |
123 | ||
124 | static __inline__ lck_mtx_t * | |
125 | my_lck_mtx_alloc_init(lck_grp_t * lck_grp) | |
126 | { | |
127 | lck_attr_t * lck_attrs; | |
128 | lck_mtx_t * lck_mtx; | |
129 | ||
130 | lck_attrs = lck_attr_alloc_init(); | |
131 | lck_mtx = lck_mtx_alloc_init(lck_grp, lck_attrs); | |
132 | lck_attr_free(lck_attrs); | |
133 | return lck_mtx; | |
134 | } | |
135 | ||
136 | static lck_mtx_t *sixlowpan_lck_mtx; | |
137 | ||
138 | static __inline__ void | |
139 | sixlowpan_lock_init(void) | |
140 | { | |
141 | lck_grp_t *lck_grp; | |
142 | ||
143 | lck_grp = my_lck_grp_alloc_init("if_6lowpan"); | |
144 | sixlowpan_lck_mtx = my_lck_mtx_alloc_init(lck_grp); | |
145 | } | |
146 | ||
147 | static __inline__ void | |
148 | sixlowpan_assert_lock_held(void) | |
149 | { | |
150 | lck_mtx_assert(sixlowpan_lck_mtx, LCK_MTX_ASSERT_OWNED); | |
151 | return; | |
152 | } | |
153 | ||
154 | #ifdef __UNUSED__ | |
155 | static __inline__ void | |
156 | sixlowpan_assert_lock_not_held(void) | |
157 | { | |
158 | lck_mtx_assert(sixlowpan_lck_mtx, LCK_MTX_ASSERT_NOTOWNED); | |
159 | return; | |
160 | } | |
161 | #endif | |
162 | ||
163 | static __inline__ void | |
164 | sixlowpan_lock(void) | |
165 | { | |
166 | lck_mtx_lock(sixlowpan_lck_mtx); | |
167 | return; | |
168 | } | |
169 | ||
170 | static __inline__ void | |
171 | sixlowpan_unlock(void) | |
172 | { | |
173 | lck_mtx_unlock(sixlowpan_lck_mtx); | |
174 | return; | |
175 | } | |
176 | ||
177 | struct if6lpan; | |
178 | LIST_HEAD(if6lpan_list, if6lpan); | |
179 | ||
180 | typedef LIST_ENTRY(if6lpan) | |
181 | if6lpan_entry; | |
182 | ||
183 | #define IF6LPAN_SIGNATURE 0x6666face | |
184 | struct if6lpan { | |
185 | if6lpan_entry if6lpan_list; | |
186 | char if6lpan_name[IFNAMSIZ]; /* our unique id */ | |
187 | char if6lpan_addr[IEEE802154_ADDR_LEN]; /* our LL address */ | |
188 | struct ifnet * if6lpan_ifp; /* our interface */ | |
189 | struct ifnet * if6lpan_pifp; /* parent interface */ | |
190 | #define IF6LPANF_DETACHING 0x1 /* interface is detaching */ | |
191 | #define IF6LPANF_READY 0x2 /* interface is ready */ | |
192 | u_int32_t if6lpan_flags; | |
193 | bpf_packet_func if6lpan_bpf_input; | |
194 | bpf_packet_func if6lpan_bpf_output; | |
195 | int32_t if6lpan_retain_count; | |
196 | u_int32_t if6lpan_signature; /* IF6LPAN_SIGNATURE */ | |
197 | u_int8_t if6lpan_ieee802154_seq; | |
198 | }; | |
199 | ||
200 | typedef struct if6lpan * if6lpan_ref; | |
201 | ||
202 | static __inline__ int | |
203 | if6lpan_flags_ready(if6lpan_ref ifl) | |
204 | { | |
205 | return (ifl->if6lpan_flags & IF6LPANF_READY) != 0; | |
206 | } | |
207 | ||
208 | static __inline__ void | |
209 | if6lpan_flags_set_ready(if6lpan_ref ifl) | |
210 | { | |
211 | ifl->if6lpan_flags |= IF6LPANF_READY; | |
212 | return; | |
213 | } | |
214 | ||
215 | static __inline__ void | |
216 | if6lpan_set_addr(if6lpan_ref ifl, caddr_t ether_addr) | |
217 | { | |
218 | ifl->if6lpan_addr[0] = 0x66; | |
219 | ifl->if6lpan_addr[1] = 0x66; | |
220 | bcopy(ether_addr, &ifl->if6lpan_addr[2], ETHER_ADDR_LEN); | |
221 | return; | |
222 | } | |
223 | ||
224 | #ifdef __UNUSED__ | |
225 | static __inline__ u_int8_t* | |
226 | if6lpan_get_addr(if6lpan_ref ifl) | |
227 | { | |
228 | return ifl->ifl6lpan_addr; | |
229 | } | |
230 | #endif | |
231 | ||
232 | static __inline__ int | |
233 | if6lpan_flags_detaching(if6lpan_ref ifl) | |
234 | { | |
235 | return (ifl->if6lpan_flags & IF6LPANF_DETACHING) != 0; | |
236 | } | |
237 | ||
238 | static __inline__ void | |
239 | if6lpan_flags_set_detaching(if6lpan_ref ifl) | |
240 | { | |
241 | ifl->if6lpan_flags |= IF6LPANF_DETACHING; | |
242 | return; | |
243 | } | |
244 | ||
245 | static int sixlowpan_clone_create(struct if_clone *, u_int32_t, void *); | |
246 | static int sixlowpan_clone_destroy(struct ifnet *); | |
247 | static int sixlowpan_input(ifnet_t ifp, protocol_family_t protocol, | |
248 | mbuf_t m, char *frame_header); | |
249 | static int sixlowpan_output(struct ifnet *ifp, struct mbuf *m); | |
250 | static int sixlowpan_ioctl(ifnet_t ifp, u_long cmd, void *addr); | |
251 | static int sixlowpan_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, | |
252 | bpf_packet_func func); | |
253 | static int sixlowpan_attach_protocol(struct ifnet *ifp); | |
254 | static int sixlowpan_detach_protocol(struct ifnet *ifp); | |
255 | static int sixlowpan_unconfig(if6lpan_ref ifl); | |
256 | static int sixlowpan_config(struct ifnet *ifp, struct ifnet *p); | |
257 | static void sixlowpan_if_free(struct ifnet *ifp); | |
258 | static int sixlowpan_remove(if6lpan_ref ifl); | |
259 | static int sixlowpan_framer_extended(struct ifnet *ifp, struct mbuf **m, | |
260 | const struct sockaddr *ndest, const char *edst, | |
261 | const char *ether_type, u_int32_t *prepend_len, u_int32_t *postpend_len); | |
262 | ||
263 | #define SIXLOWPAN_MAXUNIT IF_MAXUNIT | |
264 | #define SIXLOWPAN_ZONE_MAX_ELEM MIN(IFNETS_MAX, SIXLOWPAN_MAXUNIT) | |
265 | ||
266 | static struct if_clone sixlowpan_cloner = IF_CLONE_INITIALIZER(SIXLOWPANNAME, | |
267 | sixlowpan_clone_create, | |
268 | sixlowpan_clone_destroy, | |
269 | 0, | |
270 | SIXLOWPAN_MAXUNIT, | |
271 | SIXLOWPAN_ZONE_MAX_ELEM, | |
272 | sizeof(struct if6lpan)); | |
273 | ||
274 | /** | |
275 | ** if6lpan_ref routines | |
276 | **/ | |
277 | static void | |
278 | if6lpan_retain(if6lpan_ref ifl) | |
279 | { | |
280 | if (ifl->if6lpan_signature != IF6LPAN_SIGNATURE) { | |
281 | panic("if6lpan_retain: bad signature\n"); | |
282 | } | |
283 | if (ifl->if6lpan_retain_count == 0) { | |
284 | panic("if6lpan_retain: retain count is 0\n"); | |
285 | } | |
286 | OSIncrementAtomic(&ifl->if6lpan_retain_count); | |
287 | } | |
288 | ||
289 | static void | |
290 | if6lpan_release(if6lpan_ref ifl) | |
291 | { | |
292 | u_int32_t old_retain_count; | |
293 | ||
294 | if (ifl->if6lpan_signature != IF6LPAN_SIGNATURE) { | |
295 | panic("if6lpan_release: bad signature\n"); | |
296 | } | |
297 | old_retain_count = OSDecrementAtomic(&ifl->if6lpan_retain_count); | |
298 | switch (old_retain_count) { | |
299 | case 0: | |
300 | panic("if6lpan_release: retain count is 0\n"); | |
301 | break; | |
302 | case 1: | |
303 | ifl->if6lpan_signature = 0; | |
304 | if_clone_softc_deallocate(&sixlowpan_cloner, ifl); | |
305 | break; | |
306 | default: | |
307 | break; | |
308 | } | |
309 | return; | |
310 | } | |
311 | ||
312 | static if6lpan_ref | |
313 | ifnet_get_if6lpan(struct ifnet * ifp) | |
314 | { | |
315 | if6lpan_ref ifl; | |
316 | ||
317 | ifl = (if6lpan_ref)ifnet_softc(ifp); | |
318 | return ifl; | |
319 | } | |
320 | ||
321 | static if6lpan_ref | |
322 | ifnet_get_if6lpan_retained(struct ifnet * ifp) | |
323 | { | |
324 | if6lpan_ref ifl; | |
325 | ||
326 | ifl = ifnet_get_if6lpan(ifp); | |
327 | if (ifl == NULL) { | |
328 | return NULL; | |
329 | } | |
330 | if (if6lpan_flags_detaching(ifl)) { | |
331 | return NULL; | |
332 | } | |
333 | if6lpan_retain(ifl); | |
334 | return ifl; | |
335 | } | |
336 | ||
337 | static int | |
338 | sixlowpan_clone_attach(void) | |
339 | { | |
340 | int error; | |
341 | ||
342 | error = if_clone_attach(&sixlowpan_cloner); | |
343 | if (error != 0) { | |
344 | return error; | |
345 | } | |
346 | sixlowpan_lock_init(); | |
347 | return 0; | |
348 | } | |
349 | ||
350 | static int | |
351 | sixlowpan_demux( | |
352 | __unused ifnet_t ifp, | |
353 | __unused mbuf_t m, | |
354 | __unused char *frame_header, | |
355 | protocol_family_t *protocol_family) | |
356 | { | |
357 | *protocol_family = PF_INET6; | |
358 | return 0; | |
359 | } | |
360 | ||
361 | static errno_t | |
362 | sixlowpan_add_proto(__unused ifnet_t interface, protocol_family_t protocol, | |
363 | __unused const struct ifnet_demux_desc *demux_array, | |
364 | __unused u_int32_t demux_count) | |
365 | { | |
366 | if (protocol == PF_INET6) { | |
367 | return 0; | |
368 | } | |
369 | return ENOPROTOOPT; | |
370 | } | |
371 | ||
372 | static errno_t | |
373 | sixlowpan_del_proto(__unused ifnet_t interface, __unused protocol_family_t protocol) | |
374 | { | |
375 | return 0; | |
376 | } | |
377 | ||
378 | static int | |
379 | sixlowpan_clone_create(struct if_clone *ifc, u_int32_t unit, __unused void *params) | |
380 | { | |
381 | int error; | |
382 | if6lpan_ref ifl; | |
383 | ifnet_t ifp; | |
384 | struct ifnet_init_eparams if_epraram; | |
385 | ||
386 | ifl = if_clone_softc_allocate(&sixlowpan_cloner); | |
387 | if (ifl == NULL) { | |
388 | return ENOBUFS; | |
389 | } | |
390 | ifl->if6lpan_retain_count = 1; | |
391 | ifl->if6lpan_signature = IF6LPAN_SIGNATURE; | |
392 | ||
393 | /* use the interface name as the unique id for ifp recycle */ | |
394 | if ((unsigned int) | |
395 | snprintf(ifl->if6lpan_name, sizeof(ifl->if6lpan_name), "%s%d", | |
396 | ifc->ifc_name, unit) >= sizeof(ifl->if6lpan_name)) { | |
397 | if6lpan_release(ifl); | |
398 | return EINVAL; | |
399 | } | |
400 | ||
401 | bzero(&if_epraram, sizeof(if_epraram)); | |
402 | if_epraram.ver = IFNET_INIT_CURRENT_VERSION; | |
403 | if_epraram.len = sizeof(if_epraram); | |
404 | if_epraram.flags = IFNET_INIT_LEGACY; | |
405 | if_epraram.uniqueid = ifl->if6lpan_name; | |
406 | if_epraram.uniqueid_len = strlen(ifl->if6lpan_name); | |
407 | if_epraram.name = ifc->ifc_name; | |
408 | if_epraram.unit = unit; | |
409 | if_epraram.family = IFNET_FAMILY_6LOWPAN; | |
410 | if_epraram.type = IFT_6LOWPAN; | |
411 | if_epraram.output = sixlowpan_output; | |
412 | if_epraram.demux = sixlowpan_demux; | |
413 | if_epraram.add_proto = sixlowpan_add_proto; | |
414 | if_epraram.del_proto = sixlowpan_del_proto; | |
415 | if_epraram.framer_extended = sixlowpan_framer_extended; | |
416 | if_epraram.softc = ifl; | |
417 | if_epraram.ioctl = sixlowpan_ioctl; | |
418 | if_epraram.set_bpf_tap = sixlowpan_set_bpf_tap; | |
419 | if_epraram.detach = sixlowpan_if_free; | |
420 | error = ifnet_allocate_extended(&if_epraram, &ifp); | |
421 | ||
422 | if (error) { | |
423 | if6lpan_release(ifl); | |
424 | return error; | |
425 | } | |
426 | ||
427 | ifnet_set_offload(ifp, 0); | |
428 | ifnet_set_addrlen(ifp, IEEE802154_ADDR_LEN); | |
429 | ifnet_set_baudrate(ifp, 0); | |
430 | // TODO: ifnet_set_hdrlen(ifp, IEEE802154_ENCAP_LEN); | |
431 | ||
432 | error = ifnet_attach(ifp, NULL); | |
433 | if (error) { | |
434 | ifnet_release(ifp); | |
435 | if6lpan_release(ifl); | |
436 | return error; | |
437 | } | |
438 | ifl->if6lpan_ifp = ifp; | |
439 | ||
440 | p_6lowpan_ifnet = ifp; | |
441 | /* TODO: attach as IEEE 802.15.4 with no FCS */ | |
442 | bpfattach(ifp, DLT_IEEE802_15_4_NOFCS, IEEE802154_ENCAP_LEN); | |
443 | return 0; | |
444 | } | |
445 | ||
446 | static int | |
447 | sixlowpan_remove(if6lpan_ref ifl) | |
448 | { | |
449 | sixlowpan_assert_lock_held(); | |
450 | if (if6lpan_flags_detaching(ifl)) { | |
451 | return 0; | |
452 | } | |
453 | if6lpan_flags_set_detaching(ifl); | |
454 | sixlowpan_unconfig(ifl); | |
455 | return 1; | |
456 | } | |
457 | ||
458 | ||
459 | static int | |
460 | sixlowpan_clone_destroy(struct ifnet *ifp) | |
461 | { | |
462 | if6lpan_ref ifl; | |
463 | ||
464 | sixlowpan_lock(); | |
465 | ifl = ifnet_get_if6lpan_retained(ifp); | |
466 | if (ifl == NULL) { | |
467 | sixlowpan_unlock(); | |
468 | return 0; | |
469 | } | |
470 | if (sixlowpan_remove(ifl) == 0) { | |
471 | sixlowpan_unlock(); | |
472 | if6lpan_release(ifl); | |
473 | return 0; | |
474 | } | |
475 | sixlowpan_unlock(); | |
476 | if6lpan_release(ifl); | |
477 | ifnet_detach(ifp); | |
478 | p_6lowpan_ifnet = NULL; | |
479 | return 0; | |
480 | } | |
481 | ||
482 | static int | |
483 | sixlowpan_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, bpf_packet_func func) | |
484 | { | |
485 | if6lpan_ref ifl; | |
486 | ||
487 | sixlowpan_lock(); | |
488 | ifl = ifnet_get_if6lpan_retained(ifp); | |
489 | if (ifl == NULL) { | |
490 | sixlowpan_unlock(); | |
491 | return ENODEV; | |
492 | } | |
493 | switch (mode) { | |
494 | case BPF_TAP_DISABLE: | |
495 | ifl->if6lpan_bpf_input = ifl->if6lpan_bpf_output = NULL; | |
496 | break; | |
497 | ||
498 | case BPF_TAP_INPUT: | |
499 | ifl->if6lpan_bpf_input = func; | |
500 | break; | |
501 | ||
502 | case BPF_TAP_OUTPUT: | |
503 | ifl->if6lpan_bpf_output = func; | |
504 | break; | |
505 | ||
506 | case BPF_TAP_INPUT_OUTPUT: | |
507 | ifl->if6lpan_bpf_input = ifl->if6lpan_bpf_output = func; | |
508 | break; | |
509 | default: | |
510 | break; | |
511 | } | |
512 | sixlowpan_unlock(); | |
513 | if6lpan_release(ifl); | |
514 | return 0; | |
515 | } | |
516 | ||
517 | /* | |
518 | * 6lowpan output routine. | |
519 | * Header compression on the protocol payload | |
520 | * Frame the compressed payload in 802.15.4 Data Frame | |
521 | * Encapsulate the 802.15.4 frame in an Ethernet frame. | |
522 | */ | |
523 | static int | |
524 | sixlowpan_output(struct ifnet * ifp, struct mbuf * m) | |
525 | { | |
526 | struct ifnet *p_intf = NULL; | |
527 | if6lpan_ref ifl = NULL; | |
528 | struct flowadv adv = { .code = FADV_SUCCESS }; | |
529 | int err = 0; | |
530 | char link_layer_dest[ETHER_ADDR_LEN]; | |
531 | bpf_packet_func bpf_func; | |
532 | ||
533 | u_int16_t ethertype = htons(ETHERTYPE_IEEE802154); | |
534 | memset(link_layer_dest, 0xff, ETHER_ADDR_LEN); | |
535 | ||
536 | if (m == 0) { | |
537 | return 0; | |
538 | } | |
539 | if ((m->m_flags & M_PKTHDR) == 0) { | |
540 | m_freem_list(m); | |
541 | return 0; | |
542 | } | |
543 | ||
544 | sixlowpan_lock(); | |
545 | ifl = ifnet_get_if6lpan_retained(ifp); | |
546 | ||
547 | if (ifl == NULL || if6lpan_flags_ready(ifl) == 0) { | |
548 | goto unlock_done; | |
549 | } | |
550 | ||
551 | /* XXX parent interface equivalent? */ | |
552 | p_intf = ifl->if6lpan_pifp; | |
553 | bpf_func = ifl->if6lpan_bpf_output; | |
554 | ||
555 | sixlowpan_unlock(); | |
556 | if6lpan_release(ifl); | |
557 | ||
558 | (void)ifnet_stat_increment_out(ifp, 1, m->m_pkthdr.len, 0); | |
559 | ||
560 | /* | |
561 | * We added a 2 byte length before the 802.15.4 data frame | |
562 | * We can play just with the length of the first mbuf in the | |
563 | * chain because bpf_tap_imp() disregards the packet length | |
564 | * of the mbuf packet header. | |
565 | */ | |
566 | if (bpf_func && (mbuf_setdata(m, m->m_data + 2, m->m_len - 2) == 0)) { | |
567 | bpf_func(ifp, m); | |
568 | mbuf_setdata(m, m->m_data - 2, m->m_len + 2); | |
569 | } | |
570 | ||
571 | /* Append ethernet header */ | |
572 | if ((err = ether_frameout_extended(p_intf, &m, NULL, | |
573 | link_layer_dest, (const char *)ðertype, | |
574 | NULL, NULL))) { | |
575 | return err; | |
576 | } | |
577 | ||
578 | err = dlil_output(p_intf, PF_802154, m, NULL, NULL, 1, &adv); | |
579 | ||
580 | if (err == 0) { | |
581 | if (adv.code == FADV_FLOW_CONTROLLED) { | |
582 | err = EQFULL; | |
583 | } else if (adv.code == FADV_SUSPENDED) { | |
584 | err = EQSUSPENDED; | |
585 | } | |
586 | } | |
587 | return err; | |
588 | ||
589 | unlock_done: | |
590 | sixlowpan_unlock(); | |
591 | if (ifl != NULL) { | |
592 | if6lpan_release(ifl); | |
593 | } | |
594 | m_freem(m); | |
595 | return err; | |
596 | } | |
597 | ||
598 | /* | |
599 | * 6lowpan input routine. | |
600 | * Decapsulate the 802.15.4 Data Frame | |
601 | * Header decompression on the payload | |
602 | * Pass the mbuf to the IPV6 protocol stack using proto_input() | |
603 | */ | |
604 | static int | |
605 | sixlowpan_input(ifnet_t p, __unused protocol_family_t protocol, | |
606 | mbuf_t m, __unused char *frame_header) | |
607 | { | |
608 | frame802154_t ieee02154hdr; | |
609 | u_int8_t *payload = NULL; | |
610 | if6lpan_ref ifl = NULL; | |
611 | bpf_packet_func bpf_func; | |
612 | mbuf_t mc, m_temp; | |
613 | int off, err = 0; | |
614 | u_int16_t len; | |
615 | ||
616 | /* Allocate an mbuf cluster for the 802.15.4 frame and uncompressed payload */ | |
617 | mc = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR); | |
618 | if (mc == NULL) { | |
619 | err = -1; | |
620 | goto err_out; | |
621 | } | |
622 | ||
623 | memcpy(&len, mtod(m, u_int8_t *), sizeof(u_int16_t)); | |
624 | len = ntohs(len); | |
625 | m_adj(m, sizeof(u_int16_t)); | |
626 | /* Copy the compressed 802.15.4 payload from source mbuf to allocated cluster mbuf */ | |
627 | for (m_temp = m, off = 0; m_temp != NULL; m_temp = m_temp->m_next) { | |
628 | if (m_temp->m_len > 0) { | |
629 | m_copyback(mc, off, m_temp->m_len, mtod(m_temp, void *)); | |
630 | off += m_temp->m_len; | |
631 | } | |
632 | } | |
633 | ||
634 | p = p_6lowpan_ifnet; | |
635 | mc->m_pkthdr.rcvif = p; | |
636 | ||
637 | sixlowpan_lock(); | |
638 | ifl = ifnet_get_if6lpan_retained(p); | |
639 | ||
640 | if (ifl == NULL) { | |
641 | sixlowpan_unlock(); | |
642 | err = -1; | |
643 | goto err_out; | |
644 | } | |
645 | ||
646 | if (if6lpan_flags_ready(ifl) == 0) { | |
647 | if6lpan_release(ifl); | |
648 | sixlowpan_unlock(); | |
649 | err = -1; | |
650 | goto err_out; | |
651 | } | |
652 | ||
653 | bpf_func = ifl->if6lpan_bpf_input; | |
654 | sixlowpan_unlock(); | |
655 | if6lpan_release(ifl); | |
656 | ||
657 | if (bpf_func) { | |
658 | bpf_func(p, mc); | |
659 | } | |
660 | ||
661 | /* Parse the 802.15.4 frame header */ | |
662 | bzero(&ieee02154hdr, sizeof(ieee02154hdr)); | |
663 | frame802154_parse(mtod(mc, uint8_t *), len, &ieee02154hdr, &payload); | |
664 | ||
665 | /* XXX Add check for your link layer address being dest */ | |
666 | sixxlowpan_input(&ieee02154hdr, payload); | |
667 | ||
668 | if (mbuf_setdata(mc, payload, ieee02154hdr.payload_len)) { | |
669 | err = -1; | |
670 | goto err_out; | |
671 | } | |
672 | mbuf_pkthdr_setlen(mc, ieee02154hdr.payload_len); | |
673 | ||
674 | /* Post decompression */ | |
675 | if (proto_input(PF_INET6, mc) != 0) { | |
676 | ifnet_stat_increment_in(p, 0, 0, 1); | |
677 | err = -1; | |
678 | goto err_out; | |
679 | } else { | |
680 | ifnet_stat_increment_in(p, 1, mc->m_pkthdr.len, 0); | |
681 | } | |
682 | ||
683 | err_out: | |
684 | if (err && mc) { | |
685 | m_freem(mc); | |
686 | } | |
687 | if (!err) { | |
688 | m_freem(m); | |
689 | } | |
690 | return err; | |
691 | } | |
692 | ||
693 | #define SIXLOWPAN_IFMTU 1280 | |
694 | ||
695 | static int | |
696 | sixlowpan_config(struct ifnet *ifp, struct ifnet *p) | |
697 | { | |
698 | if6lpan_ref ifl; | |
699 | u_int16_t parent_flags; | |
700 | sixlowpan_lock(); | |
701 | ifl = ifnet_get_if6lpan_retained(ifp); | |
702 | if (ifl == NULL || ifl->if6lpan_pifp != NULL) { | |
703 | sixlowpan_unlock(); | |
704 | if (ifl != NULL) { | |
705 | if6lpan_release(ifl); | |
706 | } | |
707 | return EBUSY; | |
708 | } | |
709 | sixlowpan_attach_protocol(p); | |
710 | ||
711 | /* set our LL address derived from that of the parent */ | |
712 | if6lpan_set_addr(ifl, IF_LLADDR(p)); | |
713 | ifnet_set_lladdr_and_type(ifp, ifl->if6lpan_addr, IEEE802154_ADDR_LEN, IFT_6LOWPAN); | |
714 | ||
715 | ifl->if6lpan_pifp = p; | |
716 | ifl->if6lpan_flags = 0; | |
717 | ifnet_set_mtu(ifp, SIXLOWPAN_IFMTU); | |
718 | parent_flags = ifnet_flags(p) & (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX); | |
719 | ifnet_set_flags(ifp, parent_flags, IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX); | |
720 | ifnet_set_flags(ifp, IFF_RUNNING, IFF_RUNNING); | |
721 | ifnet_set_eflags(ifp, IFEF_NOAUTOIPV6LL, IFEF_NOAUTOIPV6LL); | |
722 | if6lpan_flags_set_ready(ifl); | |
723 | if6lpan_release(ifl); | |
724 | sixlowpan_unlock(); | |
725 | return 0; | |
726 | } | |
727 | ||
728 | static int | |
729 | sixlowpan_unconfig(if6lpan_ref ifl) | |
730 | { | |
731 | struct ifnet *ifp = ifl->if6lpan_ifp; | |
732 | ||
733 | sixlowpan_assert_lock_held(); | |
734 | /* Clear our MAC address. */ | |
735 | ifnet_set_lladdr_and_type(ifp, NULL, 0, IFT_6LOWPAN); | |
736 | sixlowpan_detach_protocol(ifl->if6lpan_pifp); | |
737 | ifnet_set_mtu(ifp, 0); | |
738 | ifnet_set_flags(ifp, 0, | |
739 | IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_RUNNING); | |
740 | ifnet_set_eflags(ifp, 0, IFEF_NOAUTOIPV6LL); | |
741 | ifl->if6lpan_flags = 0; | |
742 | ||
743 | return 0; | |
744 | } | |
745 | ||
746 | static int | |
747 | sixlowpan_ioctl(ifnet_t ifp, u_long cmd, void * data) | |
748 | { | |
749 | int error = 0; | |
750 | struct ifreq * ifr = NULL; | |
751 | struct ifnet * p = NULL; | |
752 | struct sixlowpanreq req = {}; | |
753 | user_addr_t user_addr = 0; | |
754 | if6lpan_ref ifl = NULL; | |
755 | ||
756 | if (ifnet_type(ifp) != IFT_6LOWPAN) { | |
757 | return EOPNOTSUPP; | |
758 | } | |
759 | ifr = (struct ifreq *)data; | |
760 | ||
761 | switch (cmd) { | |
762 | case SIOCSIFADDR: | |
763 | ifnet_set_flags(ifp, IFF_UP, IFF_UP); | |
764 | break; | |
765 | ||
766 | case SIOCSIF6LOWPAN: | |
767 | user_addr = proc_is64bit(current_proc()) | |
768 | ? ifr->ifr_data64 : CAST_USER_ADDR_T(ifr->ifr_data); | |
769 | error = copyin(user_addr, &req, sizeof(req)); | |
770 | req.parent[IFNAMSIZ - 1] = '\0'; | |
771 | if (error) { | |
772 | break; | |
773 | } | |
774 | if (req.parent[0] != '\0') { | |
775 | p = ifunit(req.parent); | |
776 | if (p == NULL) { | |
777 | error = ENXIO; | |
778 | break; | |
779 | } | |
780 | if (ifnet_type(p) != IFT_ETHER | |
781 | && ifnet_type(p) != IFT_IEEE8023ADLAG) { | |
782 | error = EPROTONOSUPPORT; | |
783 | break; | |
784 | } | |
785 | error = sixlowpan_config(ifp, p); | |
786 | if (error) { | |
787 | break; | |
788 | } | |
789 | } | |
790 | break; | |
791 | ||
792 | case SIOCGIF6LOWPAN: | |
793 | bzero(&req, sizeof req); | |
794 | sixlowpan_lock(); | |
795 | ifl = (if6lpan_ref)ifnet_softc(ifp); | |
796 | if (ifl == NULL || if6lpan_flags_detaching(ifl)) { | |
797 | sixlowpan_unlock(); | |
798 | return ifl == NULL ? EOPNOTSUPP : EBUSY; | |
799 | } | |
800 | p = ifl->if6lpan_pifp; | |
801 | sixlowpan_unlock(); | |
802 | if (p != NULL) { | |
803 | snprintf(req.parent, sizeof(req.parent), | |
804 | "%s%d", ifnet_name(p), ifnet_unit(p)); | |
805 | } | |
806 | user_addr = proc_is64bit(current_proc()) | |
807 | ? ifr->ifr_data64 : CAST_USER_ADDR_T(ifr->ifr_data); | |
808 | error = copyout(&req, user_addr, sizeof(req)); | |
809 | break; | |
810 | ||
811 | #ifdef SIOCSIFMTU /* xxx */ | |
812 | case SIOCGIFMTU: | |
813 | break; | |
814 | ||
815 | case SIOCSIFMTU: | |
816 | ifnet_set_mtu(ifp, ifr->ifr_mtu); | |
817 | break; | |
818 | #endif /* SIOCSIFMTU */ | |
819 | ||
820 | default: | |
821 | error = EOPNOTSUPP; | |
822 | } | |
823 | return error; | |
824 | } | |
825 | ||
826 | static void | |
827 | sixlowpan_if_free(struct ifnet * ifp) | |
828 | { | |
829 | if6lpan_ref ifl; | |
830 | ||
831 | if (ifp == NULL) { | |
832 | return; | |
833 | } | |
834 | ifl = (if6lpan_ref)ifnet_softc(ifp); | |
835 | if (ifl == NULL) { | |
836 | return; | |
837 | } | |
838 | if6lpan_release(ifl); | |
839 | ifnet_release(ifp); | |
840 | return; | |
841 | } | |
842 | ||
843 | static errno_t | |
844 | sixlowpan_detached(ifnet_t p, __unused protocol_family_t protocol) | |
845 | { | |
846 | if (ifnet_is_attached(p, 0) == 0) { | |
847 | // TODO: Find ifp from the parent p | |
848 | // sixlowpan_if_free(ifp); | |
849 | } | |
850 | return 0; | |
851 | } | |
852 | ||
853 | /* | |
854 | * Function: sixlowpan_attach_protocol | |
855 | * Purpose: | |
856 | * Attach a DLIL protocol to the interface | |
857 | * The ethernet demux actually special cases 802.15.4. | |
858 | * The demux here isn't used. The demux will return PF_802154 for the | |
859 | * appropriate packets and our sixlowpan_input function will be called. | |
860 | */ | |
861 | static int | |
862 | sixlowpan_attach_protocol(struct ifnet *ifp) | |
863 | { | |
864 | int error; | |
865 | struct ifnet_attach_proto_param reg; | |
866 | ||
867 | bzero(®, sizeof(reg)); | |
868 | reg.input = sixlowpan_input; | |
869 | reg.detached = sixlowpan_detached; | |
870 | error = ifnet_attach_protocol(ifp, PF_802154, ®); | |
871 | if (error) { | |
872 | printf("%s(%s%d) ifnet_attach_protocol failed, %d\n", | |
873 | __func__, ifnet_name(ifp), ifnet_unit(ifp), error); | |
874 | } | |
875 | return error; | |
876 | } | |
877 | ||
878 | /* | |
879 | * Function: sixlowpan_detach_protocol | |
880 | * Purpose: | |
881 | * Detach our DLIL protocol from an interface | |
882 | */ | |
883 | static int | |
884 | sixlowpan_detach_protocol(struct ifnet *ifp) | |
885 | { | |
886 | int error; | |
887 | ||
888 | error = ifnet_detach_protocol(ifp, PF_802154); | |
889 | if (error) { | |
890 | printf("(%s%d) ifnet_detach_protocol failed, %d\n", | |
891 | ifnet_name(ifp), ifnet_unit(ifp), error); | |
892 | } | |
893 | ||
894 | return error; | |
895 | } | |
896 | ||
897 | static errno_t | |
898 | sixlowpan_proto_pre_output(ifnet_t ifp, | |
899 | __unused protocol_family_t protocol_family, | |
900 | mbuf_t *m0, | |
901 | const struct sockaddr *dest, | |
902 | void *route, | |
903 | char *type, | |
904 | char *ll_dest) | |
905 | { | |
906 | #pragma unused(protocol_family) | |
907 | errno_t result = 0; | |
908 | struct sockaddr_dl sdl; | |
909 | struct sockaddr_in6 *dest6 = (struct sockaddr_in6 *)(uintptr_t)(size_t)dest; | |
910 | ||
911 | if (!IN6_IS_ADDR_MULTICAST(&dest6->sin6_addr)) { | |
912 | result = nd6_lookup_ipv6(ifp, dest6, &sdl, sizeof(sdl), route, *m0); | |
913 | if (result == 0) { | |
914 | bcopy(LLADDR(&sdl), ll_dest, sdl.sdl_alen); | |
915 | } | |
916 | } else { | |
917 | /* map multicast address */ | |
918 | ll_dest[0] = (dest6->sin6_addr.s6_addr8[14] & 0x1f) | 0x80; | |
919 | ll_dest[1] = dest6->sin6_addr.s6_addr8[15]; | |
920 | } | |
921 | ||
922 | /* | |
923 | * XXX This should be generic to the underlying hardware type | |
924 | */ | |
925 | if (result == 0) { | |
926 | u_int16_t ethertype = htons(ETHERTYPE_IEEE802154); | |
927 | bcopy(ðertype, type, sizeof(ethertype)); | |
928 | } | |
929 | ||
930 | return result; | |
931 | } | |
932 | ||
933 | static int | |
934 | sixlowpan_framer_extended(struct ifnet *ifp, struct mbuf **m, | |
935 | const struct sockaddr *ndest, const char *edst, | |
936 | const char *ether_type, u_int32_t *prepend_len, u_int32_t *postpend_len) | |
937 | { | |
938 | #pragma unused(ndest) | |
939 | #pragma unused(ether_type) | |
940 | char buf[IEEE802154_ENCAP_LEN] = {0}; | |
941 | int buflen = 0, err = 0; | |
942 | frame802154_t ieee02154hdr; | |
943 | if6lpan_ref ifl = NULL; | |
944 | u_int8_t *payload = NULL; | |
945 | struct mbuf *mc = NULL; | |
946 | u_int16_t len; | |
947 | struct sockaddr_in6 *dest6 = (struct sockaddr_in6 *)(uintptr_t)(size_t)ndest; | |
948 | ||
949 | /* Initialize 802.15.4 frame header */ | |
950 | bzero(&ieee02154hdr, sizeof(ieee02154hdr)); | |
951 | if (!IN6_IS_ADDR_MULTICAST(&dest6->sin6_addr)) { | |
952 | bcopy(edst, ieee02154hdr.dest_addr, sizeof(ieee02154hdr.dest_addr)); | |
953 | ieee02154hdr.fcf.dest_addr_mode = FRAME802154_LONGADDRMODE; | |
954 | } else { | |
955 | bcopy(edst, ieee02154hdr.dest_addr, 2); | |
956 | ieee02154hdr.fcf.dest_addr_mode = FRAME802154_SHORTADDRMODE; | |
957 | } | |
958 | ||
959 | /* Allocate a contiguous buffer for IPv6 header & payload */ | |
960 | /* | |
961 | * XXX As of now either we compress or we don't compress at all | |
962 | * adding another byte of dispatch to communicate that there's no | |
963 | * compression. | |
964 | * | |
965 | * Allocate for the worst case. | |
966 | */ | |
967 | payload = _MALLOC(m_pktlen(*m) + 1, M_TEMP, M_WAITOK | M_ZERO); | |
968 | if (payload == NULL) { | |
969 | err = -1; | |
970 | goto err_out; | |
971 | } | |
972 | ||
973 | /* Copy the IPv6 header & payload */ | |
974 | if (mbuf_copydata(*m, 0, m_pktlen(*m), payload)) { | |
975 | err = -1; | |
976 | goto err_out; | |
977 | } | |
978 | ||
979 | /* Allocate an mbuf cluster for the 802.15.4 frame and compressed payload */ | |
980 | mc = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR); | |
981 | if (mc == NULL) { | |
982 | err = -1; | |
983 | goto err_out; | |
984 | } | |
985 | ||
986 | sixlowpan_lock(); | |
987 | ifl = ifnet_get_if6lpan_retained(ifp); | |
988 | if (ifl == NULL || if6lpan_flags_ready(ifl) == 0) { | |
989 | if (ifl != NULL) { | |
990 | if6lpan_release(ifl); | |
991 | } | |
992 | sixlowpan_unlock(); | |
993 | err = -1; | |
994 | goto err_out; | |
995 | } | |
996 | bcopy(ifl->if6lpan_addr, ieee02154hdr.src_addr, sizeof(ieee02154hdr.src_addr)); | |
997 | ieee02154hdr.seq = ifl->if6lpan_ieee802154_seq++; /**< Sequence number */ | |
998 | if6lpan_release(ifl); | |
999 | sixlowpan_unlock(); | |
1000 | ||
1001 | /* Initialize frame control field */ | |
1002 | ieee02154hdr.fcf.frame_type = FRAME802154_DATAFRAME; /**< 3 bit. Frame type field, see 802.15.4 */ | |
1003 | ieee02154hdr.fcf.security_enabled = 0; /**< 1 bit. True if security is used in this frame */ | |
1004 | ieee02154hdr.fcf.frame_pending = 0; /**< 1 bit. True if sender has more data to send */ | |
1005 | ieee02154hdr.fcf.ack_required = 0; /**< 1 bit. Is an ack frame required? */ | |
1006 | ieee02154hdr.fcf.panid_compression = 0; /**< 1 bit. Is this a compressed header? */ | |
1007 | ieee02154hdr.fcf.frame_version = FRAME802154_IEEE802154_2006; /**< 2 bit. 802.15.4 frame version */ | |
1008 | ieee02154hdr.fcf.src_addr_mode = FRAME802154_LONGADDRMODE; /**< 2 bit. Source address mode, see 802.15.4 */ | |
1009 | ieee02154hdr.dest_pid = IEEE802154_PANID; /**< Destination PAN ID */ | |
1010 | ieee02154hdr.src_pid = IEEE802154_PANID; /**< Source PAN ID */ | |
1011 | ieee02154hdr.payload_len = m_pktlen(*m); /**< Length of payload field */ | |
1012 | ||
1013 | /* Create an 802.15.4 Data header frame */ | |
1014 | buflen = frame802154_create(&ieee02154hdr, (uint8_t *)buf); | |
1015 | ||
1016 | /* Perform inline compression of the IPv6 hdr & payload */ | |
1017 | sixxlowpan_output(&ieee02154hdr, payload); | |
1018 | ||
1019 | /* | |
1020 | * Add 2 bytes at the front of the frame indicating the total payload | |
1021 | * length | |
1022 | */ | |
1023 | len = htons(buflen + ieee02154hdr.payload_len); | |
1024 | m_copyback(mc, 0, sizeof(len), &len); | |
1025 | /* Copy back the 802.15.4 Data frame header into mbuf */ | |
1026 | m_copyback(mc, sizeof(len), buflen, buf); | |
1027 | /* Copy back the compressed payload into mbuf */ | |
1028 | m_copyback(mc, buflen + sizeof(len), ieee02154hdr.payload_len, payload); | |
1029 | ||
1030 | if (prepend_len != NULL) { | |
1031 | *prepend_len = buflen; | |
1032 | } | |
1033 | if (postpend_len != NULL) { | |
1034 | *postpend_len = 0; | |
1035 | } | |
1036 | ||
1037 | err_out: | |
1038 | if (payload != NULL) { | |
1039 | _FREE(payload, M_TEMP); | |
1040 | } | |
1041 | m_freem(*m); | |
1042 | *m = mc; | |
1043 | return err; | |
1044 | } | |
1045 | ||
1046 | ||
1047 | static errno_t | |
1048 | sixlowpan_attach_inet6(struct ifnet *ifp, protocol_family_t protocol_family) | |
1049 | { | |
1050 | struct ifnet_attach_proto_param proto; | |
1051 | errno_t error; | |
1052 | ||
1053 | bzero(&proto, sizeof(proto)); | |
1054 | proto.pre_output = sixlowpan_proto_pre_output; | |
1055 | ||
1056 | error = ifnet_attach_protocol(ifp, protocol_family, &proto); | |
1057 | if (error && error != EEXIST) { | |
1058 | printf("WARNING: %s can't attach ipv6 to %s\n", __func__, | |
1059 | if_name(ifp)); | |
1060 | } | |
1061 | return error; | |
1062 | } | |
1063 | ||
1064 | static void | |
1065 | sixlowpan_detach_inet6(struct ifnet *ifp, protocol_family_t protocol_family) | |
1066 | { | |
1067 | (void) ifnet_detach_protocol(ifp, protocol_family); | |
1068 | } | |
1069 | ||
1070 | #if INET6 | |
1071 | __private_extern__ int | |
1072 | sixlowpan_family_init(void) | |
1073 | { | |
1074 | int error = 0; | |
1075 | ||
1076 | error = proto_register_plumber(PF_INET6, IFNET_FAMILY_6LOWPAN, | |
1077 | sixlowpan_attach_inet6, sixlowpan_detach_inet6); | |
1078 | if (error != 0) { | |
1079 | printf("6lowpan: proto_register_plumber failed for AF_INET6 error=%d\n", | |
1080 | error); | |
1081 | goto done; | |
1082 | } | |
1083 | ||
1084 | error = sixlowpan_clone_attach(); | |
1085 | if (error != 0) { | |
1086 | printf("6lowpan: proto_register_plumber failed sixlowpan_clone_attach error=%d\n", | |
1087 | error); | |
1088 | goto done; | |
1089 | } | |
1090 | ||
1091 | ||
1092 | done: | |
1093 | return error; | |
1094 | } | |
1095 | #endif |