CAP/etc/atis.c

1252 lines
30 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

static char rcsid[] = "$Author: djh $ $Date: 1996/06/18 10:51:30 $";
static char rcsident[] = "$Header: /mac/src/cap60/etc/RCS/atis.c,v 2.11 1996/06/18 10:51:30 djh Rel djh $";
static char revision[] = "$Revision: 2.11 $";
/*
* atis.c - a simple appletalk information server
*
* This provides a simple name information and echo server.
* The NBP it assumes is a slightly extended form (cf. nbp.ext).
*
* Also acts as the RTMP listener for use with Native EtherTalk and
* Kernel EtherTalk for which we also maintain a simple routing table.
*
* Needs some cleaning. A quit signal causes it to dump it's database.
* a HUP signal tells it to reload it.
*
* AppleTalk package for UNIX (4.2 BSD).
*
* Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University
* in the City of New York.
*
*
* Edit History:
*
* July 10, 1986 CCKim Created
* August 2, 1986 CCKim Add dump and load functionality
* December 17, 1986 CCKim Revise to rev1086 of UDP code
* April 28, 1991 djh Add Phase 2 support
*
*/
#include <stdio.h>
#include <signal.h>
#include <sys/param.h>
#ifndef _TYPES
# include <sys/types.h>
#endif
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netat/appletalk.h>
#include <netat/abnbp.h>
#include <netat/compat.h>
#ifdef USETIMES
# include <time.h>
#endif
#ifdef USEVPRINTF
# include <varargs.h>
#endif
#ifdef linux
#define SIGEMT SIGUNUSED
#endif /* linux */
/* signals in use */
#define DUMPSIG sigmask(SIGQUIT)
#define EXITSIG sigmask(SIGTERM)
#define DBUGSIG sigmask(SIGIOT)
#define NDBGSIG sigmask(SIGEMT)
#define LOADSIG sigmask(SIGHUP)
#ifdef NOSIGMASK
static int allsigs[] = { SIGIOT, SIGHUP, SIGTERM, SIGEMT, SIGQUIT, 0 };
#else NOSIGMASK
#define ALLSIGS (sigmask(SIGIOT)|sigmask(SIGHUP)|sigmask(SIGTERM)|\
sigmask(SIGEMT)|sigmask(SIGQUIT))
#endif NOSIGMASK
/* logging flags */
#define L_UERR 0x20 /* want unix error message */
#define L_EXIT 0x10 /* exit after logging */
#define L_LVL 0xf /* debug levels */
#define L_LVLMAX 15 /* maximum level */
/* good place to stick the copyright so it shows up in object files */
private char Columbia_Copyright[] = "Copyright (c) 1986,1987,1988 by The Trustees of Columbia University in the City of New York";
private int tempdebugfile = 0;
private int nbpskt = nbpNIS;
private int echoskt = echoSkt;
private int rtmpskt = rtmpSkt;
char *pidfile;
extern short lap_proto; /* identifies the "LAP" level */
extern u_char bridge_node;
extern u_short this_net, nis_net, bridge_net;
#ifdef PHASE2
extern u_short net_range_start, net_range_end;
#endif PHASE2
#ifndef ETCDIR
# define ETCDIR "/etc"
#endif
#ifndef NISDUMPFILE
# define NISDUMPFILE "/usr/tmp/nis.db"
#endif
#ifndef ATISRUNFILE
# define ATISRUNFILE "/usr/tmp/atis.run"
#endif
private char *nisdumpfile = NISDUMPFILE;
private char *atisrunfile = ATISRUNFILE;
private char *progname;
import DBUG dbug;
/* each tuple is entity name + addrblock + enumerator */
#define NBPTUPSIZE (sizeof(EntityName)+sizeof(AddrBlock)+1)
/* for nbp, must offset for control word and nbp id */
#define NUMREPLYMAX ((ddpMaxData-2)/NBPTUPSIZE)
NBPTEntry reply[NUMREPLYMAX];
extern int get_debug_level();
extern int set_debug_level();
void nbp_extensions();
void nbp_reload();
void nbp_dump();
void nbp_listener();
void echo_listener();
void rtmp_listener();
void atis_end();
void atis_debuginc();
void atis_undebug();
private int nbpcpy(), c2pkt_ename(), pkt2c_ename();
void
nbp_reload()
{
FILE *fd;
int cnt;
int mask;
#ifdef NOSIGMASK
sighold_all();
#else NOSIGMASK
mask = sigblock(ALLSIGS); /* block so we don't get interrupted */
#endif NOSIGMASK
(void)logit(1, "reloading from %s",nisdumpfile);
nbptab_init(); /* reset tables */
cnt = 0;
if ((fd = fopen(nisdumpfile,"r")) != NULL) {
cnt = nbptab_load(fd);
(void)fclose(fd);
} else logit(L_UERR|1, "dump file open failed");
logit(1, "loaded %d entries",cnt);
#ifdef NOSIGMASK
sigrelse_all();
#else NOSIGMASK
sigsetmask(mask);
#endif NOSIGMASK
(void)signal(SIGHUP, nbp_reload);
}
void
nbp_dump()
{
FILE *fd;
int cnt;
int mask;
#ifdef NOSIGMASK
sighold_all();
#else NOSIGMASK
mask = sigblock(ALLSIGS); /* block so we don't get interrupted */
#endif NOSIGMASK
logit(1, "Dumping to %s",nisdumpfile);
(void)signal(SIGQUIT, SIG_IGN);
cnt = 0;
if ((fd = fopen(nisdumpfile,"w+")) != NULL) {
(void)chmod(nisdumpfile, 0774);
cnt = nbptab_dump(fd);
(void)fclose(fd);
} else logit(L_UERR|1, "dump file (write) open failed");
logit(1, "Dumped %d entries",cnt);
#ifdef NOSIGMASK
sigrelse_all();
#else NOSIGMASK
sigsetmask(mask);
#endif NOSIGMASK
(void)signal(SIGQUIT, nbp_dump);
}
void
atis_end()
{
#ifdef linux
if (lap_proto == LAP_KERNEL) {
void rtmp_release();
rtmp_release();
}
#endif /* linux */
logit(0, "exiting");
(void)unlink(pidfile);
exit(1);
}
void
atis_undebug()
{
#ifdef NOSIGMASK
sighold(DBUGSIG);
sighold(NDBGSIG);
#else NOSIGMASK
int mask = sigblock(DBUGSIG|NDBGSIG);
#endif NOSIGMASK
set_debug_level(0);
logit(0, "DEBUGGING OFF");
if (tempdebugfile)
nologitfile();
signal(SIGEMT, atis_undebug);
#ifdef NOSIGMASK
sigrelse(DBUGSIG);
sigrelse(NDBGSIG);
#else NOSIGMASK
sigsetmask(mask);
#endif NOSIGMASK
}
void
atis_debuginc()
{
int dlevel;
#ifdef NOSIGMASK
sighold(DBUGSIG);
sighold(NDBGSIG);
#else NOSIGMASK
int mask = sigblock(DBUGSIG|NDBGSIG);
#endif NOSIGMASK
if (!islogitfile()) {
tempdebugfile++;
logitfileis(atisrunfile, "w+");
}
if ((dlevel = get_debug_level()) < L_LVLMAX)
set_debug_level(++dlevel);
logit(0, "DEBUG LEVEL %d", dlevel);
signal(SIGIOT, atis_debuginc);
#ifdef NOSIGMASK
sigrelse(DBUGSIG);
sigrelse(NDBGSIG);
#else NOSIGMASK
sigsetmask(mask);
#endif NOSIGMASK
}
usage()
{
fprintf(stderr,"usage: atis -l logfile -Dlevel -d<args> -E -N -R [reload]\n");
fprintf(stderr," [dump] [exit] [debug] [nodebug]\n");
exit(1);
}
setuppidfilename()
{
int el;
char *p;
el = strlen(ETCDIR);
pidfile = (char *)malloc(el+sizeof("atis.pid")+5);
if (pidfile == NULL) {
fprintf(stderr,"Can't allocate memory for pid file");
exit(999);
}
strcpy(pidfile, ETCDIR);
if (el > 0) {
p = pidfile+el - 1; /* point to last char */
while (el-- && *p == '/') /* strip trailing slashes */
*p-- = '\0';
p++; /* make sure at end of string */
}
strcpy(p, "/atis.pid");
#ifdef DEBUG
printf("pid file name = %s\n", pidfile);
#endif
}
setatispid()
{
FILE *fd;
if ((fd = fopen(pidfile, "w")) != NULL) {
fprintf(fd, "%d\n",getpid());
(void)fclose(fd);
}
}
getatispid()
{
FILE *fp;
int pid;
if ((fp = fopen(pidfile, "r")) == NULL) {
logit(L_UERR|0, "No pid file - maybe the daemon wasn't running?");
return(-1);
}
if (fscanf(fp, "%d\n", &pid) != 1) {
logit(0, "pid file was bad");
return(-1);
}
return(pid);
}
#define NSIGACT 5
struct sigtab {
char *s_name;
int s_signal;
char *s_action;
} sigtab[NSIGACT] = {
"reload", SIGHUP, "reload nis database",
"dump", SIGQUIT, "dump nis database",
"debug", SIGIOT, "increment debug level",
"nodebug", SIGEMT, "no debug level",
"exit", SIGTERM, "stop running atis"
};
handlesigact(s, pid)
char *s;
{
int i;
struct sigtab *st;
pid = getatispid();
for (st = sigtab, i = 0; i < NSIGACT; i++, st++) {
if (strcmp(s, st->s_name) != 0)
continue;
if (kill(pid, st->s_signal) < 0)
logit(0, "Couldn't send %s signal to daemon[%d] - is it running?",
st->s_action, pid);
else
logit(0, "Sent %s signal to daemon[%d]",st->s_action,pid);
return(0);
}
return(-1);
}
doargs(argc, argv)
int argc;
char **argv;
{
int pid;
int c, dlevel;
extern char *optarg;
extern int optind;
extern boolean dochecksum;
while ((c = getopt(argc, argv, "kENRD:d:l:")) != EOF) {
switch (c) {
case 'k':
dochecksum = 0;
break;
case 'N':
nbpskt = 0;
logit(0, "no nis server will be established");
break;
case 'E':
echoskt = 0;
logit(0, "no echo listener will be established");
break;
case 'R':
rtmpskt = 0;
logit(0, "no rtmp listener will be established");
break;
case 'D':
dlevel = atoi(optarg);
if (dlevel > L_LVLMAX)
dlevel = L_LVLMAX;
set_debug_level(dlevel);
break;
case 'd':
dbugarg(optarg);
set_debug_level(1);
break;
case 'l':
logitfileis(optarg, "w");
break;
}
}
if (optind == argc)
return;
if ((pid = getatispid()) < 0) {
logit(L_EXIT|0, "Couldn't get pid of daemon - is it running?");
}
for (; optind < argc; optind++)
if (handlesigact(argv[optind], pid) < 0)
usage();
exit(0);
}
disassociate()
{
int i;
/* disassociate */
if (fork())
_exit(0); /* kill parent */
for (i=0; i < 3; i++) close(i); /* kill */
(void)open("/",0);
#ifdef NODUP2
(void)dup(0); /* slot 1 */
(void)dup(0); /* slot 2 */
#else NODUP2
(void)dup2(0,1);
(void)dup2(0,2);
#endif NODUP2
#ifndef POSIX
#ifdef TIOCNOTTY
if ((i = open("/dev/tty",2)) > 0) {
(void)ioctl(i, TIOCNOTTY, (caddr_t)0);
(void)close(i);
}
#endif TIOCNOTTY
#ifdef xenix5
/*
* USG process groups:
* The fork guarantees that the child is not a process group leader.
* Then setpgrp() can work, whick loses the controllong tty.
* Note that we must be careful not to be the first to open any tty,
* or it will become our controlling tty. C'est la vie.
*/
setpgrp();
#endif xenix5
#else POSIX
(void) setsid();
#endif POSIX
}
main(argc, argv)
int argc;
char **argv;
{
int nbperr = noErr;
int echoerr = noErr;
int rtmperr = noErr;
set_debug_level(0);
setuppidfilename();
progname = argv[0];
doargs(argc, argv);
abInit(TRUE); /* init driver */
nbpInit(); /* init NBP */
nbptab_init();
(void)signal(SIGHUP, nbp_reload);
(void)signal(SIGQUIT, nbp_dump);
(void)signal(SIGTERM, atis_end);
(void)signal(SIGIOT, atis_debuginc);
(void)signal(SIGEMT, atis_undebug);
logit(0, "Reply num max for lkup reply is %d (based on %d)",
NUMREPLYMAX, NBPTUPSIZE);
if (nbpskt) {
if ((nbperr = DDPOpenSocket(&nbpskt, nbp_listener)) != noErr)
logit(L_UERR|0, "NIS: NIS socket not available: error %d", nbperr);
}
if (echoskt) {
if ((echoerr = DDPOpenSocket(&echoskt, echo_listener)) != noErr)
logit(L_UERR|0, "ECHO: ECHO socket not available: error %d", echoerr);
}
if ((lap_proto == LAP_ETALK || lap_proto == LAP_KERNEL) && rtmpskt) {
if ((rtmperr = DDPOpenSocket(&rtmpskt, rtmp_listener)) != noErr)
logit(L_UERR|0, "RTMP: RTMP socket not available: error %d", rtmperr);
#ifdef linux
if (lap_proto == LAP_KERNEL) {
extern void rtmp_timer();
Timeout(rtmp_timer, 0, 40);
}
#endif /* linux */
}
if (echoerr != noErr && nbperr != noErr && rtmperr != noErr)
logit(0|L_EXIT,"Couldn't establish nis, rtmp or echo socket, nothing to do");
if (!dbug.db_flgs && (get_debug_level() == 0))
disassociate();
/* store pid where people can see it */
setatispid();
logit(0,"pid = %d",getpid());
/* sleep for a day (but wake up on events) */
for (;;)
abSleep(sectotick(60*60*24), TRUE);
}
/*
* This is the NBP NIS listener
*
*/
void
nbp_listener(skt, type, nbp, len, addr)
u_char skt;
u_char type;
NBP *nbp;
int len;
AddrBlock *addr;
{
int cnt;
NBPTEntry looks;
if (len < nbpMinSize || type != ddpNBP) {
logit(3, "Packet too small or bad packet type");
return;
}
/* technically, we should check to see if it is ourselves and not */
/* respond if so, but it is convient to do so and is part of the */
/* nbp extensions */
switch (nbp->control) {
default:
case nbpLkUpReply:
logit(3, "Dropping nbp of type %d",nbp->control);
return; /* drop the packet */
case nbpTickle:
case nbpRegister:
case nbpDelete:
case nbpBrRq:
case nbpLkUp:
break;
}
/* at this point we know we have a lookup */
logit(2, "Got nbp %d lkup from net %d.%d, node %d, skt %d",
nbp->tcnt, ntohs(addr->net)>>8, ntohs(addr->net)&0xff,
addr->node, addr->skt);
/* should only get one entity in incoming packet */
switch (nbp->control) {
case nbpBrRq: /* treat as lkup */
case nbpLkUp:
cnt = nbpcpy(&looks, 0, 1, nbp, (int)nbp->tcnt, FALSE);
answer(&looks, &looks.addr, nbp->id);
break;
case nbpTickle:
/* tickle may be implemented in the future */
/* tickle may take list of entities in future */
break;
case nbpRegister:
case nbpDelete:
cnt = nbpcpy(&looks, 0, 1, nbp, 1, FALSE);
nbp_extensions(&looks, addr, nbp, len);
break;
}
}
/*
* reply to a NBP LkUp request
*
*/
answer(en, addr, id)
NBPTEntry *en;
AddrBlock *addr;
u_char id;
{
NBP nbp;
ABusRecord ddp;
ddpProto *ddpr;
int cnt, nsize, tsize;
NBPTuple *tp;
int i, start = 0;
int mask;
#ifdef ISO_TRANSLATE
EntityName isoEn;
void cMac2ISO();
bcopy(en->ent.objStr.s, isoEn.objStr.s, sizeof(Str32));
bcopy(en->ent.typeStr.s, isoEn.typeStr.s, sizeof(Str32));
bcopy(en->ent.zoneStr.s, isoEn.zoneStr.s, sizeof(Str32));
cMac2ISO(isoEn.objStr.s);
cMac2ISO(isoEn.typeStr.s);
cMac2ISO(isoEn.zoneStr.s);
logit(2, "Looking for entities to answer a lookup with %s:%s@%s",
isoEn.objStr.s, isoEn.typeStr.s, isoEn.zoneStr.s);
#else ISO_TRANSLATE
logit(2, "Looking for entities to answer a lookup with %s:%s@%s",
en->ent.objStr.s, en->ent.typeStr.s, en->ent.zoneStr.s);
#endif ISO_TRANSLATE
#ifdef NOSIGMASK
sighold(DUMPSIG);
sighold(LOADSIG);
#else NOSIGMASK
mask = sigblock(DUMPSIG|LOADSIG); /* prevent inconsistency */
#endif NOSIGMASK
while ((cnt = nbpt_find(&en->ent, &start, reply, NUMREPLYMAX)) > 0) {
if (cnt > 255) {
logit(3, "Dropping count to 255 from %d",cnt);
cnt = 255;
}
logit(2, "Answering lookup with %d entities",cnt);
nbp.tcnt = cnt;
nbp.control = nbpLkUpReply;
nbp.id = id;
for (i = 0, tsize = 0, tp = nbp.tuple; i < cnt; i++) {
tp->enume = (byte)reply[i].enume;
bcopy((caddr_t)&reply[i].addr, (caddr_t)&tp->addr, sizeof(AddrBlock));
nsize = c2pkt_ename(&reply[i].ent, tp->name);
nsize += sizeof(AddrBlock) + sizeof(tp->enume);
tsize += nsize;
tp = (NBPTuple *)(((char *)tp) + nsize);
}
ddpr = &ddp.proto.ddp; /* handle on DDP protocol args */
ddpr->ddpAddress = *addr;
ddpr->ddpSocket = nbpskt;
ddpr->ddpType = ddpNBP;
ddpr->ddpDataPtr = (u_char *) &nbp;
ddpr->ddpReqCount = 2+tsize; /* control + id + rest */
DDPWrite(&ddp,FALSE); /* write it out... */
}
#ifdef NOSIGMASK
sigrelse(DUMPSIG);
sigrelse(LOADSIG);
#else NOSIGMASK
sigsetmask(mask);
#endif NOSIGMASK
}
/*
* Handle the extended NBP functions
*
*/
void
nbp_extensions(nbptent, addr, nbp, len)
NBPTEntry *nbptent;
AddrBlock *addr;
NBP *nbp;
int len;
{
ABusRecord abr;
int mask;
int rc;
char objStr[33], typeStr[33], zoneStr[33];
switch (nbp->control) {
case nbpRegister:
logit(2, "Register from net %3d.%02d node %d skt %d ",
ntohs(addr->net)>>8, ntohs(addr->net)&0xff,addr->node, addr->skt);
if (get_debug_level() > 1) {
objStr[32] = typeStr[32] = zoneStr[32] = '\0'; /* tie off? */
strcpy(objStr, (char *)nbptent->ent.objStr.s);
strcpy(typeStr, (char *)nbptent->ent.typeStr.s);
strcpy(zoneStr, (char *)nbptent->ent.zoneStr.s);
logit(2, "\tfor %s:%s@%s, net %3d.%02d node %d skt %d ",
objStr, typeStr, zoneStr,
ntohs(nbptent->addr.net)>>8,
ntohs(nbptent->addr.net)&0xff, nbptent->addr.node,
nbptent->addr.skt);
}
#ifdef NOSIGMASK
sighold(DUMPSIG);
sighold(LOADSIG);
#else NOSIGMASK
mask = sigblock(DUMPSIG|LOADSIG); /* prevent inconsistency */
#endif NOSIGMASK
rc = nbptab_insert(addr, &nbptent->addr, &nbptent->ent);
#ifdef NOSIGMASK
sigrelse(DUMPSIG);
sigrelse(LOADSIG);
#else NOSIGMASK
sigsetmask(mask);
#endif NOSIGMASK
logit(2, "Register return code %d",rc);
/* return address */
bcopy((caddr_t)&nbptent->addr, (caddr_t)&abr.proto.ddp.ddpAddress,
sizeof(AddrBlock));
break;
case nbpDelete:
if (get_debug_level() > 1) {
objStr[32] = typeStr[32] = zoneStr[32] = '\0'; /* tie off? */
strcpy(objStr, (char *)nbptent->ent.objStr.s);
strcpy(typeStr, (char *)nbptent->ent.typeStr.s);
strcpy(zoneStr, (char *)nbptent->ent.zoneStr.s);
logit(2, "Delete %32s:%32s@%32s, net %3d.%02d node %d skt %d",
objStr, typeStr, zoneStr, ntohs(addr->net)>>8,
ntohs(addr->net)&0xff,addr->node, addr->skt);
}
#ifdef NOSIGMASK
sighold(DUMPSIG);
sighold(LOADSIG);
#else NOSIGMASK
mask = sigblock(DUMPSIG|LOADSIG); /* prevent inconsistency */
#endif NOSIGMASK
rc = nbptab_delete(addr, &nbptent->ent) ;
#ifdef NOSIGMASK
sigrelse(DUMPSIG);
sigrelse(LOADSIG);
#else NOSIGMASK
sigsetmask(mask);
#endif NOSIGMASK
logit(2, "Delete returns code %d",rc);
abr.proto.ddp.ddpAddress = *addr; /* return address */
break;
case nbpTickle:
logit(2, "Got a tickle");
return;
default:
logit(2, "Unknown NBP type %d",nbp->control);
return;
}
nbp->control = nbpStatusReply;
nbp->tcnt = rc;
abr.proto.ddp.ddpSocket = nbpskt;
abr.proto.ddp.ddpType = ddpNBP;
abr.proto.ddp.ddpDataPtr = (u_char *)nbp;
abr.proto.ddp.ddpReqCount = len;
DDPWrite(&abr, FALSE);
}
/*
* This is the Echo Protocol listener
*
*/
void
echo_listener(skt, type, pkt, len, addr)
u_char skt;
u_char type;
char *pkt;
int len;
AddrBlock *addr;
{
u_char ec; /* echo command */
ABusRecord abr;
if (type != ddpECHO) {
logit(1, "Got non-echo pkt in echolistener");
return; /* drop packet */
}
ec = (u_char)*pkt;
if (ec != echoRequest) {
printf("got %u when expecting echorequest\n",ec);
return; /* drop packet */
}
*pkt = echoReply;
abr.proto.ddp.ddpAddress = *addr;
abr.proto.ddp.ddpSocket = echoskt;
abr.proto.ddp.ddpType = ddpECHO;
abr.proto.ddp.ddpDataPtr = (u_char *)pkt;
abr.proto.ddp.ddpReqCount = len;
DDPWrite(&abr, FALSE);
}
/*
* This is the RTMP listener
*
*/
void
rtmp_listener(skt, type, pkt, len, addr)
u_char skt;
u_char type;
u_char *pkt;
int len;
AddrBlock *addr;
{
u_char rc; /* rtmp command */
time_t now;
u_short net;
ABusRecord abr;
static int goodness = 0;
static AddrBlock last_addr;
static time_t last_time = 0;
static AddrBlock current_addr;
static time_t current_time = 0;
static int current_goodness = -1;
#ifdef PHASE2
u_short new_net_range_start, new_net_range_end, increment;
#endif PHASE2
if (type != ddpRTMP) {
logit(1, "Got non-rtmp pkt in rtmplistener");
return; /* drop packet */
}
net = htons((pkt[0] << 8) | pkt[1]);
logit(5, "Got RTMP pkt net %d from %d.%d",
ntohs(net), ntohs(addr->net), addr->node);
if (bridge_net == 0 && net != 0) {
addr->net = net;
SetBridgeAddress(addr);
logit(1, "Gleaned network number %d from bridge %d",ntohs(net),addr->node);
}
#ifdef PHASE2
if (ntohs(net)>=ntohs(net_range_start) && ntohs(net)<=ntohs(net_range_end)) {
#else PHASE2
if (net == addr->net) {
#endif PHASE2
/*
* Compute the goodness of this router. We prefer routers that do
* split horizon, and among them, those that have the most routes.
* The goodness is thus the number of routes, and 0 for non-split horizon.
* At Rutgers the effect is to prefer cisco (which does split horizon)
* over Kinetics, and to pick the cisco that is the most "central".
* This should tend to produce the best routes. It will probably
* work reasonably at most other places as well. We expire an old
* router after 15 sec. This allows us to miss one update, but no
* more. Apple wants us to use the most recent router we heard from,
* but that allows no selectivity at all, and also tends to lead to
* inconsistent results. Unfortunately Apple specifies that when
* several routes have the same metric, routers choose the most recently
* heard. This leads to rapidly changing entries. The result can be
* small changes in goodness. If two routers are very close, we could
* end up going between them. If this is a problem for you, redefine
* MARGIN to be 10% or more of the current_goodness.
*
* Be aware that very good routers can send a full RTMP packet followed
* by a small "overflow" RTMP packet, be careful not to overreact.
*
*/
/*
* go to beginning of routing triples
*
*/
#ifdef PHASE2
pkt += 4;
len -= 4;
if (pkt[0] == 0 && pkt[1] == 0 && pkt[2] == 0x82) { /* non-extended net */
new_net_range_start = net; /* net byte order */
new_net_range_end = net; /* net byte order */
} else if (pkt[2] & 0x80 && pkt[5] == 0x82) { /* extended network */
new_net_range_start = htons((pkt[0] << 8) | pkt[1]);
new_net_range_end = htons((pkt[3] << 8) | pkt[4]);
pkt += 3;
len -= 3;
} else {
logit(2, "RTMP: unknown format packet, dropped!");
return;
}
pkt += 3;
len -= 3;
#ifdef notdef
/* ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ */
/* we should be doing this, in case the router was down when */
/* we started up. However, doing so will proably confuse our */
/* clients too much. We should also do it to ensure that the */
/* correct zone multicast address gets enabled on the intrfc */
/* ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ */
if (net_range_start == 0x00 && net_range_end == htons(0xfffe)) {
this_net = nis_net = net_range_start = new_net_range_start;
net_range_end = new_net_range_end;
SetNetRange(net_range_start, net_range_end);
}
#endif notdef
#else PHASE2
pkt += 7;
len -= 7;
#endif PHASE2
#ifdef linux
/*
* build RTMP table for kernel
*
*/
if (lap_proto == LAP_KERNEL) {
void rtmp_data();
rtmp_data(addr->node, addr->net, pkt, len);
}
#endif /* linux */
now = time(NULL);
/*
* reset goodness if more than 2 seconds
* old or this is from a different router.
*
*/
if (now > last_time+2
|| last_addr.net != addr->net
|| last_addr.node != addr->node)
goodness = 0;
/*
* loop over routing triples, counting them.
* If we find the router network, this router
* isn't doing split horizon.
*
*/
while (len > 0) {
net = htons((pkt[0] << 8) | pkt[1]); /* pkt data is not aligned */
if (net == addr->net) { /* not doing split horizon */
logit (10, "No split horizon in router %d.%d",
ntohs(addr->net), addr->node);
goodness = 0;
}
goodness++;
#ifdef PHASE2
if (pkt[2] & 0x80 && pkt[5] == 0x82)
increment = 3;
else
increment = 0;
len -= increment;
pkt += increment;
#endif PHASE2
len -= 3;
pkt += 3;
}
logit (8, "Router %d.%d has goodness %d",
ntohs(addr->net), addr->node, goodness);
last_time = now;
last_addr.net = addr->net;
last_addr.node = addr->node;
#define MARGIN (current_goodness / 20) /* currently 5% */
/*
* this router knows more than 5% more
* routes than the current one, adopt it
* (may be subsequent packets from current)
*
*/
if (goodness > (current_goodness+MARGIN)) {
if (addr->node != current_addr.node
|| addr->net != current_addr.net) {
SetBridgeAddress(addr);
logit (1, "New default router %d.%d, goodness %d -> %d",
ntohs(addr->net), addr->node, current_goodness, goodness);
current_addr.node = addr->node;
current_addr.net = addr->net;
}
current_goodness = goodness;
current_time = now;
return;
}
/*
* same router, update heard-from time
*
*/
if (addr->node == current_addr.node
&& addr->net == current_addr.net) {
logit (1, "Current default router %d.%d, goodness %d",
ntohs(addr->net), addr->node, current_goodness);
current_time = now;
return;
}
/*
* different router, and we haven't heard
* from our current router for more than
* 15 seconds, adopt it
*
*/
if ((addr->node != current_addr.node
|| addr->net != current_addr.net)
&& (now - current_time) > 15) {
SetBridgeAddress(addr);
logit (1, "New default router %d.%d (old expired)",
ntohs(addr->net), addr->node);
current_addr.node = addr->node;
current_addr.net = addr->net;
current_goodness = goodness;
current_time = now;
return;
}
}
}
/*
* The following should be integrated into ABNBP.C someday
* (Looks like that someday will never come :-).
*/
/*
* private int nbpcpy(en, enc, start, nbp, nbptcnt, unique)
*
* nbpcpy copies the entities in nbp to the NBP Table entry array
* pointed to by en. Maximum number of entities is enc. Start specifies
* where in the array to start. Unique is used as a flag: if true
* then make sure the incoming items are unique (e.g. don't duplicate
* items in the table pointed to by en).
*
* at end number of entries inserted is returned
*
*/
private int
nbpcpy(entab, enstart, entabmax, nbp, nbptcnt, unique)
NBPTEntry *entab;
int enstart;
int entabmax;
NBP *nbp;
int nbptcnt;
int unique;
{
NBPTuple *ep;
int i, tcount, len;
NBPTEntry curr;
/* Add NBP tuples to user's data structure */
/* make sure curr's ent is empty */
bzero((caddr_t)&curr.ent, sizeof(curr.ent));
for (i=enstart, tcount=nbptcnt, ep = nbp->tuple;
tcount != 0 && i < entabmax;
tcount--) {
bcopy((caddr_t)&ep->addr,(caddr_t)&curr.addr, sizeof(AddrBlock));
bcopy((caddr_t)&ep->enume,(caddr_t)&curr.enume, sizeof(curr.enume));
len = pkt2c_ename(ep->name,&curr.ent);
ep = (NBPTuple *) ((char *) ep+(len+sizeof(AddrBlock)+1));
i += nbpinsertentry(entab, i, &curr, unique);
}
return(i-enstart);
}
/* insert entry into specified NBP Table. Check previous entries for */
/* conflict - if conflict - then update entry if update flag is on */
/* ow. insert at specified point */
/* returns 1 if inserted item, zero o.w. - no errors possible */
nbpinsertentry(entab, point, nbptentry, update)
NBPTEntry entab[];
int point;
NBPTEntry *nbptentry;
boolean update;
{
NBPTEntry *cetp = entab;
int i;
if (update) { /* update==false ==> always insert */
for (i=0; i < (point-1); i++) {
if (bcmp((caddr_t)&cetp->addr, (caddr_t)&nbptentry->addr,
sizeof(cetp->addr)) == 0)
if (cetp->enume == nbptentry->enume) {
bcopy((caddr_t)&nbptentry->ent,(caddr_t)&cetp->ent,
sizeof(cetp->ent));
return(0); /* done */
}
}
}
bcopy((caddr_t)nbptentry, (caddr_t)cetp, sizeof(NBPTEntry));
return(1);
}
/*
* Private int c2pkt_ename(EntityName *cn, u_char *pn)
*
* Copy entity name from c form into contiguous Apple Pascal
* form (packet form).
*
* return: length of pascal form entity name
*
*/
private int
c2pkt_ename(cn,pn)
u_char *pn;
EntityName *cn;
{
int i, cnt;
byte *s;
byte *pc;
cnt = 0;
for (s = cn->objStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) {
*pn = *s;
if (*s == '\0')
break;
}
if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */
i = ENTITYSIZE; /* too large: turncated to 32 chars */
*pc = i;
cnt += (i+1);
for (s = cn->typeStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) {
*pn = *s;
if (*s == '\0')
break;
}
if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */
i = ENTITYSIZE; /* too large: turncated to 32 chars */
*pc = i;
cnt += (i+1);
for (s = cn->zoneStr.s, pc = pn++, i = 0; i < ENTITYSIZE; i++, pn++, s++) {
*pn = *s;
if (*s == '\0')
break;
}
if (i > ENTITYSIZE) /* increment to cnt and check aginst cutoff */
i = ENTITYSIZE; /* too large: turncated to 32 chars */
*pc = i;
cnt += (i+1);
return(cnt); /* return number of bytes used */
}
/*
* Private int pkt2c_enames(u_char *pn, EntityName *cn);
*
* Copy entity names from packet form (abutting Apple Pascal
* strings) to c form into structure of type EntityName.
*
* return: the length of the packed string.
*
*/
private int
pkt2c_ename(pn,cn)
u_char *pn;
EntityName *cn;
{
int ol,tl,zl;
ol = *pn; /* length of object */
tl = *(pn+ol+1); /* length of type */
zl = *(pn+ol+tl+2); /* length of zone */
if (ol > ENTITYSIZE || tl > ENTITYSIZE || zl > ENTITYSIZE) {
logit(3,"pkt2c_entity_names: invalid length!");
return(0);
}
cpyp2cstr(cn->objStr.s,pn); /* copy them... */
cpyp2cstr(cn->typeStr.s,pn+ol+1);
cpyp2cstr(cn->zoneStr.s,pn+ol+tl+2);
return(ol+tl+zl+3); /* return length */
}
#ifdef notdef
/*
* print message - use vprintf whenever possible (solves the problem
* of using the varargs macros -- you must interpret the format).
* This is something all machine should, but don't have :-)
*/
private FILE *lfp = stderr;
#ifndef USEVPRINTF
/* Bletch - gotta do it because pyramids don't work the other way */
/* (using _doprnt and &args) and don't have vprintf */
/* of course, there will be something that is just one arg larger :-) */
/*VARARGS1*/
logit(level, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af)
int level;
char *fmt;
#else
logit(va_alist)
va_dcl
#endif
{
long time(), tloc;
char *timestr;
#ifdef USEVPRINTF
register char *fmt;
va_list args;
int level;
#endif
int saveerr;
extern int errno;
extern int sys_nerr;
extern char *sys_errlist[];
if (lfp == NULL) /* no logging? */
return;
saveerr = errno;
#ifdef USEVPRINTF
va_start(args);
level = va_arg(args, int);
fmt = va_arg(args, char *);
#endif
if (dlevel < (level & L_LVL))
return;
(void)time(&tloc);
timestr = (char *)ctime(&tloc);
timestr[24] = '\0'; /* hokey */
fprintf(lfp,"atis: %s ",timestr);
#ifdef USEVPRINTF
vfprintf(lfp, fmt, args);
va_end(args);
#else
fprintf(lfp, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af);
#endif
if (level & L_UERR) {
if (saveerr < sys_nerr)
fprintf(lfp, ": %s", sys_errlist[saveerr]);
else
fprintf(lfp, ": error %d\n", saveerr);
}
putc('\n', lfp);
fflush(lfp);
if (level & L_EXIT)
exit(1);
}
islogitfile()
{
if (lfp == stderr)
return(FALSE);
return(lfp != NULL);
}
logitfileis(filename, mode)
char *filename;
char *mode;
{
FILE *fp;
if ((fp = fopen(filename, mode)) != NULL) {
logit(0, "log file name %s", filename);
} else {
logit(0|L_UERR, "couldn't open logfile %s", filename);
}
lfp = fp; /* reset */
}
nologitfile()
{
if (lfp && lfp != stderr)
fclose(lfp);
}
#endif notdef
#ifdef NOSIGMASK
sighold_all()
{
int i;
for (i = 0; allsigs[i]; ++i)
sighold(allsigs[i]);
}
sigrelse_all()
{
int i;
for (i = 0; allsigs[i]; ++i)
sigrelse(allsigs[i]);
}
#endif NOSIGMASK