]> git.saurik.com Git - apple/xnu.git/blame - bsd/netat/at.c
xnu-517.tar.gz
[apple/xnu.git] / bsd / netat / at.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
43866e37 6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
1c79356b 7 *
43866e37
A
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
1c79356b
A
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
43866e37
A
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.
1c79356b
A
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25/*
26 * Copyright (c) 1998 Apple Computer, Inc.
27 */
28
29/* at.c
30 */
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/ioctl.h>
35#include <sys/errno.h>
36#include <sys/malloc.h>
37#include <sys/socket.h>
38#include <sys/socketvar.h>
39#include <sys/file.h>
40
41#include <net/if.h>
42#include <net/if_dl.h>
43#include <net/if_types.h>
1c79356b
A
44#include <net/dlil.h>
45
46#include <netat/appletalk.h>
47#include <netat/sysglue.h>
48#include <netat/at_pcb.h>
49#include <netat/at_var.h>
50#include <netat/ddp.h>
51#include <netat/nbp.h>
52#include <netat/routing_tables.h>
53#include <netat/debug.h>
54
9bccf70c
A
55#include <sys/kern_event.h>
56
0b4e3aa0 57extern int at_ioctl(struct atpcb *, u_long, caddr_t, int fromKernel);
1c79356b
A
58extern int routerStart(at_kern_err_t *);
59extern void elap_offline(at_ifaddr_t *);
60extern at_ifaddr_t *find_ifID(char *);
61extern at_nvestr_t *getRTRLocalZone(zone_usage_t *);
62extern int setLocalZones(at_nvestr_t *, int);
63
64extern int xpatcnt;
65extern at_ifaddr_t at_interfaces[];
66extern at_ifaddr_t *ifID_home;
67extern TAILQ_HEAD(name_registry, _nve_) name_registry;
68extern int nve_lock;
69
70struct etalk_addr etalk_multicast_addr = {
71 {0x09, 0x00, 0x07, 0xff, 0xff, 0xff}};
72struct etalk_addr ttalk_multicast_addr = {
73 {0xC0, 0x00, 0x40, 0x00, 0x00, 0x00}};
74
75/* called only in router mode */
76static int set_zones(ifz)
77 zone_usage_t *ifz;
78
79/* 1. adds zone to table
80 2. looks up each route entry from zone list
81 3. sets zone bit in each route entry
82
83 returns 0 if successful
84 errno if error occurred
85*/
86{
87 int i;
88 at_ifaddr_t *ifID;
89 short zno;
90 RT_entry *rte;
91
92 zno = zt_add_zone(ifz->zone_name.str, ifz->zone_name.len);
93
94 if (zno == ZT_MAXEDOUT) {
95 dPrintf(D_M_ELAP, D_L_ERROR, ("set_zones: error: table full\n"));
96 return(ENOSPC);
97 }
98 if (ifz->zone_home) {
99 ifID_home->ifZoneName = ifz->zone_name;
100 ifID_home->ifDefZone = zno;
101 }
102
103 for (i=0; i<IF_TOTAL_MAX; i++) {
104 if (ifz->zone_iflist.at_if[i][0]) {
105 if ((ifID = find_ifID(ifz->zone_iflist.at_if[i]))) {
106 rte = rt_blookup(ifID->ifThisCableEnd);
107 if (!rte) {
108 dPrintf(D_M_ELAP, D_L_ERROR,
109 ("set_zones: error: can't find route\n"));
110 } else {
111 zt_set_zmap(zno, rte->ZoneBitMap);
112
113 /* if first zone for this I/F,
114 make default */
115 if (!ifID->ifDefZone)
116 ifID->ifDefZone = zno;
117 }
118 }
119 }
120 }
121
122 return(0);
123} /* set_zones */
124
125/*
126 * Generic internet control operations (ioctl's).
127 * ifp is 0 if not an interface-specific ioctl.
128 */
129
130int at_control(so, cmd, data, ifp)
131 struct socket *so;
132 u_long cmd;
133 caddr_t data;
134 struct ifnet *ifp;
135{
136 struct ifreq *ifr = (struct ifreq *)data;
137 int pat_id = 0, error = 0;
138 struct proc *p = current_proc();
139 at_ifaddr_t *ifID = 0;
140 struct ifaddr *ifa;
141 struct sockaddr_dl *sdl;
142
143 if (cmd == 0x2000ff99) {
144 /* *** this is a temporary hack to get at_send_to_dev() to
145 work with BSD-style sockets instead of the special purpose
146 system calls, ATsocket() and ATioctl().
147 *** */
0b4e3aa0 148 if ((error = at_ioctl((struct atpcb *)so->so_pcb, cmd, data, 0))) {
1c79356b
A
149 if (((struct atpcb *)so->so_pcb)->proto != ATPROTO_LAP) {
150 ((struct atpcb *)so->so_pcb)->proto = ATPROTO_LAP;
0b4e3aa0 151 error = at_ioctl((struct atpcb *)so->so_pcb, cmd, data, 0);
1c79356b
A
152 }
153 }
154 return(error);
155
156 /* *** processing should be
157 return(EINVAL);
158 *** */
159 }
160 /*
161 * Find address for this interface, if it exists.
162 */
163 if (ifp)
164 for (pat_id = 0; pat_id < xpatcnt; pat_id++)
165 if (at_interfaces[pat_id].aa_ifp == ifp) {
166 ifID = &at_interfaces[pat_id];
167 break;
168 }
169
170 switch (cmd) {
171
172 case AIOCGETSTATE:
173 {
174 at_state_t *global_state = (at_state_t *)data;
175
176 *global_state = at_state;
177 return(0);
178 break;
179 }
180
181 case AIOCGETIFCFG:
182 {
183 at_if_cfg_t *cfgp = (at_if_cfg_t *)data;
184
185 ifID = 0;
186 if ((at_state.flags & AT_ST_STARTED) &&
187 ifID_home) {
188 if (strlen(cfgp->ifr_name)) {
189 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
190 if (!strncmp(ifID->ifName, cfgp->ifr_name,
191 strlen(ifID->ifName)))
192 break;
193 }
194 } else {
195 ifID = ifID_home;
196 strncpy(cfgp->ifr_name, ifID->ifName,
197 sizeof(ifID->ifName));
198 }
199 if (ifID && ifID->ifState != LAP_OFFLINE) {
200 cfgp->flags = ifID->ifFlags;
201 /* put the IF state into the low order
202 bits of flags */
203 cfgp->flags |= (ifID->ifState & LAP_STATE_MASK);
204 cfgp->node = ifID->ifThisNode;
205 cfgp->router = ifID->ifARouter;
206 cfgp->netStart = ifID->ifThisCableStart;
207 cfgp->netEnd = ifID->ifThisCableEnd;
208 cfgp->zonename = ifID->ifZoneName;
209 return(0);
210 } else
211 return(EINVAL);
212 } else
213 return(ENOTREADY);
214 break;
215 }
216
217 case AIOCSETDEFZONE:
218 {
219 at_def_zone_t *defzonep = (at_def_zone_t *)data;
220
221 /* check for root access */
222 if (error = suser(p->p_ucred, &p->p_acflag))
223 return(EACCES);
224
225 ifID = 0;
226 if ((at_state.flags & AT_ST_STARTED) && ifID_home) {
227 if (strlen(defzonep->ifr_name)) {
228 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
229 if (!strncmp(ifID->ifName, defzonep->ifr_name,
230 strlen(ifID->ifName)))
231 break;
232 }
233 } else {
234 ifID = ifID_home;
235 strncpy(defzonep->ifr_name, ifID->ifName,
236 sizeof(ifID->ifName));
237 }
238
239 /* In routing mode the default zone is only set for the
240 default interface. */
241 if (ROUTING_MODE && (ifID != ifID_home))
242 return(EINVAL);
243
244 if (ifID && ifID->ifState != LAP_OFFLINE) {
245 if (zonename_equal(&ifID->ifZoneName,
246 &defzonep->zonename))
247 return(0);
248 else {
249 /* check the zone name */
250 if (MULTIPORT_MODE) {
251 short zno;
9bccf70c 252 at_ifnames_t ifs_in_zone;
1c79356b
A
253
254 if (!(zno = zt_find_zname(&defzonep->zonename)))
255 return(EINVAL);
256
9bccf70c
A
257 getIfUsage(zno-1, &ifs_in_zone);
258 if (!ifs_in_zone.at_if[ifID->ifPort])
1c79356b
A
259 return(EINVAL);
260 ifID->ifDefZone = zno+1;
261 } else {
262 int i;
263 at_nvestr_t *zone;
264
265 for (i = 0, zone = getSPLocalZone(i);
266 zone;
267 i++, zone = getSPLocalZone(i)) {
268 if (zonename_equal(zone,
269 &defzonep->zonename))
270 break;
271 }
272 if (!zone)
273 return(EINVAL);
274 }
275 ifID->ifZoneName = defzonep->zonename;
276 (void)regDefaultZone(ifID);
9bccf70c
A
277
278 /* AppleTalk zone was changed. Send event with zone info. */
279 atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ZONEUPDATED, 0, &(ifID->ifZoneName));
280
1c79356b
A
281 return(0);
282 }
283 } else
284 return(EINVAL);
285 } else
286 return(ENOTREADY);
287 break;
288 }
289
290 case AIOCREGLOCALZN:
291 {
292 at_nvestr_t *zone = (at_nvestr_t *)data;
293
294 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
295 return(ENOTREADY);
296
297 if (MULTIPORT_MODE)
298 return(EINVAL);
299
300 return(setLocalZones(zone, zone->len));
301
302 break;
303 }
304 case AIOCSETZNUSAGE:
305 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
306 return(ENOTREADY);
307
308 if (!ROUTING_MODE)
309 return(EINVAL);
310
311 return(set_zones((zone_usage_t *)data));
312
313 break;
314
315 case AIOCGETZNUSAGE:
316 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
317 return(ENOTREADY);
318
319 if (!MULTIPORT_MODE)
320 return(EINVAL);
321
322 if (getRTRLocalZone((zone_usage_t *)data))
323 return(0);
324 else
325 return(ENOENT);
326 break;
327
328 case AIOCNBPREG:
329 {
330 at_nbp_reg_t *nbpP = (at_nbp_reg_t *)data;
331 nve_entry_t nve;
332 int error;
333
334 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
335 return(ENOTREADY);
336
337 /* multihoming mode */
338 if (MULTIHOME_MODE) {
339 return(nbp_mh_reg(nbpP));
340 }
341
342 /* single port mode or router mode */
343 if (nbp_fillin_nve(&nbpP->name, &nve) != 0) {
344 /* bad tuple... */
345 return(EINVAL);
346 }
347
348 /* In routing mode when the zone is specified, we need to
349 find an interface on which the specified zone is seeded, so
350 that the zone multicast will be plausible. */
351 if (ROUTING_MODE && !(DEFAULT_ZONE(&nve.zone))) {
352 /* find first segment (interface) which is seeded for
353 this zone */
354 int finished = FALSE;
355 int zno;
9bccf70c 356 at_ifnames_t ifs_in_zone;
1c79356b
A
357 if (!(zno = zt_find_zname(&nve.zone))) {
358 return(EINVAL);
359 }
9bccf70c 360 getIfUsage(zno-1, &ifs_in_zone);
1c79356b
A
361
362 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
9bccf70c 363 if (!ifs_in_zone.at_if[ifID->ifPort])
1c79356b
A
364 /* zone doesn't match */
365 continue;
9bccf70c 366 else {
1c79356b 367 finished = TRUE;
9bccf70c
A
368 break;
369 }
1c79356b
A
370 }
371 if (!finished)
372 return(EINVAL);
373 } else
374 ifID = ifID_home;
375
376 nve.address.net = ifID->ifThisNode.s_net;
377 nve.address.node = ifID->ifThisNode.s_node;
378 nve.address.socket = nbpP->addr.socket;
379 nve.ddptype = nbpP->ddptype;
380
381 if (nbp_find_nve(&nve))
382 return(EADDRNOTAVAIL);
383
384 /* Normal case; no tuple found for this name, so insert
385 * this tuple in the registry and return ok response.
386 */
387 ATDISABLE(nve_lock, NVE_LOCK);
388 if ((error = nbp_new_nve_entry(&nve, ifID)) == 0) {
389 nbpP->addr.net = ifID->ifThisNode.s_net;
390 nbpP->addr.node = ifID->ifThisNode.s_node;
391 nbpP->unique_nbp_id = nve.unique_nbp_id;
392 }
393 ATENABLE(nve_lock, NVE_LOCK);
394
395 return(error);
396 break;
397 }
398
399 case AIOCNBPREMOVE:
400 {
401 at_nbp_reg_t *nbpP = (at_nbp_reg_t *)data;
402 nve_entry_t *nve_entry, nve;
403
404 if (!(at_state.flags & AT_ST_STARTED))
405 return(ENOTREADY);
406
407 /* delete by id */
408 if (nbpP->unique_nbp_id) {
409 ATDISABLE(nve_lock, NVE_LOCK);
410 TAILQ_FOREACH(nve_entry, &name_registry, nve_link) {
411 if (nve_entry->unique_nbp_id == nbpP->unique_nbp_id) {
412 /* Found a match! */
413 nbp_delete_entry(nve_entry);
414 ATENABLE(nve_lock, NVE_LOCK);
415 return(0);
416 }
417 }
418 ATENABLE(nve_lock, NVE_LOCK);
419 return(EADDRNOTAVAIL);
420 }
421
422 /* delete by entity */
423 if (nbp_fillin_nve(&nbpP->name, &nve) != 0) {
424 /* bad tuple... */
425 return(EINVAL);
426 }
427
428 if (MULTIHOME_MODE && DEFAULT_ZONE(&nbpP->name.zone)) {
429 /* if mhome & *, remove nve from all default zones */
430 int found = FALSE; /* if any found & deleted */
431
432 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
433 nve.zone = ifID->ifZoneName;
434 nve.zone_hash = nbp_strhash(&nve.zone);
435 if ((nve_entry = nbp_find_nve(&nve)) == NULL)
436 continue;
437
438 ATDISABLE(nve_lock, NVE_LOCK);
439 nbp_delete_entry(nve_entry);
440 ATENABLE(nve_lock, NVE_LOCK);
441 found = TRUE;
442 }
443 if (found)
444 return(0);
445 else
446 return(EADDRNOTAVAIL);
447 }
448
449 if ((nve_entry = nbp_find_nve(&nve)) == NULL)
450 /* Can't find the tuple we're looking for, send error*/
451 return(EADDRNOTAVAIL);
452
453 /* Normal case; tuple found for this name, so delete
454 * the entry from the registry and return ok response.
455 */
456 ATDISABLE(nve_lock, NVE_LOCK);
457 nbp_delete_entry(nve_entry);
458 ATENABLE(nve_lock, NVE_LOCK);
459 return(0);
460
461 break;
462 }
463
464 case AIOCSETROUTER:
465 {
466 at_router_params_t *rt = (at_router_params_t *)data;
467
468 /* check for root access */
469 if (error = suser(p->p_ucred, &p->p_acflag))
470 return(EACCES);
471
472 /* when in routing/multihome mode the AIOCSETROUTER IOCTL
473 is done first */
474 if (at_state.flags & AT_ST_STARTED)
475 return(EALREADY);
476
477 /* Setup the routing & zip table size for the router */
478 if (rt->rtmp_table_sz >= RT_MIN && rt->rtmp_table_sz <= RT_MAX)
479 RT_maxentry = rt->rtmp_table_sz;
480 else
481 RT_maxentry = RT_DEFAULT;
482
483 if (rt->zone_table_sz >= ZT_MIN && rt->zone_table_sz <= ZT_MAX)
484 ZT_maxentry = rt->zone_table_sz;
485 else
486 ZT_maxentry = ZT_DEFAULT;
487
488 if (rt_table_init() == ENOBUFS)
489 return(ENOBUFS);
490
491 if (rt->router_mix)
492 RouterMix = (int)rt->router_mix;
493 else
494 RouterMix = RT_MIX_DEFAULT;
495
496 add_ddp_handler(RTMP_SOCKET, rtmp_router_input);
497
498 if (rt->multihome)
499 at_state.flags |= AT_ST_MULTIHOME;
500 else
501 at_state.flags |= AT_ST_ROUTER;
502 break;
503 }
504 case AIOCSTARTROUTER:
505 {
506 at_kern_err_t *keP = (at_kern_err_t *)data;
507
508 /* check for root access */
509 if (suser(p->p_ucred, &p->p_acflag))
510 return(EACCES);
511
512 if (!(at_state.flags & AT_ST_STARTED))
513 return(ENOTREADY);
514
515 bzero(keP, sizeof(at_kern_err_t));
516 error = routerStart(keP);
517
518 break;
519 }
520 case AIOCGETROUTER:
521 {
522 at_router_params_t *rt = (at_router_params_t *)data;
523
524 if (!(at_state.flags & AT_ST_STARTED))
525 return(ENOTREADY);
526
527 rt->multihome = (MULTIHOME_MODE)? 1: 0;
528 rt->rtmp_table_sz = RT_maxentry;
529 rt->zone_table_sz = ZT_maxentry;
530 rt->router_mix = RouterMix;
531
532 break;
533 }
534 case AIOCSTOPATALK:
9bccf70c
A
535 {
536 int *count_only = (int *)data,
1c79356b
A
537 ret;
538
539 /* check for root access */
540 if (error = suser(p->p_ucred, &p->p_acflag))
541 return(EACCES);
542
543 ret = ddp_shutdown(*count_only);
9bccf70c
A
544
545 if (*count_only != 0)
546 {
1c79356b
A
547 *count_only = ret;
548 return(0);
9bccf70c
A
549 }
550 else
551 {
552 if (ret == 0)
553 {
554 /* AppleTalk was successfully shut down. Send event. */
555 atalk_post_msg(0, KEV_ATALK_DISABLED, 0, 0);
556 return 0;
557 }
558 else
559 return EBUSY;
560 }
561
1c79356b 562 break;
9bccf70c 563 }
1c79356b
A
564
565 case SIOCSIFADDR:
566 /* check for root access */
567 if (error = suser(p->p_ucred, &p->p_acflag))
568 error = EACCES;
569 else if (ifID)
570 error = EEXIST;
571 else {
572 int s;
573 if (xpatcnt == 0) {
9bccf70c 574 at_state.flags |= AT_ST_STARTING;
1c79356b
A
575 ddp_brt_init();
576 }
577
578 /* *** find an empty entry *** */
579 ifID = &at_interfaces[xpatcnt];
580 bzero((caddr_t)ifID, sizeof(at_ifaddr_t));
581 strncpy(ifID->ifName, ifr->ifr_name, sizeof(ifID->ifName));
582
583 ifID->aa_ifp = ifp;
584 ifa = &ifID->aa_ifa;
585 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
586 if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) &&
587 (sdl->sdl_family == AF_LINK)) {
588 bcopy(LLADDR(sdl), ifID->xaddr, sizeof(ifID->xaddr));
589#ifdef APPLETALK_DEBUG
590 kprintf("SIOCSIFADDR: local enet address is %x.%x.%x.%x.%x.%x\n",
591 ifID->xaddr[0], ifID->xaddr[1],
592 ifID->xaddr[2], ifID->xaddr[3],
593 ifID->xaddr[4], ifID->xaddr[5]);
594#endif
595 break;
596 }
597
598 /* attach the AppleTalk address to the ifnet structure */
599 ifa = &ifID->aa_ifa;
600 ifa->ifa_addr = (struct sockaddr *)&ifID->ifNodeAddress;
601 ifID->ifNodeAddress.sat_len = sizeof(struct sockaddr_at);
602 ifID->ifNodeAddress.sat_family = AF_APPLETALK;
603 /* the address itself will be filled in when ifThisNode
604 is set */
605 s = splnet();
606 TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link);
607 splx(s);
608
609 switch (ifp->if_type) {
610 case IFT_ETHER:
611 ether_attach_at(ifp, &ifID->at_dl_tag,
612 &ifID->aarp_dl_tag);
613 error = 0;
614 ifID->cable_multicast_addr = etalk_multicast_addr;
615
616 xpatcnt++;
617 break;
618 case IFT_FDDI:
619 ifID->cable_multicast_addr = etalk_multicast_addr;
620 ddp_bit_reverse(&ifID->cable_multicast_addr);
621 xpatcnt++;
622 break;
623 case IFT_ISO88025: /* token ring */
624 ifID->cable_multicast_addr = ttalk_multicast_addr;
625 ddp_bit_reverse(&ifID->cable_multicast_addr);
626
627 xpatcnt++;
628 break;
629 default:
630 error = EINVAL;
631 }
632 }
633 break;
634
635 /* complete the initialization started in SIOCSIFADDR */
636 case AIOCSIFADDR:
9bccf70c 637 {
1c79356b
A
638 at_if_cfg_t *cfgp = (at_if_cfg_t *)data;
639
9bccf70c 640 if (!(at_state.flags & AT_ST_STARTING))
1c79356b
A
641 return(ENOTREADY);
642
643 if (!(ifID = find_ifID(cfgp->ifr_name)))
644 return(EINVAL);
9bccf70c 645
1c79356b
A
646 return(lap_online(ifID, cfgp));
647 break;
9bccf70c 648 }
1c79356b
A
649
650#ifdef NOT_YET
651 /* *** this can't be added until AT can handle dynamic addition and
652 deletion of interfaces *** */
653 case SIOCDIFADDR:
654 /* check for root access */
655 if (error = suser(p->p_ucred, &p->p_acflag))
656 error = EACCES;
657 else if (!ifID)
658 error = EINVAL;
659 else
660 elap_offline(ifID);
661 break;
662#endif
663
664 case SIOCSETOT: {
665 int s;
666 struct atpcb *at_pcb, *clonedat_pcb;
667 int cloned_fd = *(int *)data;
668
669 s = splnet(); /* XXX */
670 at_pcb = sotoatpcb(so);
671
672 /* let's make sure it's either -1 or a valid file descriptor */
673 if (cloned_fd != -1) {
674 struct socket *cloned_so;
675 struct file *cloned_fp;
676 error = getsock(p->p_fd, cloned_fd, &cloned_fp);
677 if (error){
678 splx(s); /* XXX */
679 break;
680 }
681 cloned_so = (struct socket *)cloned_fp->f_data;
682 clonedat_pcb = sotoatpcb(cloned_so);
683 } else {
684 clonedat_pcb = NULL;
685 }
686
687 if (clonedat_pcb == NULL) {
688 at_pcb->ddp_flags |= DDPFLG_STRIPHDR;
689 } else {
690 at_pcb->ddp_flags = clonedat_pcb->ddp_flags;
691 }
692 splx(s); /* XXX */
693 break;
694 }
695
696 default:
697 if (ifp == 0 || ifp->if_ioctl == 0)
698 return (EOPNOTSUPP);
699 return dlil_ioctl(0, ifp, cmd, (caddr_t) data);
700 }
701
702 return(error);
703}
9bccf70c
A
704
705/* From dlil_post_msg() */
706void atalk_post_msg(struct ifnet *ifp, u_long event_code, struct at_addr *address, at_nvestr_t *zone)
707{
708 struct kev_atalk_data at_event_data;
709 struct kev_msg ev_msg;
710
711 ev_msg.vendor_code = KEV_VENDOR_APPLE;
712 ev_msg.kev_class = KEV_NETWORK_CLASS;
713 ev_msg.kev_subclass = KEV_ATALK_SUBCLASS;
714 ev_msg.event_code = event_code;
715
716 bzero(&at_event_data, sizeof(struct kev_atalk_data));
717
718 if (ifp != 0) {
719 strncpy(&at_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ);
720 at_event_data.link_data.if_family = ifp->if_family;
721 at_event_data.link_data.if_unit = (unsigned long) ifp->if_unit;
722 }
723
724 if (address != 0) {
725 at_event_data.node_data.address = *address;
726 }
727 else if (zone != 0) {
728 at_event_data.node_data.zone = *zone;
729 }
730
731 ev_msg.dv[0].data_length = sizeof(struct kev_atalk_data);
732 ev_msg.dv[0].data_ptr = &at_event_data;
733 ev_msg.dv[1].data_length = 0;
734
735 kev_post_msg(&ev_msg);
736}