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