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) 1993-1998 Apple Computer, Inc.
30 *	All Rights Reserved.
31 */
32
33/*
34 *   Modified, March 17, 1997 by Tuyen Nguyen for MacOSX.
35 */
36
37#include <sys/errno.h>
38#include <sys/types.h>
39#include <sys/param.h>
40#include <machine/spl.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/proc.h>
44#include <sys/filedesc.h>
45#include <sys/fcntl.h>
46#include <sys/mbuf.h>
47#include <sys/ioctl.h>
48#include <sys/malloc.h>
49#include <sys/socket.h>
50#include <sys/socketvar.h>
51
52#include <net/if.h>
53
54#include <netat/sysglue.h>
55#include <netat/appletalk.h>
56#include <netat/at_pcb.h>
57#include <netat/at_var.h>
58#include <netat/ddp.h>
59#include <netat/rtmp.h>
60#include <netat/zip.h>
61#include <netat/routing_tables.h>
62#include <netat/debug.h>
63
64#include <sys/kern_event.h>
65
66
67/****************************************************************/
68/*								*/
69/*								*/
70/*			RTMP Protocol				*/
71/*								*/
72/*								*/
73/****************************************************************/
74
75
76/* rtmp.c: , 1.6; 2/26/93; Apple Computer, Inc." */
77
78
79#define	NROUTERS2TRAK	8
80#define	FIFTYSECS	10
81#define NODE(r)		((r)->ifARouter.s_node)
82#define NET(r)		((r)->ifARouter.s_net)
83#define	INUSE(r)	(NODE(r))
84
85void ddp_age_router(void *arg);
86
87static struct routerinfo {
88	struct at_addr ifARouter;
89	at_ifaddr_t	  *ifID;
90	void		  *tmo;
91} trackedrouters[NROUTERS2TRAK];
92
93void trackrouter_rem_if(ifID)
94     register at_ifaddr_t *ifID;
95{
96	int i;
97	register struct routerinfo *router;
98
99	for (i = NROUTERS2TRAK; --i >= 0;) {
100		router = &trackedrouters[i];
101		if (trackedrouters[i].ifID == ifID) {
102			untimeout(ddp_age_router, (caddr_t)router);
103			break;
104		}
105	}
106}
107
108
109void routershutdown(void)
110{
111	int i;
112
113	for (i = NROUTERS2TRAK; --i >= 0;) {
114		register struct routerinfo *router;
115
116		router = &trackedrouters[i];
117		if (INUSE(router)) {
118			untimeout(ddp_age_router, (caddr_t) router);
119			bzero((caddr_t) router, sizeof(struct routerinfo));
120		}
121	}
122}
123
124int router_added  = 0;
125int router_killed = 0;
126
127
128
129void trackrouter(ifID, net, node)
130     register at_ifaddr_t *ifID;
131     register unsigned short	net;
132     register unsigned char	node;
133{
134	register struct routerinfo *unused = NULL;
135	int i;
136
137	for (i = NROUTERS2TRAK; --i >= 0;) {
138		register struct routerinfo *router;
139
140		router = &trackedrouters[(i + node) & (NROUTERS2TRAK-1)];
141		if ((NODE(router) == node) && (NET(router) == net)) {
142			untimeout(ddp_age_router, (caddr_t) router);
143			timeout(ddp_age_router, (caddr_t) router, 50*SYS_HZ);
144			unused = NULL;
145			break;
146		}
147		else if (!INUSE(router) && !unused)
148			unused = router;
149	}
150	if (unused) {
151		router_added++;
152
153		if (ifID->ifARouter.s_net == 0) {
154			/* Send event that this interface just got a router. This does not
155				discriminate on whether this router is valid or not. If it is not
156				valid rtmp_input will send a KEV_ATALK_ROUTERUP_INVALID event. */
157			atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ROUTERUP, 0, 0);
158		}
159
160		unused->ifID = ifID;
161		NET(unused) =  net;
162		NODE(unused) = node;
163		ifID->ifRouterState = ROUTER_AROUND;
164		timeout(ddp_age_router, (caddr_t) unused, 50*SYS_HZ);
165
166		if (NET(ifID) == 0 && NODE(ifID) == 0) {
167			NET(ifID) = net;
168			NODE(ifID) = node;
169		}
170	}
171}
172
173/*
174 * This is the timeout function that is called after 50 seconds,
175 * if no router packets come in. That way we won't send extended
176 * frames to something that is not there. Untimeout is called if
177 * an RTMP packet comes in so this routine will not be called.
178 */
179void ddp_age_router(void *arg)
180{
181	struct routerinfo *deadrouter = (struct routerinfo*)arg;
182	register at_ifaddr_t *ourrouter;
183
184	atalk_lock();
185
186	ourrouter = deadrouter->ifID;
187	if (ourrouter == NULL) {
188		atalk_unlock();
189		return;
190	}
191
192	dPrintf(D_M_RTMP, D_L_INFO,
193		("ddp_age_router called deadrouter=%d:%d\n", NODE(deadrouter), NET(deadrouter)));
194
195	router_killed++;
196
197	if (NODE(ourrouter) == NODE(deadrouter) &&
198	    NET(ourrouter) == NET(deadrouter)) {
199		register unsigned long	atrandom = random();
200		register struct routerinfo *newrouter = NULL;
201		int i;
202
203		bzero((caddr_t) deadrouter, sizeof(struct routerinfo));
204		for (i = NROUTERS2TRAK; --i >= 0;) {
205			newrouter = &trackedrouters[(i + atrandom) & (NROUTERS2TRAK-1)];
206			if (INUSE(newrouter))
207				break;
208			else
209				newrouter = NULL;
210		}
211		if (newrouter) {
212			/* Set our router to another on the list and go on with life */
213			NET(ourrouter) = NET(newrouter);
214			NODE(ourrouter) = NODE(newrouter);
215		}
216		else {
217			/* from gorouterless() */
218			/* We have no other routers. */
219			ATTRACE(AT_MID_DDP, AT_SID_TIMERS, AT_LV_WARNING, FALSE,
220				"ddp_age_router entry : ARouter = 0x%x, RouterState = 0x%x",
221				ATALK_VALUE(ourrouter->ifARouter), ourrouter->ifRouterState, 0);
222
223			switch (ourrouter->ifRouterState) {
224			case ROUTER_AROUND :
225				/* This is where we lose our cable.
226					Reset router fields and state accordingly. */
227				ourrouter->ifARouter.s_net = 0;
228				ourrouter->ifARouter.s_node = 0;
229				ourrouter->ifThisCableStart = DDP_MIN_NETWORK;
230				ourrouter->ifThisCableEnd = DDP_MAX_NETWORK;
231				ourrouter->ifRouterState = NO_ROUTER;
232
233				/* Send event to indicate that we've lost our seed router. */
234				atalk_post_msg(ourrouter->aa_ifp, KEV_ATALK_ROUTERDOWN, 0, 0);
235
236				zip_control(ourrouter, ZIP_NO_ROUTER);
237				break;
238			case ROUTER_WARNING :
239				/* there was a router that we were ignoring...
240				 * now, even that's gone.  But we want to tackle the
241				 * case where another router may come up after all
242				 * of them have died...
243				 */
244				ourrouter->ifRouterState = NO_ROUTER;
245				break;
246			}
247		}
248	} else
249	        bzero((caddr_t) deadrouter, sizeof(struct routerinfo));
250
251	atalk_unlock();
252
253} /* ddp_age_router */
254
255void rtmp_input (mp, ifID)
256     register gbuf_t *mp;
257     register at_ifaddr_t *ifID;
258{
259	register at_net_al	this_net;
260	register at_net_al	range_start, range_end;
261	register at_ddp_t	*ddp = (at_ddp_t *)gbuf_rptr(mp);
262				/* NOTE: there is an assumption here that the
263				 * DATA follows the header. */
264	register at_rtmp	*rtmp = (at_rtmp *)ddp->data;
265
266	if (gbuf_type(mp) != MSG_DATA) {
267		/* If this is a M_ERROR message, DDP is shutting down,
268		 * nothing to do here...If it's something else, we don't
269		 * understand what it is
270		 */
271		gbuf_freem(mp);
272		return;
273	}
274
275	if (!ifID) {
276		gbuf_freem(mp);
277		return;
278	}
279	if (gbuf_len(mp) < (DDP_X_HDR_SIZE + sizeof(at_rtmp))) {
280		gbuf_freem(mp);
281		return;
282	}
283	this_net = ifID->ifThisNode.s_net;
284	if (rtmp->at_rtmp_id_length  != 8) {
285		gbuf_freem(mp);
286		return;
287	}
288
289	{
290		at_rtmp_tuple *tp;
291		tp = ((at_rtmp_tuple *)&rtmp->at_rtmp_id[1]);
292		range_start = NET_VALUE(tp->at_rtmp_net);
293		tp = ((at_rtmp_tuple *)&rtmp->at_rtmp_id[4]);
294		range_end = NET_VALUE(tp->at_rtmp_net);
295
296		if (ifID->ifRouterState == ROUTER_AROUND) {
297			if ((ifID->ifThisCableStart == range_start) &&
298			    (ifID->ifThisCableEnd == range_end)) {
299				trackrouter(ifID,
300					    NET_VALUE(rtmp->at_rtmp_this_net),
301					    rtmp->at_rtmp_id[0]
302					    );
303			}
304		} else {
305			/* There was no router around earlier, one
306			 * probably just came up.
307			 */
308			if ((this_net >= DDP_STARTUP_LOW) &&
309			    (this_net <= DDP_STARTUP_HIGH)) {
310				/* we're operating in the startup range,
311				 * ignore the presence of router
312				 */
313				if (ifID->ifRouterState == NO_ROUTER) {
314					dPrintf(D_M_RTMP, D_L_INFO, ("rtmp_input: new router came up, INVALID: net \
315						in startup range.\n"));
316					/* trackrouter sends a KEV_ATALK_ROUTERUP event to note that
317						a new router has come up when we had none before. */
318					trackrouter(ifID,
319						    NET_VALUE(rtmp->at_rtmp_this_net),
320						    rtmp->at_rtmp_id[0]
321						    );
322					ifID->ifRouterState = ROUTER_WARNING;
323
324					/* This router is invalid. Send event. */
325					atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ROUTERUP_INVALID, 0, 0);
326				}
327			} else {
328				/* our address
329				 * is not in startup range; Is our
330				 * address good for the cable??
331				 */
332				if ((this_net >= range_start) &&
333				    (this_net <= range_end)) {
334					/* Our address is in the range
335					 * valid for this cable... Note
336					 * the router address and then
337					 * get ZIP rolling to get the
338					 * zone info.
339					 */
340					ifID->ifThisCableStart = range_start;
341					ifID->ifThisCableEnd = range_end;
342
343					/* A seed router that gives us back our cable range came up.
344						It's a valid router and gives us our network back. */
345					atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ROUTERUP, 0, 0);
346
347					trackrouter(ifID,
348						    NET_VALUE(rtmp->at_rtmp_this_net),
349						    rtmp->at_rtmp_id[0]
350						    );
351					zip_control(ifID, ZIP_LATE_ROUTER);
352				} else {
353					/* Our address is not in the
354					 * range valid for this cable..
355					 * ignore presence of the
356					 * router
357					 */
358					if (ifID->ifRouterState == NO_ROUTER) {
359						/* trackrouter sends a KEV_ATALK_ROUTERUP event to note that
360							a new router has come up when we had none before. */
361						trackrouter(ifID,
362							    NET_VALUE(rtmp->at_rtmp_this_net),
363							    rtmp->at_rtmp_id[0]
364							    );
365						ifID->ifRouterState = ROUTER_WARNING;
366
367						/* A new seed router came up, but the cable range is different
368							than what we had before. */
369						atalk_post_msg(ifID->aa_ifp, KEV_ATALK_ROUTERUP_INVALID, 0, 0);
370					}
371				}
372			}
373		}
374	}
375
376	gbuf_freem(mp);
377	return;
378}
379
380
381void rtmp_init()
382{
383  bzero((caddr_t)trackedrouters, sizeof(struct routerinfo)*NROUTERS2TRAK);
384}
385
386
387