]> git.saurik.com Git - apple/xnu.git/blob - bsd/netat/at.c
ae612079885f6354aed532268acbd289331be653
[apple/xnu.git] / bsd / netat / at.c
1 /*
2 * Copyright (c) 2000-2010 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 (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/sysglue.h>
51 #include <netat/appletalk.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 #include <net/kpi_protocol.h>
61
62 int lap_online( at_ifaddr_t *, at_if_cfg_t *cfgp);
63
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
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(zone_usage_t *ifz)
81
82 /* 1. adds zone to table
83 2. looks up each route entry from zone list
84 3. sets zone bit in each route entry
85
86 returns 0 if successful
87 errno if error occurred
88 */
89 {
90 int i;
91 at_ifaddr_t *ifID;
92 short zno;
93 RT_entry *rte;
94
95 if (ifz->zone_name.len <= 0 || ifz->zone_name.len > NBP_NVE_STR_SIZE)
96 return(ENOSPC);
97
98 zno = zt_add_zone((char *)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 static int
132 at_domifattach(struct ifnet *ifp, at_ifaddr_t *ifID)
133 {
134 int error;
135
136 if ((error = proto_plumb(PF_APPLETALK, ifp))) {
137 if (error != EEXIST)
138 log(LOG_ERR, "%s: proto_plumb returned %d if=%s%d\n",
139 __func__, error, ifp->if_name, ifp->if_unit);
140 } else if (ifID)
141 ifID->at_was_attached = 1;
142
143 return (error);
144 }
145
146 /*
147 * Generic internet control operations (ioctl's).
148 * ifp is 0 if not an interface-specific ioctl.
149 */
150
151 int
152 at_control(so, cmd, data, ifp)
153 struct socket *so;
154 u_long cmd;
155 caddr_t data;
156 struct ifnet *ifp;
157 {
158 struct ifreq *ifr = (struct ifreq *)data;
159 int pat_id = 0, error = 0;
160 at_ifaddr_t *ifID = 0;
161 struct ifaddr *ifa;
162 struct sockaddr_dl *sdl;
163
164 if ((cmd & 0xffff) == 0xff99) {
165 u_long fixed_command;
166 /* *** this is a temporary hack to get at_send_to_dev() to
167 work with BSD-style sockets instead of the special purpose
168 system calls, ATsocket() and ATioctl().
169 *** */
170 fixed_command = _IOW(0, 0xff99, user_addr_t);
171 if ((error = at_ioctl((struct atpcb *)so->so_pcb, fixed_command, data, 0))) {
172 if (((struct atpcb *)so->so_pcb)->proto != ATPROTO_LAP) {
173 ((struct atpcb *)so->so_pcb)->proto = ATPROTO_LAP;
174 error = at_ioctl((struct atpcb *)so->so_pcb, fixed_command, data , 0);
175 }
176 }
177 return(error);
178
179 /* *** processing should be
180 return(EINVAL);
181 *** */
182 }
183 /*
184 * Find address for this interface, if it exists.
185 */
186 if (ifp)
187 for (pat_id = 0; pat_id < xpatcnt; pat_id++)
188 if (at_interfaces[pat_id].aa_ifp == ifp) {
189 ifID = &at_interfaces[pat_id];
190 break;
191 }
192
193 switch (cmd) {
194
195 case AIOCGETSTATE:
196 {
197 at_state_t *global_state = (at_state_t *)data;
198
199 *global_state = at_state;
200 return(0);
201 break;
202 }
203
204 case AIOCGETIFCFG:
205 {
206 at_if_cfg_t *cfgp = (at_if_cfg_t *)data;
207
208 ifID = 0;
209 if ((at_state.flags & AT_ST_STARTED) &&
210 ifID_home) {
211 if (strlen(cfgp->ifr_name)) {
212 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
213 if (!strncmp(ifID->ifName, cfgp->ifr_name,
214 strlen(ifID->ifName)))
215 break;
216 }
217 } else {
218 ifID = ifID_home;
219 strlcpy(cfgp->ifr_name, ifID->ifName,
220 sizeof(cfgp->ifr_name));
221 }
222 if (ifID && ifID->ifState != LAP_OFFLINE) {
223 cfgp->flags = ifID->ifFlags;
224 /* put the IF state into the low order
225 bits of flags */
226 cfgp->flags |= (ifID->ifState & LAP_STATE_MASK);
227 cfgp->node = ifID->ifThisNode;
228 cfgp->router = ifID->ifARouter;
229 cfgp->netStart = ifID->ifThisCableStart;
230 cfgp->netEnd = ifID->ifThisCableEnd;
231 cfgp->zonename = ifID->ifZoneName;
232 return(0);
233 } else
234 return(EINVAL);
235 } else
236 return(ENOTREADY);
237 break;
238 }
239
240 case AIOCSETDEFZONE:
241 {
242 at_def_zone_t *defzonep = (at_def_zone_t *)data;
243
244 /* check for root access */
245 if ((error = suser(kauth_cred_get(), 0)))
246 return(EACCES);
247
248 ifID = 0;
249 if ((at_state.flags & AT_ST_STARTED) && ifID_home) {
250 if (strlen(defzonep->ifr_name)) {
251 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
252 if (!strncmp(ifID->ifName, defzonep->ifr_name,
253 strlen(ifID->ifName)))
254 break;
255 }
256 } else {
257 ifID = ifID_home;
258 strlcpy(defzonep->ifr_name, ifID->ifName,
259 sizeof(defzonep->ifr_name));
260 }
261
262 /* In routing mode the default zone is only set for the
263 default interface. */
264 if (ROUTING_MODE && (ifID != ifID_home))
265 return(EINVAL);
266
267 if (ifID && ifID->ifState != LAP_OFFLINE) {
268 if (zonename_equal(&ifID->ifZoneName,
269 &defzonep->zonename))
270 return(0);
271 else {
272 /* check the zone name */
273 if (MULTIPORT_MODE) {
274 short zno;
275 at_ifnames_t ifs_in_zone;
276
277 if (!(zno = zt_find_zname(&defzonep->zonename)))
278 return(EINVAL);
279
280 getIfUsage(zno-1, &ifs_in_zone);
281 if (!ifs_in_zone.at_if[ifID->ifPort])
282 return(EINVAL);
283 ifID->ifDefZone = zno+1;
284 } else {
285 int i;
286 at_nvestr_t *zone;
287
288 for (i = 0, zone = getSPLocalZone(i);
289 zone;
290 i++, zone = getSPLocalZone(i)) {
291 if (zonename_equal(zone,
292 &defzonep->zonename))
293 break;
294 }
295 if (!zone)
296 return(EINVAL);
297 }
298 ifID->ifZoneName = defzonep->zonename;
299 (void)regDefaultZone(ifID);
300
301 /* AppleTalk zone was changed. Send event with zone info. */
302 atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ZONEUPDATED, 0, &(ifID->ifZoneName));
303
304 return(0);
305 }
306 } else
307 return(EINVAL);
308 } else
309 return(ENOTREADY);
310 break;
311 }
312
313 case AIOCREGLOCALZN:
314 {
315 at_nvestr_t *zone = (at_nvestr_t *)data;
316
317 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
318 return(ENOTREADY);
319
320 if (MULTIPORT_MODE)
321 return(EINVAL);
322
323 return(setLocalZones(zone, zone->len));
324
325 break;
326 }
327 case AIOCSETZNUSAGE:
328 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
329 return(ENOTREADY);
330
331 if (!ROUTING_MODE)
332 return(EINVAL);
333
334 return(set_zones((zone_usage_t *)data));
335
336 break;
337
338 case AIOCGETZNUSAGE:
339 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
340 return(ENOTREADY);
341
342 if (!MULTIPORT_MODE)
343 return(EINVAL);
344
345 if (getRTRLocalZone((zone_usage_t *)data))
346 return(0);
347 else
348 return(ENOENT);
349 break;
350
351 case AIOCNBPREG:
352 {
353 at_nbp_reg_t *nbpP = (at_nbp_reg_t *)data;
354 nve_entry_t nve;
355 int error2;
356
357 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
358 return(ENOTREADY);
359
360 /* multihoming mode */
361 if (MULTIHOME_MODE) {
362 return(nbp_mh_reg(nbpP));
363 }
364
365 /* single port mode or router mode */
366 if (nbp_fillin_nve(&nbpP->name, &nve) != 0) {
367 /* bad tuple... */
368 return(EINVAL);
369 }
370
371 /* In routing mode when the zone is specified, we need to
372 find an interface on which the specified zone is seeded, so
373 that the zone multicast will be plausible. */
374 if (ROUTING_MODE && !(DEFAULT_ZONE(&nve.zone))) {
375 /* find first segment (interface) which is seeded for
376 this zone */
377 int finished = FALSE;
378 int zno;
379 at_ifnames_t ifs_in_zone;
380 if (!(zno = zt_find_zname(&nve.zone))) {
381 return(EINVAL);
382 }
383 getIfUsage(zno-1, &ifs_in_zone);
384
385 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
386 if (!ifs_in_zone.at_if[ifID->ifPort])
387 /* zone doesn't match */
388 continue;
389 else {
390 finished = TRUE;
391 break;
392 }
393 }
394 if (!finished)
395 return(EINVAL);
396 } else
397 ifID = ifID_home;
398
399 nve.address.net = ifID->ifThisNode.s_net;
400 nve.address.node = ifID->ifThisNode.s_node;
401 nve.address.socket = nbpP->addr.socket;
402 nve.ddptype = nbpP->ddptype;
403
404 if (nbp_find_nve(&nve))
405 return(EADDRNOTAVAIL);
406
407 /* Normal case; no tuple found for this name, so insert
408 * this tuple in the registry and return ok response.
409 */
410 if ((error2 = nbp_new_nve_entry(&nve, ifID)) == 0) {
411 nbpP->addr.net = ifID->ifThisNode.s_net;
412 nbpP->addr.node = ifID->ifThisNode.s_node;
413 nbpP->unique_nbp_id = nve.unique_nbp_id;
414 }
415
416 return(error2);
417 break;
418 }
419
420 case AIOCNBPREMOVE:
421 {
422 at_nbp_reg_t *nbpP = (at_nbp_reg_t *)data;
423 nve_entry_t *nve_entry, nve;
424
425 if (!(at_state.flags & AT_ST_STARTED))
426 return(ENOTREADY);
427
428 /* delete by id */
429 if (nbpP->unique_nbp_id) {
430 TAILQ_FOREACH(nve_entry, &name_registry, nve_link) {
431 if (nve_entry->unique_nbp_id == nbpP->unique_nbp_id) {
432 /* Found a match! */
433 nbp_delete_entry(nve_entry);
434 return(0);
435 }
436 }
437 return(EADDRNOTAVAIL);
438 }
439
440 /* delete by entity */
441 if (nbp_fillin_nve(&nbpP->name, &nve) != 0) {
442 /* bad tuple... */
443 return(EINVAL);
444 }
445
446 if (MULTIHOME_MODE && DEFAULT_ZONE(&nbpP->name.zone)) {
447 /* if mhome & *, remove nve from all default zones */
448 int found = FALSE; /* if any found & deleted */
449
450 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
451 nve.zone = ifID->ifZoneName;
452 nve.zone_hash = nbp_strhash(&nve.zone);
453 if ((nve_entry = nbp_find_nve(&nve)) == NULL)
454 continue;
455
456 nbp_delete_entry(nve_entry);
457 found = TRUE;
458 }
459 if (found)
460 return(0);
461 else
462 return(EADDRNOTAVAIL);
463 }
464
465 if ((nve_entry = nbp_find_nve(&nve)) == NULL)
466 /* Can't find the tuple we're looking for, send error*/
467 return(EADDRNOTAVAIL);
468
469 /* Normal case; tuple found for this name, so delete
470 * the entry from the registry and return ok response.
471 */
472 nbp_delete_entry(nve_entry);
473 return(0);
474
475 break;
476 }
477
478 case AIOCSETROUTER:
479 {
480 at_router_params_t *rt = (at_router_params_t *)data;
481
482 /* check for root access */
483 if ((error = suser(kauth_cred_get(), 0)))
484 return(EACCES);
485
486 /* when in routing/multihome mode the AIOCSETROUTER IOCTL
487 is done first */
488 if (at_state.flags & AT_ST_STARTED)
489 return(EALREADY);
490
491 /* Setup the routing & zip table size for the router */
492 if (rt->rtmp_table_sz >= RT_MIN && rt->rtmp_table_sz <= RT_MAX)
493 RT_maxentry = rt->rtmp_table_sz;
494 else
495 RT_maxentry = RT_DEFAULT;
496
497 if (rt->zone_table_sz >= ZT_MIN && rt->zone_table_sz <= ZT_MAX)
498 ZT_maxentry = rt->zone_table_sz;
499 else
500 ZT_maxentry = ZT_DEFAULT;
501
502 if (rt_table_init() == ENOBUFS)
503 return(ENOBUFS);
504
505 if (rt->router_mix)
506 RouterMix = (int)rt->router_mix;
507 else
508 RouterMix = RT_MIX_DEFAULT;
509
510 add_ddp_handler(RTMP_SOCKET, rtmp_router_input);
511
512 if (rt->multihome)
513 at_state.flags |= AT_ST_MULTIHOME;
514 else
515 at_state.flags |= AT_ST_ROUTER;
516 break;
517 }
518 case AIOCSTARTROUTER:
519 {
520 at_kern_err_t *keP = (at_kern_err_t *)data;
521
522 /* check for root access */
523 if (suser(kauth_cred_get(), 0))
524 return(EACCES);
525
526 if (!(at_state.flags & AT_ST_STARTED))
527 return(ENOTREADY);
528
529 bzero(keP, sizeof(at_kern_err_t));
530 error = routerStart(keP);
531
532 break;
533 }
534 case AIOCGETROUTER:
535 {
536 at_router_params_t *rt = (at_router_params_t *)data;
537
538 if (!(at_state.flags & AT_ST_STARTED))
539 return(ENOTREADY);
540
541 rt->multihome = (MULTIHOME_MODE)? 1: 0;
542 rt->rtmp_table_sz = RT_maxentry;
543 rt->zone_table_sz = ZT_maxentry;
544 rt->router_mix = RouterMix;
545
546 break;
547 }
548 case AIOCSTOPATALK:
549 {
550 int *count_only = (int *)data,
551 ret;
552
553 /* check for root access */
554 if ((error = suser(kauth_cred_get(), 0)))
555 return(EACCES);
556
557 ret = ddp_shutdown(*count_only);
558
559 if (*count_only != 0)
560 {
561 *count_only = ret;
562 return(0);
563 }
564 else
565 {
566 if (ret == 0)
567 {
568 /* AppleTalk was successfully shut down. Send event. */
569 atalk_post_msg(0, KEV_ATALK_DISABLED, 0, 0);
570 return 0;
571 }
572 else
573 return EBUSY;
574 }
575
576 break;
577 }
578
579 case SIOCSIFADDR:
580 /* check for root access */
581 if ((error = suser(kauth_cred_get(), 0)))
582 error = EACCES;
583 else if (ifID)
584 error = EEXIST;
585 else {
586 if (xpatcnt == 0) {
587 at_state.flags |= AT_ST_STARTING;
588 ddp_brt_init();
589 }
590
591 /* *** find an empty entry *** */
592 ifID = &at_interfaces[xpatcnt];
593 bzero((caddr_t)ifID, sizeof(at_ifaddr_t));
594 strlcpy(ifID->ifName, ifr->ifr_name, sizeof(ifID->ifName));
595
596 ifID->aa_ifp = ifp;
597 ifa = &ifID->aa_ifa;
598 error = at_domifattach(ifp, ifID);
599 if (error == EEXIST) {
600 ifID->at_was_attached = 1;
601 error = 0;
602 }
603 if (error != 0) {
604 break;
605 }
606 /* XXX ethernet-specific */
607 ifID->cable_multicast_addr = etalk_multicast_addr;
608 xpatcnt++;
609 ifnet_lock_exclusive(ifp);
610 /*
611 * Holding ifnet lock here prevents the link address
612 * from changing contents, so no need to hold the ifa
613 * lock. The link address is always present; it's
614 * never freed.
615 */
616 sdl = (struct sockaddr_dl *)ifp->if_lladdr->ifa_addr;
617 bcopy(LLADDR(sdl), ifID->xaddr, sizeof(ifID->xaddr));
618 #ifdef APPLETALK_DEBUG
619 kprintf("SIOCSIFADDR: local enet address is "
620 "%x.%x.%x.%x.%x.%x\n",
621 ifID->xaddr[0], ifID->xaddr[1],
622 ifID->xaddr[2], ifID->xaddr[3],
623 ifID->xaddr[4], ifID->xaddr[5]);
624 #endif
625
626 /* attach the AppleTalk address to the ifnet structure */
627 ifa = &ifID->aa_ifa;
628 ifa_lock_init(ifa);
629 VERIFY(!(ifa->ifa_debug & IFD_ALLOC));
630 ifa->ifa_addr = (struct sockaddr *)&ifID->ifNodeAddress;
631 ifID->ifNodeAddress.sat_len = sizeof(struct sockaddr_at);
632 ifID->ifNodeAddress.sat_family = AF_APPLETALK;
633 /* the address itself will be filled in when ifThisNode
634 is set */
635 IFA_LOCK(ifa);
636 if_attach_ifa(ifp, ifa);
637 /* add a reference for at_interfaces[] */
638 IFA_ADDREF_LOCKED(ifa);
639 IFA_UNLOCK(ifa);
640 ifnet_lock_done(ifp);
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 struct atpcb *at_pcb, *clonedat_pcb;
675 int cloned_fd = *(int *)data;
676
677 at_pcb = sotoatpcb(so);
678
679 /* let's make sure it's either -1 or a valid file descriptor */
680 if (cloned_fd != -1) {
681 struct socket *cloned_so;
682 error = file_socket(cloned_fd, &cloned_so);
683 if (error)
684 break;
685 clonedat_pcb = sotoatpcb(cloned_so);
686 } else {
687 clonedat_pcb = NULL;
688 }
689
690 if (clonedat_pcb == NULL) {
691 at_pcb->ddp_flags |= DDPFLG_STRIPHDR;
692 } else {
693 at_pcb->ddp_flags = clonedat_pcb->ddp_flags;
694 }
695 file_drop(cloned_fd);
696 break;
697 }
698
699 case SIOCPROTOATTACH:
700 /* check for root access */
701 if (suser(kauth_cred_get(), 0) != 0) {
702 error = EACCES;
703 break;
704 }
705 error = at_domifattach(ifp, ifID);
706 break;
707
708 case SIOCPROTODETACH:
709 /* check for root access */
710 if (suser(kauth_cred_get(), 0) != 0) {
711 error = EACCES;
712 break;
713 }
714 if (ifID != NULL) {
715 error = EBUSY;
716 break;
717 }
718 error = proto_unplumb(PF_APPLETALK, ifp);
719 break;
720
721 default:
722 if (ifp == 0 || ifp->if_ioctl == 0)
723 return (EOPNOTSUPP);
724 return ifnet_ioctl(ifp, 0, cmd, data);
725 }
726
727 return(error);
728 }
729
730 /* From dlil_post_msg() */
731 void atalk_post_msg(struct ifnet *ifp, u_long event_code, struct at_addr *address, at_nvestr_t *zone)
732 {
733 struct kev_atalk_data at_event_data;
734 struct kev_msg ev_msg;
735
736 bzero(&ev_msg, sizeof(struct kev_msg));
737 ev_msg.vendor_code = KEV_VENDOR_APPLE;
738 ev_msg.kev_class = KEV_NETWORK_CLASS;
739 ev_msg.kev_subclass = KEV_ATALK_SUBCLASS;
740 ev_msg.event_code = event_code;
741
742 bzero(&at_event_data, sizeof(struct kev_atalk_data));
743
744 if (ifp != 0) {
745 strlcpy(&at_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ);
746 at_event_data.link_data.if_family = ifp->if_family;
747 at_event_data.link_data.if_unit = (unsigned long) ifp->if_unit;
748 }
749
750 if (address != 0) {
751 at_event_data.node_data.address = *address;
752 }
753 else if (zone != 0) {
754 at_event_data.node_data.zone = *zone;
755 }
756
757 ev_msg.dv[0].data_length = sizeof(struct kev_atalk_data);
758 ev_msg.dv[0].data_ptr = &at_event_data;
759 ev_msg.dv[1].data_length = 0;
760
761 kev_post_msg(&ev_msg);
762 }
763
764
765 /*
766 * This is untested; the code is here only for completeness.
767 */
768 void
769 at_purgeaddrs(struct ifnet *ifp)
770 {
771 at_ifaddr_t *ifID = NULL;
772 int pat_id;
773
774 /* Find address for this interface, if it exists */
775 for (pat_id = 0; pat_id < xpatcnt; pat_id++) {
776 if (at_interfaces[pat_id].aa_ifp == ifp) {
777 ifID = &at_interfaces[pat_id];
778 elap_offline(ifID);
779 }
780 }
781 }