sap_tables.c revision 27127
1/*
2 * Copyright (c) 1995 John Hay.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. All advertising materials mentioning features or use of this software
13 *    must display the following acknowledgement:
14 *	This product includes software developed by John Hay.
15 * 4. Neither the name of the author nor the names of any co-contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY John Hay AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	$Id: sap_tables.c,v 1.3 1997/02/22 16:01:01 peter Exp $
32 */
33
34#include "defs.h"
35#include <string.h>
36#include <stdlib.h>
37
38#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
39
40sap_hash sap_head[SAPHASHSIZ];
41
42void
43sapinit(void)
44{
45	int i;
46
47	for (i=0; i<SAPHASHSIZ; i++)
48		sap_head[i].forw = sap_head[i].back =
49					(struct sap_entry *)&sap_head[i];
50}
51
52/*
53 * XXX Make sure that this hash is good enough.
54 *
55 * This hash use the first 8 letters of the ServName and the ServType
56 * to create a 32 bit hash value.
57 *
58 * NOTE: The first two letters of ServName will be used to generate
59 * the lower bits of the hash. This is used to index into the hash table.
60 */
61#define rol(x)		(((x * 2) & 0xFFFF) + ((x & 0x8000) != 0))
62
63int
64saphash(u_short ServType, char *ServName)
65{
66	int hsh, i;
67	char name[SERVNAMELEN];
68
69	bzero(name, SERVNAMELEN);
70	strncpy(name, ServName, SERVNAMELEN);
71	ServName = name;
72
73	hsh = 0;
74
75	for (i=0;i<8;i++) {
76		hsh = rol(hsh) + *ServName;
77		ServName++;
78	}
79
80	hsh = rol(hsh) + (ServType >> 8);
81	hsh = rol(hsh) + (ServType & 0xff);
82	hsh = (hsh >> 7) ^ hsh;
83	return hsh;
84}
85
86/*
87 * Look for an exact match on ServType and ServName. It is
88 * mostly used by the function that process SAP RESPONSE packets.
89 *
90 * A hash is created and used to index into the hash table. Then
91 * that list is walk through searching for a match.
92 *
93 * If no match is found NULL is returned.
94 */
95struct sap_entry *
96sap_lookup(u_short ServType, char *ServName)
97{
98	register struct sap_entry *sap;
99	register struct sap_hash  *sh;
100	int hsh;
101
102	hsh = saphash(ServType, ServName);
103	sh = &sap_head[hsh & SAPHASHMASK];
104
105	for(sap = sh->forw; sap != (sap_entry *)sh; sap = sap->forw) {
106		if ((hsh == sap->hash) &&
107		    (ServType == sap->sap.ServType) &&
108		    (strncmp(ServName, sap->sap.ServName, SERVNAMELEN) == 0)) {
109			return sap;
110		}
111	}
112	return NULL;
113}
114
115/*
116 * This returns the nearest service of the specified type. If no
117 * suitable service is found or if that service is on the interface
118 * where the request came from, NULL is returned.
119 *
120 * When checking interfaces clones must be considered also.
121 *
122 * XXX TODO:
123 * Maybe we can use RIP tables to get the fastest service (ticks).
124 */
125struct sap_entry *
126sap_nearestserver(ushort ServType, struct interface *ifp)
127{
128	register struct sap_entry *sap;
129	register struct sap_entry *csap;
130	struct sap_hash  *sh;
131	register struct sap_entry *best = NULL;
132	register int besthops = HOPCNT_INFINITY;
133
134	sh = sap_head;
135
136	for (; sh < &sap_head[SAPHASHSIZ]; sh++)
137		for(sap = sh->forw; sap != (sap_entry *)sh; sap = sap->forw) {
138			if (ServType != sap->sap.ServType)
139				continue;
140			if (ifp == sap->ifp)
141				continue;
142
143			csap = sap->clone;
144			while (csap) {
145				if (ifp == csap->ifp)
146					/*
147					 * I would have loved to use
148					 * something else here.
149					 */
150					goto next;
151				csap = csap->clone;
152			}
153
154			if (ntohs(sap->sap.hops) < besthops) {
155				best = sap;
156				besthops = ntohs(best->sap.hops);
157			}
158next:;
159		}
160	return best;
161}
162
163/*
164 * Add a entry to the SAP table.
165 *
166 * If the malloc fail, the entry will silently be thrown away.
167 */
168void
169sap_add(struct sap_info *si, struct sockaddr *from)
170{
171	register struct sap_entry *nsap;
172	register struct sap_hash *sh;
173
174	FIXLEN(from);
175	nsap = malloc(sizeof(struct sap_entry));
176	if (nsap == NULL)
177		return;
178
179	nsap->sap = *si;
180	nsap->source = *from;
181	nsap->clone = NULL;
182	nsap->ifp = if_ifwithnet(from);
183	nsap->state = RTS_CHANGED;
184	nsap->timer = 0;
185	nsap->hash = saphash(si->ServType, si->ServName);
186
187	sh = &sap_head[nsap->hash & SAPHASHMASK];
188
189	insque(nsap, sh);
190}
191
192/*
193 * Change an existing SAP entry. If a clone exist for the old one,
194 * check if it is cheaper. If it is change tothe clone, otherwise
195 * delete all the clones.
196 */
197void
198sap_change(struct sap_entry *sap,
199           struct sap_info *si,
200           struct sockaddr *from)
201{
202	struct sap_entry *osap = NULL;
203
204	FIXLEN(from);
205	/*
206	 * If the hopcount (metric) is HOPCNT_INFINITY (16) it means that
207	 * a service has gone down. We should keep it like that for 30
208	 * seconds, so that it will get broadcast and then change to a
209	 * clone if one exist.
210	 */
211	if (sap->clone && (ntohs(si->hops) != HOPCNT_INFINITY)) {
212		/*
213		 * There are three possibilities:
214		 * 1. The new path is cheaper than the old one.
215		 *      Free all the clones.
216		 *
217		 * 2. The new path is the same cost as the old ones.
218		 *      If it is on the list of clones remove it
219		 *      from the clone list and free it.
220		 *
221		 * 3. The new path is more expensive than the old one.
222		 *      Use the values of the first clone and take it
223		 *      out of the list, to be freed at the end.
224		 */
225		osap = sap->clone;
226		if (ntohs(osap->sap.hops) > ntohs(si->hops)) {
227			struct sap_entry *nsap;
228
229			while (osap) {
230				nsap = osap->clone;
231				free(osap);
232				osap = nsap;
233			}
234			sap->clone = NULL;
235		} else if (ntohs(osap->sap.hops) == ntohs(si->hops)) {
236			struct sap_entry *psap;
237
238			psap = sap;
239			while (osap) {
240				if (equal(&osap->source, from)) {
241					psap->clone = osap->clone;
242					free(osap);
243					osap = psap->clone;
244				} else {
245					psap = osap;
246					osap = osap->clone;
247				}
248			}
249		} else {
250		from = &osap->source;
251		si = &osap->sap;
252		sap->clone = osap->clone;
253		}
254	}
255	sap->sap = *si;
256	sap->source = *from;
257	sap->ifp = if_ifwithnet(from);
258	sap->state = RTS_CHANGED;
259	if (ntohs(si->hops) == HOPCNT_INFINITY)
260		sap->timer = EXPIRE_TIME;
261	else
262		sap->timer = 0;
263
264	if (osap)
265		free(osap);
266}
267
268/*
269 * Add a clone to the specified SAP entry. A clone is a different
270 * route to the same service. We must know about them when we use
271 * the split horizon algorithm.
272 *
273 * If the malloc fail, the entry will silently be thrown away.
274 */
275void
276sap_add_clone(struct sap_entry *sap,
277	      struct sap_info *clone,
278	      struct sockaddr *from)
279{
280	register struct sap_entry *nsap;
281	register struct sap_entry *csap;
282
283	FIXLEN(from);
284	nsap = malloc(sizeof(struct sap_entry));
285	if (nsap == NULL)
286		return;
287
288	if (ftrace)
289		fprintf(ftrace, "CLONE ADD %04.4X %s.\n",
290			ntohs(clone->ServType),
291			clone->ServName);
292
293	nsap->sap = *clone;
294	nsap->source = *from;
295	nsap->clone = NULL;
296	nsap->ifp = if_ifwithnet(from);
297	nsap->state = RTS_CHANGED;
298	nsap->timer = 0;
299	nsap->hash = saphash(clone->ServType, clone->ServName);
300
301	csap = sap;
302	while (csap->clone)
303		csap = csap->clone;
304	csap->clone = nsap;
305}
306
307/*
308 * Remove a SAP entry from the table and free the memory
309 * used by it.
310 *
311 * If the service have clone, do a sap_change to it and free
312 * the clone.
313 */
314void
315sap_delete(struct sap_entry *sap)
316{
317	if (sap->clone) {
318		sap_change(sap, &sap->clone->sap, &sap->clone->source);
319		return;
320	}
321	remque(sap);
322	free(sap);
323}
324