]> git.saurik.com Git - apple/xnu.git/blob - bsd/net/ether_if_module.c
126f377dbf7937a67085fb3683cc8be2734ea822
[apple/xnu.git] / bsd / net / ether_if_module.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * Copyright (c) 1982, 1989, 1993
27 * The Regents of the University of California. All rights reserved.
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
31 * are met:
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. All advertising materials mentioning features or use of this software
38 * must display the following acknowledgement:
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 *
57 */
58
59
60
61 #include <sys/param.h>
62 #include <sys/systm.h>
63 #include <sys/kernel.h>
64 #include <sys/malloc.h>
65 #include <sys/mbuf.h>
66 #include <sys/socket.h>
67 #include <sys/sockio.h>
68 #include <sys/sysctl.h>
69
70 #include <net/if.h>
71 #include <net/netisr.h>
72 #include <net/route.h>
73 #include <net/if_llc.h>
74 #include <net/if_dl.h>
75 #include <net/if_types.h>
76 #include <netinet/if_ether.h>
77 #include <netinet/in.h> /* For M_LOOP */
78
79 /*
80 #if INET
81 #include <netinet/in.h>
82 #include <netinet/in_var.h>
83
84 #include <netinet/in_systm.h>
85 #include <netinet/ip.h>
86 #endif
87 */
88
89 #include <sys/socketvar.h>
90 #include <net/if_vlan_var.h>
91
92 #include <net/dlil.h>
93
94 extern int vlan_demux(struct ifnet * ifp, struct mbuf *,
95 char * frame_header, struct if_proto * * proto);
96
97 #if LLC && CCITT
98 extern struct ifqueue pkintrq;
99 #endif
100
101 /* General stuff from if_ethersubr.c - may not need some of it */
102
103 #include <netat/at_pat.h>
104 #if NETAT
105 extern struct ifqueue atalkintrq;
106 #endif
107
108
109 #if BRIDGE
110 #include <net/bridge.h>
111 #endif
112
113 static u_long lo_dlt = 0;
114
115 #define IFP2AC(IFP) ((struct arpcom *)IFP)
116
117 struct en_desc {
118 u_int16_t type; /* Type of protocol stored in data */
119 struct if_proto *proto; /* Protocol structure */
120 u_long data[2]; /* Protocol data */
121 };
122
123 #define ETHER_DESC_BLK_SIZE (10)
124 #define MAX_INTERFACES 50
125
126 /*
127 * Statics for demux module
128 */
129
130 struct ether_desc_blk_str {
131 u_long n_max_used;
132 u_long n_count;
133 struct en_desc *block_ptr;
134 };
135
136
137 static struct ether_desc_blk_str ether_desc_blk[MAX_INTERFACES];
138
139
140 /* from if_ethersubr.c */
141 int ether_resolvemulti __P((struct ifnet *, struct sockaddr **,
142 struct sockaddr *));
143
144 /*
145 * Release all descriptor entries owned by this dl_tag (there may be several).
146 * Setting the type to 0 releases the entry. Eventually we should compact-out
147 * the unused entries.
148 */
149 __private_extern__ int
150 ether_del_proto(struct if_proto *proto, u_long dl_tag)
151 {
152 struct en_desc* ed = ether_desc_blk[proto->ifp->family_cookie].block_ptr;
153 u_long current = 0;
154 int found = 0;
155
156 for (current = ether_desc_blk[proto->ifp->family_cookie].n_max_used;
157 current > 0; current--) {
158 if (ed[current - 1].proto == proto) {
159 found = 1;
160 ed[current - 1].type = 0;
161
162 if (current == ether_desc_blk[proto->ifp->family_cookie].n_max_used) {
163 ether_desc_blk[proto->ifp->family_cookie].n_max_used--;
164 }
165 }
166 }
167
168 return found;
169 }
170
171
172
173
174 __private_extern__ int
175 ether_add_proto(struct ddesc_head_str *desc_head, struct if_proto *proto, u_long dl_tag)
176 {
177 char *current_ptr;
178 struct dlil_demux_desc *desc;
179 struct en_desc *ed;
180 struct en_desc *last;
181 u_long *bitmask;
182 u_long *proto_id;
183 u_long i;
184 short total_length;
185 u_long block_count;
186 u_long *tmp;
187
188
189 TAILQ_FOREACH(desc, desc_head, next) {
190 switch (desc->type) {
191 /* These types are supported */
192 /* Top three are preferred */
193 case DLIL_DESC_ETYPE2:
194 if (desc->variants.native_type_length != 2)
195 return EINVAL;
196 break;
197
198 case DLIL_DESC_SAP:
199 if (desc->variants.native_type_length != 3)
200 return EINVAL;
201 break;
202
203 case DLIL_DESC_SNAP:
204 if (desc->variants.native_type_length != 5)
205 return EINVAL;
206 break;
207
208 case DLIL_DESC_802_2:
209 case DLIL_DESC_802_2_SNAP:
210 break;
211
212 case DLIL_DESC_RAW:
213 if (desc->variants.bitmask.proto_id_length == 0)
214 break;
215 /* else fall through, bitmask variant not supported */
216
217 default:
218 ether_del_proto(proto, dl_tag);
219 return EINVAL;
220 }
221
222 ed = ether_desc_blk[proto->ifp->family_cookie].block_ptr;
223
224 /* Find a free entry */
225 for (i = 0; i < ether_desc_blk[proto->ifp->family_cookie].n_count; i++) {
226 if (ed[i].type == 0) {
227 break;
228 }
229 }
230
231 if (i >= ether_desc_blk[proto->ifp->family_cookie].n_count) {
232 u_long new_count = ETHER_DESC_BLK_SIZE +
233 ether_desc_blk[proto->ifp->family_cookie].n_count;
234 tmp = _MALLOC((new_count * (sizeof(*ed))), M_IFADDR, M_WAITOK);
235 if (tmp == 0) {
236 /*
237 * Remove any previous descriptors set in the call.
238 */
239 ether_del_proto(proto, dl_tag);
240 return ENOMEM;
241 }
242
243 bzero(tmp, new_count * sizeof(*ed));
244 bcopy(ether_desc_blk[proto->ifp->family_cookie].block_ptr,
245 tmp, ether_desc_blk[proto->ifp->family_cookie].n_count * sizeof(*ed));
246 FREE(ether_desc_blk[proto->ifp->family_cookie].block_ptr, M_IFADDR);
247 ether_desc_blk[proto->ifp->family_cookie].n_count = new_count;
248 ether_desc_blk[proto->ifp->family_cookie].block_ptr = (struct en_desc*)tmp;
249 ed = ether_desc_blk[proto->ifp->family_cookie].block_ptr;
250 }
251
252 /* Bump n_max_used if appropriate */
253 if (i + 1 > ether_desc_blk[proto->ifp->family_cookie].n_max_used) {
254 ether_desc_blk[proto->ifp->family_cookie].n_max_used = i + 1;
255 }
256
257 ed[i].proto = proto;
258 ed[i].data[0] = 0;
259 ed[i].data[1] = 0;
260
261 switch (desc->type) {
262 case DLIL_DESC_RAW:
263 /* 2 byte ethernet raw protocol type is at native_type */
264 /* protocol is not in network byte order */
265 ed[i].type = DLIL_DESC_ETYPE2;
266 ed[i].data[0] = htons(*(u_int16_t*)desc->native_type);
267 break;
268
269 case DLIL_DESC_ETYPE2:
270 /* 2 byte ethernet raw protocol type is at native_type */
271 /* prtocol must be in network byte order */
272 ed[i].type = DLIL_DESC_ETYPE2;
273 ed[i].data[0] = *(u_int16_t*)desc->native_type;
274 break;
275
276 case DLIL_DESC_802_2:
277 ed[i].type = DLIL_DESC_SAP;
278 ed[i].data[0] = *(u_int32_t*)&desc->variants.desc_802_2;
279 ed[i].data[0] &= htonl(0xFFFFFF00);
280 break;
281
282 case DLIL_DESC_SAP:
283 ed[i].type = DLIL_DESC_SAP;
284 bcopy(desc->native_type, &ed[i].data[0], 3);
285 break;
286
287 case DLIL_DESC_802_2_SNAP:
288 ed[i].type = DLIL_DESC_SNAP;
289 desc->variants.desc_802_2_SNAP.protocol_type =
290 htons(desc->variants.desc_802_2_SNAP.protocol_type);
291 bcopy(&desc->variants.desc_802_2_SNAP, &ed[i].data[0], 8);
292 ed[i].data[0] &= htonl(0x000000FF);
293 desc->variants.desc_802_2_SNAP.protocol_type =
294 ntohs(desc->variants.desc_802_2_SNAP.protocol_type);
295 break;
296
297 case DLIL_DESC_SNAP: {
298 u_int8_t* pDest = ((u_int8_t*)&ed[i].data[0]) + 3;
299 ed[i].type = DLIL_DESC_SNAP;
300 bcopy(desc->native_type, pDest, 5);
301 }
302 break;
303 }
304 }
305
306 return 0;
307 }
308
309
310 static
311 int ether_shutdown()
312 {
313 return 0;
314 }
315
316
317 int ether_demux(ifp, m, frame_header, proto)
318 struct ifnet *ifp;
319 struct mbuf *m;
320 char *frame_header;
321 struct if_proto **proto;
322
323 {
324 register struct ether_header *eh = (struct ether_header *)frame_header;
325 u_short ether_type = eh->ether_type;
326 u_short ether_type_host;
327 u_int16_t type;
328 u_int8_t *data;
329 u_long i = 0;
330 u_long max = ether_desc_blk[ifp->family_cookie].n_max_used;
331 struct en_desc *ed = ether_desc_blk[ifp->family_cookie].block_ptr;
332 u_int32_t extProto1 = 0;
333 u_int32_t extProto2 = 0;
334
335 if (eh->ether_dhost[0] & 1) {
336 /* Check for broadcast */
337 if (*(u_int32_t*)eh->ether_dhost == 0xFFFFFFFF &&
338 *(u_int16_t*)(eh->ether_dhost + sizeof(u_int32_t)) == 0xFFFF)
339 m->m_flags |= M_BCAST;
340 else
341 m->m_flags |= M_MCAST;
342 } else {
343 /*
344 * When the driver is put into promiscuous mode we may receive unicast
345 * frames that are not intended for our interfaces. They are filtered
346 * here to keep them from traveling further up the stack to code that
347 * is not expecting them or prepared to deal with them. In the near
348 * future, the filtering done here will be moved even further down the
349 * stack into the IONetworkingFamily, preventing even interface
350 * filter NKE's from receiving promiscuous packets. Please use BPF.
351 */
352 #define ETHER_CMP(x, y) ( ((u_int16_t *) x)[0] != ((u_int16_t *) y)[0] || \
353 ((u_int16_t *) x)[1] != ((u_int16_t *) y)[1] || \
354 ((u_int16_t *) x)[2] != ((u_int16_t *) y)[2] )
355
356 if (ETHER_CMP(eh->ether_dhost, ((struct arpcom *) ifp)->ac_enaddr)) {
357 m_freem(m);
358 return EJUSTRETURN;
359 }
360 }
361 ether_type_host = ntohs(ether_type);
362 if ((m->m_pkthdr.csum_flags & CSUM_VLAN_TAG_VALID)
363 || ether_type_host == ETHERTYPE_VLAN) {
364 return (vlan_demux(ifp, m, frame_header, proto));
365 }
366 data = mtod(m, u_int8_t*);
367
368 /*
369 * Determine the packet's protocol type and stuff the protocol into
370 * longs for quick compares.
371 */
372 if (ether_type_host <= 1500) {
373 extProto1 = *(u_int32_t*)data;
374
375 // SAP or SNAP
376 if ((extProto1 & htonl(0xFFFFFF00)) == htonl(0xAAAA0300)) {
377 // SNAP
378 type = DLIL_DESC_SNAP;
379 extProto2 = *(u_int32_t*)(data + sizeof(u_int32_t));
380 extProto1 &= htonl(0x000000FF);
381 } else {
382 type = DLIL_DESC_SAP;
383 extProto1 &= htonl(0xFFFFFF00);
384 }
385 } else {
386 type = DLIL_DESC_ETYPE2;
387 }
388
389 /*
390 * Search through the connected protocols for a match.
391 */
392
393 switch (type) {
394 case DLIL_DESC_ETYPE2:
395 for (i = 0; i < max; i++) {
396 if ((ed[i].type == type) && (ed[i].data[0] == ether_type)) {
397 *proto = ed[i].proto;
398 return 0;
399 }
400 }
401 break;
402
403 case DLIL_DESC_SAP:
404 for (i = 0; i < max; i++) {
405 if ((ed[i].type == type) && (ed[i].data[0] == extProto1)) {
406 *proto = ed[i].proto;
407 return 0;
408 }
409 }
410 break;
411
412 case DLIL_DESC_SNAP:
413 for (i = 0; i < max; i++) {
414 if ((ed[i].type == type) && (ed[i].data[0] == extProto1) &&
415 (ed[i].data[1] == extProto2)) {
416 *proto = ed[i].proto;
417 return 0;
418 }
419 }
420 break;
421 }
422
423 return ENOENT;
424 }
425
426
427
428 /*
429 * Ethernet output routine.
430 * Encapsulate a packet of type family for the local net.
431 * Use trailer local net encapsulation if enough data in first
432 * packet leaves a multiple of 512 bytes of data in remainder.
433 * Assumes that ifp is actually pointer to arpcom structure.
434 */
435 int
436 ether_frameout(ifp, m, ndest, edst, ether_type)
437 register struct ifnet *ifp;
438 struct mbuf **m;
439 struct sockaddr *ndest;
440 char *edst;
441 char *ether_type;
442 {
443 register struct ether_header *eh;
444 int hlen; /* link layer header length */
445 struct arpcom *ac = IFP2AC(ifp);
446
447
448 hlen = ETHER_HDR_LEN;
449
450 /*
451 * If a simplex interface, and the packet is being sent to our
452 * Ethernet address or a broadcast address, loopback a copy.
453 * XXX To make a simplex device behave exactly like a duplex
454 * device, we should copy in the case of sending to our own
455 * ethernet address (thus letting the original actually appear
456 * on the wire). However, we don't do that here for security
457 * reasons and compatibility with the original behavior.
458 */
459 if ((ifp->if_flags & IFF_SIMPLEX) &&
460 ((*m)->m_flags & M_LOOP)) {
461 if (lo_dlt == 0)
462 dlil_find_dltag(APPLE_IF_FAM_LOOPBACK, 0, PF_INET, &lo_dlt);
463
464 if (lo_dlt) {
465 if ((*m)->m_flags & M_BCAST) {
466 struct mbuf *n = m_copy(*m, 0, (int)M_COPYALL);
467 if (n != NULL)
468 dlil_output(lo_dlt, n, 0, ndest, 0);
469 }
470 else
471 {
472 if (bcmp(edst, ac->ac_enaddr, ETHER_ADDR_LEN) == 0) {
473 dlil_output(lo_dlt, *m, 0, ndest, 0);
474 return EJUSTRETURN;
475 }
476 }
477 }
478 }
479
480
481 /*
482 * Add local net header. If no space in first mbuf,
483 * allocate another.
484 */
485 M_PREPEND(*m, sizeof (struct ether_header), M_DONTWAIT);
486 if (*m == 0) {
487 return (EJUSTRETURN);
488 }
489
490
491 eh = mtod(*m, struct ether_header *);
492 (void)memcpy(&eh->ether_type, ether_type,
493 sizeof(eh->ether_type));
494 (void)memcpy(eh->ether_dhost, edst, 6);
495 (void)memcpy(eh->ether_shost, ac->ac_enaddr,
496 sizeof(eh->ether_shost));
497
498 return 0;
499 }
500
501
502
503 __private_extern__ int
504 ether_add_if(struct ifnet *ifp)
505 {
506 u_long i;
507
508 ifp->if_framer = ether_frameout;
509 ifp->if_demux = ether_demux;
510 ifp->if_event = 0;
511 ifp->if_resolvemulti = ether_resolvemulti;
512 ifp->if_nvlans = 0;
513
514 for (i=0; i < MAX_INTERFACES; i++)
515 if (ether_desc_blk[i].n_count == 0)
516 break;
517
518 if (i == MAX_INTERFACES)
519 return ENOMEM;
520
521 ether_desc_blk[i].block_ptr = _MALLOC(ETHER_DESC_BLK_SIZE * sizeof(struct en_desc),
522 M_IFADDR, M_WAITOK);
523 if (ether_desc_blk[i].block_ptr == 0)
524 return ENOMEM;
525
526 ether_desc_blk[i].n_count = ETHER_DESC_BLK_SIZE;
527 bzero(ether_desc_blk[i].block_ptr, ETHER_DESC_BLK_SIZE * sizeof(struct en_desc));
528
529 ifp->family_cookie = i;
530
531 return 0;
532 }
533
534 __private_extern__ int
535 ether_del_if(struct ifnet *ifp)
536 {
537 if ((ifp->family_cookie < MAX_INTERFACES) &&
538 (ether_desc_blk[ifp->family_cookie].n_count))
539 {
540 FREE(ether_desc_blk[ifp->family_cookie].block_ptr, M_IFADDR);
541 ether_desc_blk[ifp->family_cookie].block_ptr = NULL;
542 ether_desc_blk[ifp->family_cookie].n_count = 0;
543 ether_desc_blk[ifp->family_cookie].n_max_used = 0;
544 return 0;
545 }
546 else
547 return ENOENT;
548 }
549
550 __private_extern__ int
551 ether_init_if(struct ifnet *ifp)
552 {
553 register struct ifaddr *ifa;
554 register struct sockaddr_dl *sdl;
555
556 ifa = ifnet_addrs[ifp->if_index - 1];
557 if (ifa == 0) {
558 printf("ether_ifattach: no lladdr!\n");
559 return (EINVAL);
560 }
561 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
562 sdl->sdl_type = IFT_ETHER;
563 sdl->sdl_alen = ifp->if_addrlen;
564 bcopy((IFP2AC(ifp))->ac_enaddr, LLADDR(sdl), ifp->if_addrlen);
565
566 return 0;
567 }
568
569
570 int
571 ether_ifmod_ioctl(ifp, command, data)
572 struct ifnet *ifp;
573 u_long command;
574 caddr_t data;
575 {
576 struct rslvmulti_req *rsreq = (struct rslvmulti_req *) data;
577 int error = 0;
578 struct sockaddr_dl *sdl;
579 struct sockaddr_in *sin;
580 u_char *e_addr;
581
582
583 switch (command) {
584 case SIOCRSLVMULTI:
585 switch(rsreq->sa->sa_family) {
586 case AF_UNSPEC:
587 /* AppleTalk uses AF_UNSPEC for multicast registration.
588 * No mapping needed. Just check that it's a valid MC address.
589 */
590 e_addr = &rsreq->sa->sa_data[0];
591 if ((e_addr[0] & 1) != 1)
592 return EADDRNOTAVAIL;
593 *rsreq->llsa = 0;
594 return EJUSTRETURN;
595
596
597 case AF_LINK:
598 /*
599 * No mapping needed. Just check that it's a valid MC address.
600 */
601 sdl = (struct sockaddr_dl *)rsreq->sa;
602 e_addr = LLADDR(sdl);
603 if ((e_addr[0] & 1) != 1)
604 return EADDRNOTAVAIL;
605 *rsreq->llsa = 0;
606 return EJUSTRETURN;
607
608 default:
609 return EAFNOSUPPORT;
610 }
611
612 default:
613 return EOPNOTSUPP;
614 }
615 }
616
617
618 extern int ether_attach_inet(struct ifnet *ifp, u_long *dl_tag);
619 extern int ether_detach_inet(struct ifnet *ifp, u_long dl_tag);
620 extern int ether_attach_inet6(struct ifnet *ifp, u_long *dl_tag);
621 extern int ether_detach_inet6(struct ifnet *ifp, u_long dl_tag);
622 int ether_family_init()
623 {
624 int i, error=0;
625 struct dlil_ifmod_reg_str ifmod_reg;
626 struct dlil_protomod_reg_str enet_protoreg;
627 extern int vlan_family_init(void);
628
629 /* ethernet family is built-in, called from bsd_init */
630 thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
631
632 bzero(&ifmod_reg, sizeof(ifmod_reg));
633 ifmod_reg.add_if = ether_add_if;
634 ifmod_reg.del_if = ether_del_if;
635 ifmod_reg.init_if = ether_init_if;
636 ifmod_reg.add_proto = ether_add_proto;
637 ifmod_reg.del_proto = ether_del_proto;
638 ifmod_reg.ifmod_ioctl = ether_ifmod_ioctl;
639 ifmod_reg.shutdown = ether_shutdown;
640
641 if (dlil_reg_if_modules(APPLE_IF_FAM_ETHERNET, &ifmod_reg)) {
642 printf("WARNING: ether_family_init -- Can't register if family modules\n");
643 error = EIO;
644 goto done;
645 }
646
647
648 /* Register protocol registration functions */
649
650 bzero(&enet_protoreg, sizeof(enet_protoreg));
651 enet_protoreg.attach_proto = ether_attach_inet;
652 enet_protoreg.detach_proto = ether_detach_inet;
653
654 if (error = dlil_reg_proto_module(PF_INET, APPLE_IF_FAM_ETHERNET, &enet_protoreg) != 0) {
655 printf("ether_family_init: dlil_reg_proto_module failed for AF_INET error=%d\n", error);
656 goto done;
657 }
658
659 enet_protoreg.attach_proto = ether_attach_inet6;
660 enet_protoreg.detach_proto = ether_detach_inet6;
661
662 if (error = dlil_reg_proto_module(PF_INET6, APPLE_IF_FAM_ETHERNET, &enet_protoreg) != 0) {
663 printf("ether_family_init: dlil_reg_proto_module failed for AF_INET6 error=%d\n", error);
664 goto done;
665 }
666 vlan_family_init();
667
668 done:
669 thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
670
671 return (error);
672 }