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