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