]> git.saurik.com Git - apple/xnu.git/blob - bsd/netat/at.c
xnu-792.18.15.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(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 zno = zt_add_zone(ifz->zone_name.str, ifz->zone_name.len);
96
97 if (zno == ZT_MAXEDOUT) {
98 dPrintf(D_M_ELAP, D_L_ERROR, ("set_zones: error: table full\n"));
99 return(ENOSPC);
100 }
101 if (ifz->zone_home) {
102 ifID_home->ifZoneName = ifz->zone_name;
103 ifID_home->ifDefZone = zno;
104 }
105
106 for (i=0; i<IF_TOTAL_MAX; i++) {
107 if (ifz->zone_iflist.at_if[i][0]) {
108 if ((ifID = find_ifID(ifz->zone_iflist.at_if[i]))) {
109 rte = rt_blookup(ifID->ifThisCableEnd);
110 if (!rte) {
111 dPrintf(D_M_ELAP, D_L_ERROR,
112 ("set_zones: error: can't find route\n"));
113 } else {
114 zt_set_zmap(zno, rte->ZoneBitMap);
115
116 /* if first zone for this I/F,
117 make default */
118 if (!ifID->ifDefZone)
119 ifID->ifDefZone = zno;
120 }
121 }
122 }
123 }
124
125 return(0);
126 } /* set_zones */
127
128 /*
129 * Generic internet control operations (ioctl's).
130 * ifp is 0 if not an interface-specific ioctl.
131 */
132
133 int
134 at_control(so, cmd, data, ifp)
135 struct socket *so;
136 u_long cmd;
137 caddr_t data;
138 struct ifnet *ifp;
139 {
140 struct ifreq *ifr = (struct ifreq *)data;
141 int pat_id = 0, error = 0;
142 struct proc *p = current_proc();
143 at_ifaddr_t *ifID = 0;
144 struct ifaddr *ifa;
145 struct sockaddr_dl *sdl;
146
147 if ((cmd & 0xffff) == 0xff99) {
148 u_long fixed_command;
149 /* *** this is a temporary hack to get at_send_to_dev() to
150 work with BSD-style sockets instead of the special purpose
151 system calls, ATsocket() and ATioctl().
152 *** */
153 fixed_command = _IOW(0, 0xff99, user_addr_t);
154 if ((error = at_ioctl((struct atpcb *)so->so_pcb, fixed_command, data, 0))) {
155 if (((struct atpcb *)so->so_pcb)->proto != ATPROTO_LAP) {
156 ((struct atpcb *)so->so_pcb)->proto = ATPROTO_LAP;
157 error = at_ioctl((struct atpcb *)so->so_pcb, fixed_command, data , 0);
158 }
159 }
160 return(error);
161
162 /* *** processing should be
163 return(EINVAL);
164 *** */
165 }
166 /*
167 * Find address for this interface, if it exists.
168 */
169 if (ifp)
170 for (pat_id = 0; pat_id < xpatcnt; pat_id++)
171 if (at_interfaces[pat_id].aa_ifp == ifp) {
172 ifID = &at_interfaces[pat_id];
173 break;
174 }
175
176 switch (cmd) {
177
178 case AIOCGETSTATE:
179 {
180 at_state_t *global_state = (at_state_t *)data;
181
182 *global_state = at_state;
183 return(0);
184 break;
185 }
186
187 case AIOCGETIFCFG:
188 {
189 at_if_cfg_t *cfgp = (at_if_cfg_t *)data;
190
191 ifID = 0;
192 if ((at_state.flags & AT_ST_STARTED) &&
193 ifID_home) {
194 if (strlen(cfgp->ifr_name)) {
195 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
196 if (!strncmp(ifID->ifName, cfgp->ifr_name,
197 strlen(ifID->ifName)))
198 break;
199 }
200 } else {
201 ifID = ifID_home;
202 strlcpy(cfgp->ifr_name, ifID->ifName,
203 sizeof(cfgp->ifr_name));
204 }
205 if (ifID && ifID->ifState != LAP_OFFLINE) {
206 cfgp->flags = ifID->ifFlags;
207 /* put the IF state into the low order
208 bits of flags */
209 cfgp->flags |= (ifID->ifState & LAP_STATE_MASK);
210 cfgp->node = ifID->ifThisNode;
211 cfgp->router = ifID->ifARouter;
212 cfgp->netStart = ifID->ifThisCableStart;
213 cfgp->netEnd = ifID->ifThisCableEnd;
214 cfgp->zonename = ifID->ifZoneName;
215 return(0);
216 } else
217 return(EINVAL);
218 } else
219 return(ENOTREADY);
220 break;
221 }
222
223 case AIOCSETDEFZONE:
224 {
225 at_def_zone_t *defzonep = (at_def_zone_t *)data;
226
227 /* check for root access */
228 if (error = suser(kauth_cred_get(), 0))
229 return(EACCES);
230
231 ifID = 0;
232 if ((at_state.flags & AT_ST_STARTED) && ifID_home) {
233 if (strlen(defzonep->ifr_name)) {
234 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
235 if (!strncmp(ifID->ifName, defzonep->ifr_name,
236 strlen(ifID->ifName)))
237 break;
238 }
239 } else {
240 ifID = ifID_home;
241 strlcpy(defzonep->ifr_name, ifID->ifName,
242 sizeof(defzonep->ifr_name));
243 }
244
245 /* In routing mode the default zone is only set for the
246 default interface. */
247 if (ROUTING_MODE && (ifID != ifID_home))
248 return(EINVAL);
249
250 if (ifID && ifID->ifState != LAP_OFFLINE) {
251 if (zonename_equal(&ifID->ifZoneName,
252 &defzonep->zonename))
253 return(0);
254 else {
255 /* check the zone name */
256 if (MULTIPORT_MODE) {
257 short zno;
258 at_ifnames_t ifs_in_zone;
259
260 if (!(zno = zt_find_zname(&defzonep->zonename)))
261 return(EINVAL);
262
263 getIfUsage(zno-1, &ifs_in_zone);
264 if (!ifs_in_zone.at_if[ifID->ifPort])
265 return(EINVAL);
266 ifID->ifDefZone = zno+1;
267 } else {
268 int i;
269 at_nvestr_t *zone;
270
271 for (i = 0, zone = getSPLocalZone(i);
272 zone;
273 i++, zone = getSPLocalZone(i)) {
274 if (zonename_equal(zone,
275 &defzonep->zonename))
276 break;
277 }
278 if (!zone)
279 return(EINVAL);
280 }
281 ifID->ifZoneName = defzonep->zonename;
282 (void)regDefaultZone(ifID);
283
284 /* AppleTalk zone was changed. Send event with zone info. */
285 atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ZONEUPDATED, 0, &(ifID->ifZoneName));
286
287 return(0);
288 }
289 } else
290 return(EINVAL);
291 } else
292 return(ENOTREADY);
293 break;
294 }
295
296 case AIOCREGLOCALZN:
297 {
298 at_nvestr_t *zone = (at_nvestr_t *)data;
299
300 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
301 return(ENOTREADY);
302
303 if (MULTIPORT_MODE)
304 return(EINVAL);
305
306 return(setLocalZones(zone, zone->len));
307
308 break;
309 }
310 case AIOCSETZNUSAGE:
311 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
312 return(ENOTREADY);
313
314 if (!ROUTING_MODE)
315 return(EINVAL);
316
317 return(set_zones((zone_usage_t *)data));
318
319 break;
320
321 case AIOCGETZNUSAGE:
322 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
323 return(ENOTREADY);
324
325 if (!MULTIPORT_MODE)
326 return(EINVAL);
327
328 if (getRTRLocalZone((zone_usage_t *)data))
329 return(0);
330 else
331 return(ENOENT);
332 break;
333
334 case AIOCNBPREG:
335 {
336 at_nbp_reg_t *nbpP = (at_nbp_reg_t *)data;
337 nve_entry_t nve;
338 int error2;
339
340 if (!(at_state.flags & AT_ST_STARTED) || !ifID_home)
341 return(ENOTREADY);
342
343 /* multihoming mode */
344 if (MULTIHOME_MODE) {
345 return(nbp_mh_reg(nbpP));
346 }
347
348 /* single port mode or router mode */
349 if (nbp_fillin_nve(&nbpP->name, &nve) != 0) {
350 /* bad tuple... */
351 return(EINVAL);
352 }
353
354 /* In routing mode when the zone is specified, we need to
355 find an interface on which the specified zone is seeded, so
356 that the zone multicast will be plausible. */
357 if (ROUTING_MODE && !(DEFAULT_ZONE(&nve.zone))) {
358 /* find first segment (interface) which is seeded for
359 this zone */
360 int finished = FALSE;
361 int zno;
362 at_ifnames_t ifs_in_zone;
363 if (!(zno = zt_find_zname(&nve.zone))) {
364 return(EINVAL);
365 }
366 getIfUsage(zno-1, &ifs_in_zone);
367
368 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
369 if (!ifs_in_zone.at_if[ifID->ifPort])
370 /* zone doesn't match */
371 continue;
372 else {
373 finished = TRUE;
374 break;
375 }
376 }
377 if (!finished)
378 return(EINVAL);
379 } else
380 ifID = ifID_home;
381
382 nve.address.net = ifID->ifThisNode.s_net;
383 nve.address.node = ifID->ifThisNode.s_node;
384 nve.address.socket = nbpP->addr.socket;
385 nve.ddptype = nbpP->ddptype;
386
387 if (nbp_find_nve(&nve))
388 return(EADDRNOTAVAIL);
389
390 /* Normal case; no tuple found for this name, so insert
391 * this tuple in the registry and return ok response.
392 */
393 if ((error2 = nbp_new_nve_entry(&nve, ifID)) == 0) {
394 nbpP->addr.net = ifID->ifThisNode.s_net;
395 nbpP->addr.node = ifID->ifThisNode.s_node;
396 nbpP->unique_nbp_id = nve.unique_nbp_id;
397 }
398
399 return(error2);
400 break;
401 }
402
403 case AIOCNBPREMOVE:
404 {
405 at_nbp_reg_t *nbpP = (at_nbp_reg_t *)data;
406 nve_entry_t *nve_entry, nve;
407
408 if (!(at_state.flags & AT_ST_STARTED))
409 return(ENOTREADY);
410
411 /* delete by id */
412 if (nbpP->unique_nbp_id) {
413 TAILQ_FOREACH(nve_entry, &name_registry, nve_link) {
414 if (nve_entry->unique_nbp_id == nbpP->unique_nbp_id) {
415 /* Found a match! */
416 nbp_delete_entry(nve_entry);
417 return(0);
418 }
419 }
420 return(EADDRNOTAVAIL);
421 }
422
423 /* delete by entity */
424 if (nbp_fillin_nve(&nbpP->name, &nve) != 0) {
425 /* bad tuple... */
426 return(EINVAL);
427 }
428
429 if (MULTIHOME_MODE && DEFAULT_ZONE(&nbpP->name.zone)) {
430 /* if mhome & *, remove nve from all default zones */
431 int found = FALSE; /* if any found & deleted */
432
433 TAILQ_FOREACH(ifID, &at_ifQueueHd, aa_link) {
434 nve.zone = ifID->ifZoneName;
435 nve.zone_hash = nbp_strhash(&nve.zone);
436 if ((nve_entry = nbp_find_nve(&nve)) == NULL)
437 continue;
438
439 nbp_delete_entry(nve_entry);
440 found = TRUE;
441 }
442 if (found)
443 return(0);
444 else
445 return(EADDRNOTAVAIL);
446 }
447
448 if ((nve_entry = nbp_find_nve(&nve)) == NULL)
449 /* Can't find the tuple we're looking for, send error*/
450 return(EADDRNOTAVAIL);
451
452 /* Normal case; tuple found for this name, so delete
453 * the entry from the registry and return ok response.
454 */
455 nbp_delete_entry(nve_entry);
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(kauth_cred_get(), 0))
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(kauth_cred_get(), 0))
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(kauth_cred_get(), 0))
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(kauth_cred_get(), 0))
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 strlcpy(ifID->ifName, ifr->ifr_name, sizeof(ifID->ifName));
579
580 ifID->aa_ifp = ifp;
581 ifa = &ifID->aa_ifa;
582 ifnet_lock_exclusive(ifp);
583 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
584 if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) &&
585 (sdl->sdl_family == AF_LINK)) {
586 bcopy(LLADDR(sdl), ifID->xaddr, sizeof(ifID->xaddr));
587 #ifdef APPLETALK_DEBUG
588 kprintf("SIOCSIFADDR: local enet address is %x.%x.%x.%x.%x.%x\n",
589 ifID->xaddr[0], ifID->xaddr[1],
590 ifID->xaddr[2], ifID->xaddr[3],
591 ifID->xaddr[4], ifID->xaddr[5]);
592 #endif
593 break;
594 }
595
596 /* attach the AppleTalk address to the ifnet structure */
597 ifa = &ifID->aa_ifa;
598 ifa->ifa_addr = (struct sockaddr *)&ifID->ifNodeAddress;
599 ifID->ifNodeAddress.sat_len = sizeof(struct sockaddr_at);
600 ifID->ifNodeAddress.sat_family = AF_APPLETALK;
601 /* the address itself will be filled in when ifThisNode
602 is set */
603 if_attach_ifa(ifp, ifa);
604 ifnet_lock_done(ifp);
605
606 switch (ifp->if_type) {
607 case IFT_ETHER:
608 case IFT_L2VLAN:
609 case IFT_IEEE8023ADLAG: /* bonded ethernet */
610 ether_attach_at(ifp);
611 error = 0;
612 ifID->cable_multicast_addr = etalk_multicast_addr;
613
614 xpatcnt++;
615 break;
616 case IFT_FDDI:
617 ifID->cable_multicast_addr = etalk_multicast_addr;
618 ddp_bit_reverse(&ifID->cable_multicast_addr);
619 xpatcnt++;
620 break;
621 case IFT_ISO88025: /* token ring */
622 ifID->cable_multicast_addr = ttalk_multicast_addr;
623 ddp_bit_reverse(&ifID->cable_multicast_addr);
624
625 xpatcnt++;
626 break;
627 default:
628 error = EINVAL;
629 }
630 }
631 break;
632
633 /* complete the initialization started in SIOCSIFADDR */
634 case AIOCSIFADDR:
635 {
636 at_if_cfg_t *cfgp = (at_if_cfg_t *)data;
637
638 if (!(at_state.flags & AT_ST_STARTING))
639 return(ENOTREADY);
640
641 if (!(ifID = find_ifID(cfgp->ifr_name)))
642 return(EINVAL);
643
644 return(lap_online(ifID, cfgp));
645 break;
646 }
647
648 #ifdef NOT_YET
649 /* *** this can't be added until AT can handle dynamic addition and
650 deletion of interfaces *** */
651 case SIOCDIFADDR:
652 /* check for root access */
653 if (error = suser(kauth_cred_get(), 0))
654 error = EACCES;
655 else if (!ifID)
656 error = EINVAL;
657 else
658 elap_offline(ifID);
659 break;
660 #endif
661
662 case SIOCSETOT: {
663 struct atpcb *at_pcb, *clonedat_pcb;
664 int cloned_fd = *(int *)data;
665
666 at_pcb = sotoatpcb(so);
667
668 /* let's make sure it's either -1 or a valid file descriptor */
669 if (cloned_fd != -1) {
670 struct socket *cloned_so;
671 error = file_socket(cloned_fd, &cloned_so);
672 if (error)
673 break;
674 clonedat_pcb = sotoatpcb(cloned_so);
675 } else {
676 clonedat_pcb = NULL;
677 }
678
679 if (clonedat_pcb == NULL) {
680 at_pcb->ddp_flags |= DDPFLG_STRIPHDR;
681 } else {
682 at_pcb->ddp_flags = clonedat_pcb->ddp_flags;
683 }
684 file_drop(cloned_fd);
685 break;
686 }
687
688 default:
689 if (ifp == 0 || ifp->if_ioctl == 0)
690 return (EOPNOTSUPP);
691 return dlil_ioctl(0, ifp, cmd, (caddr_t) data);
692 }
693
694 return(error);
695 }
696
697 /* From dlil_post_msg() */
698 void atalk_post_msg(struct ifnet *ifp, u_long event_code, struct at_addr *address, at_nvestr_t *zone)
699 {
700 struct kev_atalk_data at_event_data;
701 struct kev_msg ev_msg;
702
703 ev_msg.vendor_code = KEV_VENDOR_APPLE;
704 ev_msg.kev_class = KEV_NETWORK_CLASS;
705 ev_msg.kev_subclass = KEV_ATALK_SUBCLASS;
706 ev_msg.event_code = event_code;
707
708 bzero(&at_event_data, sizeof(struct kev_atalk_data));
709
710 if (ifp != 0) {
711 strlcpy(&at_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ);
712 at_event_data.link_data.if_family = ifp->if_family;
713 at_event_data.link_data.if_unit = (unsigned long) ifp->if_unit;
714 }
715
716 if (address != 0) {
717 at_event_data.node_data.address = *address;
718 }
719 else if (zone != 0) {
720 at_event_data.node_data.zone = *zone;
721 }
722
723 ev_msg.dv[0].data_length = sizeof(struct kev_atalk_data);
724 ev_msg.dv[0].data_ptr = &at_event_data;
725 ev_msg.dv[1].data_length = 0;
726
727 kev_post_msg(&ev_msg);
728 }