2 * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * Copyright (c) 1998 Apple Computer, Inc.
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>
43 #include <sys/kauth.h>
46 #include <net/if_dl.h>
47 #include <net/if_types.h>
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>
59 #include <sys/kern_event.h>
60 #include <net/kpi_protocol.h>
62 int lap_online( at_ifaddr_t
*, at_if_cfg_t
*cfgp
);
64 extern int routerStart(at_kern_err_t
*);
65 extern void elap_offline(at_ifaddr_t
*);
66 extern at_ifaddr_t
*find_ifID(char *);
69 extern at_ifaddr_t at_interfaces
[];
70 extern at_ifaddr_t
*ifID_home
;
71 extern TAILQ_HEAD(name_registry
, _nve_
) name_registry
;
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}};
79 /* called only in router mode */
80 static int set_zones(zone_usage_t
*ifz
)
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
86 returns 0 if successful
87 errno if error occurred
95 if (ifz
->zone_name
.len
<= 0 || ifz
->zone_name
.len
> NBP_NVE_STR_SIZE
)
98 zno
= zt_add_zone((char *)ifz
->zone_name
.str
, ifz
->zone_name
.len
);
100 if (zno
== ZT_MAXEDOUT
) {
101 dPrintf(D_M_ELAP
, D_L_ERROR
, ("set_zones: error: table full\n"));
104 if (ifz
->zone_home
) {
105 ifID_home
->ifZoneName
= ifz
->zone_name
;
106 ifID_home
->ifDefZone
= zno
;
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
);
114 dPrintf(D_M_ELAP
, D_L_ERROR
,
115 ("set_zones: error: can't find route\n"));
117 zt_set_zmap(zno
, rte
->ZoneBitMap
);
119 /* if first zone for this I/F,
121 if (!ifID
->ifDefZone
)
122 ifID
->ifDefZone
= zno
;
132 at_domifattach(struct ifnet
*ifp
, at_ifaddr_t
*ifID
)
136 if ((error
= proto_plumb(PF_APPLETALK
, ifp
))) {
138 log(LOG_ERR
, "%s: proto_plumb returned %d if=%s%d\n",
139 __func__
, error
, ifp
->if_name
, ifp
->if_unit
);
141 ifID
->at_was_attached
= 1;
147 * Generic internet control operations (ioctl's).
148 * ifp is 0 if not an interface-specific ioctl.
152 at_control(so
, cmd
, data
, ifp
)
158 struct ifreq
*ifr
= (struct ifreq
*)data
;
159 int pat_id
= 0, error
= 0;
160 at_ifaddr_t
*ifID
= 0;
162 struct sockaddr_dl
*sdl
;
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().
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);
179 /* *** processing should be
184 * Find address for this interface, if it exists.
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
];
197 at_state_t
*global_state
= (at_state_t
*)data
;
199 *global_state
= at_state
;
206 at_if_cfg_t
*cfgp
= (at_if_cfg_t
*)data
;
209 if ((at_state
.flags
& AT_ST_STARTED
) &&
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
)))
219 strlcpy(cfgp
->ifr_name
, ifID
->ifName
,
220 sizeof(cfgp
->ifr_name
));
222 if (ifID
&& ifID
->ifState
!= LAP_OFFLINE
) {
223 cfgp
->flags
= ifID
->ifFlags
;
224 /* put the IF state into the low order
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
;
242 at_def_zone_t
*defzonep
= (at_def_zone_t
*)data
;
244 /* check for root access */
245 if ((error
= suser(kauth_cred_get(), 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
)))
258 strlcpy(defzonep
->ifr_name
, ifID
->ifName
,
259 sizeof(defzonep
->ifr_name
));
262 /* In routing mode the default zone is only set for the
263 default interface. */
264 if (ROUTING_MODE
&& (ifID
!= ifID_home
))
267 if (ifID
&& ifID
->ifState
!= LAP_OFFLINE
) {
268 if (zonename_equal(&ifID
->ifZoneName
,
269 &defzonep
->zonename
))
272 /* check the zone name */
273 if (MULTIPORT_MODE
) {
275 at_ifnames_t ifs_in_zone
;
277 if (!(zno
= zt_find_zname(&defzonep
->zonename
)))
280 getIfUsage(zno
-1, &ifs_in_zone
);
281 if (!ifs_in_zone
.at_if
[ifID
->ifPort
])
283 ifID
->ifDefZone
= zno
+1;
288 for (i
= 0, zone
= getSPLocalZone(i
);
290 i
++, zone
= getSPLocalZone(i
)) {
291 if (zonename_equal(zone
,
292 &defzonep
->zonename
))
298 ifID
->ifZoneName
= defzonep
->zonename
;
299 (void)regDefaultZone(ifID
);
301 /* AppleTalk zone was changed. Send event with zone info. */
302 atalk_post_msg(ifID
->aa_ifp
, KEV_ATALK_ZONEUPDATED
, 0, &(ifID
->ifZoneName
));
315 at_nvestr_t
*zone
= (at_nvestr_t
*)data
;
317 if (!(at_state
.flags
& AT_ST_STARTED
) || !ifID_home
)
323 return(setLocalZones(zone
, zone
->len
));
328 if (!(at_state
.flags
& AT_ST_STARTED
) || !ifID_home
)
334 return(set_zones((zone_usage_t
*)data
));
339 if (!(at_state
.flags
& AT_ST_STARTED
) || !ifID_home
)
345 if (getRTRLocalZone((zone_usage_t
*)data
))
353 at_nbp_reg_t
*nbpP
= (at_nbp_reg_t
*)data
;
357 if (!(at_state
.flags
& AT_ST_STARTED
) || !ifID_home
)
360 /* multihoming mode */
361 if (MULTIHOME_MODE
) {
362 return(nbp_mh_reg(nbpP
));
365 /* single port mode or router mode */
366 if (nbp_fillin_nve(&nbpP
->name
, &nve
) != 0) {
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
377 int finished
= FALSE
;
379 at_ifnames_t ifs_in_zone
;
380 if (!(zno
= zt_find_zname(&nve
.zone
))) {
383 getIfUsage(zno
-1, &ifs_in_zone
);
385 TAILQ_FOREACH(ifID
, &at_ifQueueHd
, aa_link
) {
386 if (!ifs_in_zone
.at_if
[ifID
->ifPort
])
387 /* zone doesn't match */
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
;
404 if (nbp_find_nve(&nve
))
405 return(EADDRNOTAVAIL
);
407 /* Normal case; no tuple found for this name, so insert
408 * this tuple in the registry and return ok response.
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
;
422 at_nbp_reg_t
*nbpP
= (at_nbp_reg_t
*)data
;
423 nve_entry_t
*nve_entry
, nve
;
425 if (!(at_state
.flags
& AT_ST_STARTED
))
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
) {
433 nbp_delete_entry(nve_entry
);
437 return(EADDRNOTAVAIL
);
440 /* delete by entity */
441 if (nbp_fillin_nve(&nbpP
->name
, &nve
) != 0) {
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 */
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
)
456 nbp_delete_entry(nve_entry
);
462 return(EADDRNOTAVAIL
);
465 if ((nve_entry
= nbp_find_nve(&nve
)) == NULL
)
466 /* Can't find the tuple we're looking for, send error*/
467 return(EADDRNOTAVAIL
);
469 /* Normal case; tuple found for this name, so delete
470 * the entry from the registry and return ok response.
472 nbp_delete_entry(nve_entry
);
480 at_router_params_t
*rt
= (at_router_params_t
*)data
;
482 /* check for root access */
483 if ((error
= suser(kauth_cred_get(), 0)))
486 /* when in routing/multihome mode the AIOCSETROUTER IOCTL
488 if (at_state
.flags
& AT_ST_STARTED
)
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
;
495 RT_maxentry
= RT_DEFAULT
;
497 if (rt
->zone_table_sz
>= ZT_MIN
&& rt
->zone_table_sz
<= ZT_MAX
)
498 ZT_maxentry
= rt
->zone_table_sz
;
500 ZT_maxentry
= ZT_DEFAULT
;
502 if (rt_table_init() == ENOBUFS
)
506 RouterMix
= (int)rt
->router_mix
;
508 RouterMix
= RT_MIX_DEFAULT
;
510 add_ddp_handler(RTMP_SOCKET
, rtmp_router_input
);
513 at_state
.flags
|= AT_ST_MULTIHOME
;
515 at_state
.flags
|= AT_ST_ROUTER
;
518 case AIOCSTARTROUTER
:
520 at_kern_err_t
*keP
= (at_kern_err_t
*)data
;
522 /* check for root access */
523 if (suser(kauth_cred_get(), 0))
526 if (!(at_state
.flags
& AT_ST_STARTED
))
529 bzero(keP
, sizeof(at_kern_err_t
));
530 error
= routerStart(keP
);
536 at_router_params_t
*rt
= (at_router_params_t
*)data
;
538 if (!(at_state
.flags
& AT_ST_STARTED
))
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
;
550 int *count_only
= (int *)data
,
553 /* check for root access */
554 if ((error
= suser(kauth_cred_get(), 0)))
557 ret
= ddp_shutdown(*count_only
);
559 if (*count_only
!= 0)
568 /* AppleTalk was successfully shut down. Send event. */
569 atalk_post_msg(0, KEV_ATALK_DISABLED
, 0, 0);
580 /* check for root access */
581 if ((error
= suser(kauth_cred_get(), 0)))
587 at_state
.flags
|= AT_ST_STARTING
;
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
));
598 error
= at_domifattach(ifp
, ifID
);
599 if (error
== EEXIST
) {
600 ifID
->at_was_attached
= 1;
606 /* XXX ethernet-specific */
607 ifID
->cable_multicast_addr
= etalk_multicast_addr
;
609 ifnet_lock_exclusive(ifp
);
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
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]);
626 /* attach the AppleTalk address to the ifnet structure */
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
636 if_attach_ifa(ifp
, ifa
);
637 /* add a reference for at_interfaces[] */
638 IFA_ADDREF_LOCKED(ifa
);
640 ifnet_lock_done(ifp
);
644 /* complete the initialization started in SIOCSIFADDR */
647 at_if_cfg_t
*cfgp
= (at_if_cfg_t
*)data
;
649 if (!(at_state
.flags
& AT_ST_STARTING
))
652 if (!(ifID
= find_ifID(cfgp
->ifr_name
)))
655 return(lap_online(ifID
, cfgp
));
660 /* *** this can't be added until AT can handle dynamic addition and
661 deletion of interfaces *** */
663 /* check for root access */
664 if (error
= suser(kauth_cred_get(), 0))
674 struct atpcb
*at_pcb
, *clonedat_pcb
;
675 int cloned_fd
= *(int *)data
;
677 at_pcb
= sotoatpcb(so
);
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
);
685 clonedat_pcb
= sotoatpcb(cloned_so
);
690 if (clonedat_pcb
== NULL
) {
691 at_pcb
->ddp_flags
|= DDPFLG_STRIPHDR
;
693 at_pcb
->ddp_flags
= clonedat_pcb
->ddp_flags
;
695 file_drop(cloned_fd
);
699 case SIOCPROTOATTACH
:
700 /* check for root access */
701 if (suser(kauth_cred_get(), 0) != 0) {
705 error
= at_domifattach(ifp
, ifID
);
708 case SIOCPROTODETACH
:
709 /* check for root access */
710 if (suser(kauth_cred_get(), 0) != 0) {
718 error
= proto_unplumb(PF_APPLETALK
, ifp
);
722 if (ifp
== 0 || ifp
->if_ioctl
== 0)
724 return ifnet_ioctl(ifp
, 0, cmd
, data
);
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
)
733 struct kev_atalk_data at_event_data
;
734 struct kev_msg ev_msg
;
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
;
742 bzero(&at_event_data
, sizeof(struct kev_atalk_data
));
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
;
751 at_event_data
.node_data
.address
= *address
;
753 else if (zone
!= 0) {
754 at_event_data
.node_data
.zone
= *zone
;
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;
761 kev_post_msg(&ev_msg
);
766 * This is untested; the code is here only for completeness.
769 at_purgeaddrs(struct ifnet
*ifp
)
771 at_ifaddr_t
*ifID
= NULL
;
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
];