322 lines
8.0 KiB
C
322 lines
8.0 KiB
C
/*
|
|
* Copyright (c) 1995 John Hay. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by John Hay.
|
|
* 4. Neither the name of the author nor the names of any co-contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY John Hay AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include <search.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
|
|
|
|
sap_hash sap_head[SAPHASHSIZ];
|
|
|
|
void
|
|
sapinit(void)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<SAPHASHSIZ; i++)
|
|
sap_head[i].forw = sap_head[i].back =
|
|
(struct sap_entry *)&sap_head[i];
|
|
}
|
|
|
|
/*
|
|
* This hash use the first 14 letters of the ServName and the ServType
|
|
* to create a 32 bit hash value.
|
|
*/
|
|
int
|
|
saphash(u_short ServType, char *ServName)
|
|
{
|
|
int hsh, i;
|
|
char name[SERVNAMELEN];
|
|
|
|
bzero(name, SERVNAMELEN);
|
|
strncpy(name, ServName, SERVNAMELEN);
|
|
ServName = name;
|
|
|
|
hsh = 0;
|
|
|
|
#define SMVAL 33
|
|
|
|
hsh = hsh * SMVAL + (ServType & 0xff);
|
|
hsh = hsh * SMVAL + (ServType >> 8);
|
|
|
|
for (i=0;i<14;i++) {
|
|
hsh = hsh * SMVAL + *ServName++;
|
|
ServName++;
|
|
}
|
|
|
|
#undef SMVAL
|
|
|
|
return hsh;
|
|
}
|
|
|
|
/*
|
|
* Look for an exact match on ServType and ServName. It is
|
|
* mostly used by the function that process SAP RESPONSE packets.
|
|
*
|
|
* A hash is created and used to index into the hash table. Then
|
|
* that list is walk through searching for a match.
|
|
*
|
|
* If no match is found NULL is returned.
|
|
*/
|
|
struct sap_entry *
|
|
sap_lookup(u_short ServType, char *ServName)
|
|
{
|
|
register struct sap_entry *sap;
|
|
register struct sap_hash *sh;
|
|
int hsh;
|
|
|
|
hsh = saphash(ServType, ServName);
|
|
sh = &sap_head[hsh & SAPHASHMASK];
|
|
|
|
for(sap = sh->forw; sap != (sap_entry *)sh; sap = sap->forw) {
|
|
if ((hsh == sap->hash) &&
|
|
(ServType == sap->sap.ServType) &&
|
|
(strncmp(ServName, sap->sap.ServName, SERVNAMELEN) == 0)) {
|
|
return sap;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* This returns the nearest service of the specified type. If no
|
|
* suitable service is found or if that service is on the interface
|
|
* where the request came from, NULL is returned.
|
|
*
|
|
* When checking interfaces clones must be considered also.
|
|
*
|
|
* XXX TODO:
|
|
* Maybe we can use RIP tables to get the fastest service (ticks).
|
|
*/
|
|
struct sap_entry *
|
|
sap_nearestserver(ushort ServType, struct interface *ifp)
|
|
{
|
|
register struct sap_entry *sap;
|
|
struct sap_hash *sh;
|
|
register struct sap_entry *best = NULL;
|
|
register int besthops = HOPCNT_INFINITY;
|
|
|
|
sh = sap_head;
|
|
|
|
for (; sh < &sap_head[SAPHASHSIZ]; sh++)
|
|
for(sap = sh->forw; sap != (sap_entry *)sh; sap = sap->forw) {
|
|
if (ServType != sap->sap.ServType)
|
|
continue;
|
|
|
|
if (ntohs(sap->sap.hops) < besthops) {
|
|
best = sap;
|
|
besthops = ntohs(best->sap.hops);
|
|
}
|
|
}
|
|
return best;
|
|
}
|
|
|
|
/*
|
|
* Add an entry to the SAP table.
|
|
*
|
|
* If the malloc fail, the entry will silently be thrown away.
|
|
*/
|
|
void
|
|
sap_add(struct sap_info *si, struct sockaddr *from)
|
|
{
|
|
register struct sap_entry *nsap;
|
|
register struct sap_hash *sh;
|
|
|
|
if (ntohs(si->hops) == HOPCNT_INFINITY)
|
|
return;
|
|
|
|
FIXLEN(from);
|
|
nsap = malloc(sizeof(struct sap_entry));
|
|
if (nsap == NULL)
|
|
return;
|
|
|
|
nsap->sap = *si;
|
|
nsap->source = *from;
|
|
nsap->clone = NULL;
|
|
nsap->ifp = if_ifwithnet(from);
|
|
nsap->state = RTS_CHANGED;
|
|
nsap->timer = 0;
|
|
nsap->hash = saphash(si->ServType, si->ServName);
|
|
|
|
sh = &sap_head[nsap->hash & SAPHASHMASK];
|
|
|
|
insque(nsap, sh);
|
|
TRACE_SAP_ACTION("ADD", nsap);
|
|
}
|
|
|
|
/*
|
|
* Change an existing SAP entry. If a clone exist for the old one,
|
|
* check if it is cheaper. If it is change to the clone, otherwise
|
|
* delete all the clones.
|
|
*/
|
|
void
|
|
sap_change(struct sap_entry *sap,
|
|
struct sap_info *si,
|
|
struct sockaddr *from)
|
|
{
|
|
struct sap_entry *osap = NULL;
|
|
|
|
FIXLEN(from);
|
|
TRACE_SAP_ACTION("CHANGE FROM", sap);
|
|
/*
|
|
* If the hopcount (metric) is HOPCNT_INFINITY (16) it means that
|
|
* a service has gone down. We should keep it like that for 30
|
|
* seconds, so that it will get broadcast and then change to a
|
|
* clone if one exist.
|
|
*/
|
|
if (sap->clone && (ntohs(si->hops) != HOPCNT_INFINITY)) {
|
|
/*
|
|
* There are three possibilities:
|
|
* 1. The new path is cheaper than the old one.
|
|
* Free all the clones.
|
|
*
|
|
* 2. The new path is the same cost as the old ones.
|
|
* If it is on the list of clones remove it
|
|
* from the clone list and free it.
|
|
*
|
|
* 3. The new path is more expensive than the old one.
|
|
* Use the values of the first clone and take it
|
|
* out of the list, to be freed at the end.
|
|
*/
|
|
osap = sap->clone;
|
|
if (ntohs(osap->sap.hops) > ntohs(si->hops)) {
|
|
struct sap_entry *nsap;
|
|
|
|
while (osap) {
|
|
nsap = osap->clone;
|
|
TRACE_SAP_ACTION("DELETE", osap);
|
|
free(osap);
|
|
osap = nsap;
|
|
}
|
|
sap->clone = NULL;
|
|
} else if (ntohs(osap->sap.hops) == ntohs(si->hops)) {
|
|
struct sap_entry *psap;
|
|
|
|
psap = sap;
|
|
while (osap) {
|
|
if (equal(&osap->source, from)) {
|
|
psap->clone = osap->clone;
|
|
TRACE_SAP_ACTION("DELETE", osap);
|
|
free(osap);
|
|
osap = psap->clone;
|
|
} else {
|
|
psap = osap;
|
|
osap = osap->clone;
|
|
}
|
|
}
|
|
} else {
|
|
from = &osap->source;
|
|
si = &osap->sap;
|
|
sap->clone = osap->clone;
|
|
}
|
|
}
|
|
sap->sap = *si;
|
|
sap->source = *from;
|
|
sap->ifp = if_ifwithnet(from);
|
|
sap->state = RTS_CHANGED;
|
|
if (ntohs(si->hops) == HOPCNT_INFINITY)
|
|
sap->timer = EXPIRE_TIME;
|
|
else
|
|
sap->timer = 0;
|
|
|
|
if (osap) {
|
|
TRACE_SAP_ACTION("DELETE", osap);
|
|
free(osap);
|
|
}
|
|
TRACE_SAP_ACTION("CHANGE TO", sap);
|
|
}
|
|
|
|
/*
|
|
* Add a clone to the specified SAP entry. A clone is a different
|
|
* route to the same service. We must know about them when we use
|
|
* the split horizon algorithm.
|
|
*
|
|
* If the malloc fail, the entry will silently be thrown away.
|
|
*/
|
|
void
|
|
sap_add_clone(struct sap_entry *sap,
|
|
struct sap_info *clone,
|
|
struct sockaddr *from)
|
|
{
|
|
register struct sap_entry *nsap;
|
|
register struct sap_entry *csap;
|
|
|
|
if (ntohs(clone->hops) == HOPCNT_INFINITY)
|
|
return;
|
|
|
|
FIXLEN(from);
|
|
nsap = malloc(sizeof(struct sap_entry));
|
|
if (nsap == NULL)
|
|
return;
|
|
|
|
if (ftrace)
|
|
fprintf(ftrace, "CLONE ADD %4.4X %s.\n",
|
|
ntohs(clone->ServType),
|
|
clone->ServName);
|
|
|
|
nsap->sap = *clone;
|
|
nsap->source = *from;
|
|
nsap->clone = NULL;
|
|
nsap->ifp = if_ifwithnet(from);
|
|
nsap->state = RTS_CHANGED;
|
|
nsap->timer = 0;
|
|
nsap->hash = saphash(clone->ServType, clone->ServName);
|
|
|
|
csap = sap;
|
|
while (csap->clone)
|
|
csap = csap->clone;
|
|
csap->clone = nsap;
|
|
TRACE_SAP_ACTION("ADD CLONE", nsap);
|
|
}
|
|
|
|
/*
|
|
* Remove a SAP entry from the table and free the memory
|
|
* used by it.
|
|
*
|
|
* If the service have clone, do a sap_change to it and free
|
|
* the clone.
|
|
*/
|
|
void
|
|
sap_delete(struct sap_entry *sap)
|
|
{
|
|
if (sap->clone) {
|
|
sap_change(sap, &sap->clone->sap, &sap->clone->source);
|
|
return;
|
|
}
|
|
remque(sap);
|
|
TRACE_SAP_ACTION("DELETE", sap);
|
|
free(sap);
|
|
}
|