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 62int lap_online( at_ifaddr_t *, at_if_cfg_t *cfgp); 63 64extern int routerStart(at_kern_err_t *); 65extern void elap_offline(at_ifaddr_t *); 66extern at_ifaddr_t *find_ifID(char *); 67 68extern int xpatcnt; 69extern at_ifaddr_t at_interfaces[]; 70extern at_ifaddr_t *ifID_home; 71extern TAILQ_HEAD(name_registry, _nve_) name_registry; 72extern int nve_lock; 73 74struct etalk_addr etalk_multicast_addr = { 75 {0x09, 0x00, 0x07, 0xff, 0xff, 0xff}}; 76struct etalk_addr ttalk_multicast_addr = { 77 {0xC0, 0x00, 0x40, 0x00, 0x00, 0x00}}; 78 79/* called only in router mode */ 80static 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 131static int 132at_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 151int 152at_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() */ 731void 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 */ 768void 769at_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} 782