Files
CAP/support/uab/rtmp.c
2015-03-18 22:05:00 +01:00

1390 lines
35 KiB
C

static char rcsid[] = "$Author: djh $ $Date: 1992/07/15 14:04:45 $";
static char rcsident[] = "$Header: /mac/src/cap60/support/uab/RCS/rtmp.c,v 2.4 1992/07/15 14:04:45 djh Rel djh $";
static char revision[] = "$Revision: 2.4 $";
/*
* rtmp.c: RTMP, ZIP, and NBP gateway protocol modules
*
* dropped NBP here because it needs access to routing table
*
* Follows specification set in "Inside Appletalk" by Gursharan Sidhu,
* Richard F. Andrews, and Alan B. Oppenheimer, Apple Computer, Inc.
* June 1986.
*
* Copyright (c) 1988 by The Trustees of Columbia University
* in the City of New York.
*
* Permission is granted to any individual or institution to use,
* copy, or redistribute this software so long as it is not sold for
* profit, provided that this notice and the original copyright
* notices are retained. Columbia University nor the author make no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
*
* Edit History:
*
* August, 1988 CCKim Created
*
*/
static char columbia_copyright[] = "Copyright (c) 1988 by The Trustees of \
Columbia University in the City of New York";
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netat/appletalk.h>
#include "gw.h"
#ifndef OWNHASH
#include "hash.h"
#endif
/* stupid function so we can link together items */
struct chain {
struct chain *c_next; /* next in list */
caddr_t c_data; /* data */
};
/* should be in appletalk.h */
#define ZIP_query 1 /* query type */
#define ZIP_reply 2 /* reply type */
#define ZIP_takedown 3 /* takedown (NYI) */
#define ZIP_bringup 4 /* bringup (NYI) */
struct zipddp { /* ZIP packet */
byte zip_cmd;
byte zip_netcount;
};
/* zip name as found in zip reply and bringup packets */
struct zipname {
word zip_net;
byte zip_len;
};
#define zipNameLen 3
/* route states */
#define R_NONE 0 /* or false */
#define R_GOOD 1
#define R_BAD 2
#define R_SUSPECT 3
/* messages describing route states (shouldn't change) */
private char *rtmp_state_msg[4] = {
"deleted",
"good",
"bad",
"suspect"
};
#define MAXHOPS 15 /* maximum # of hops a route can have */
/* when to "age" route entries */
#define RTMP_VALIDITY_TIMEOUT 20
/* rtmp send timeout: initial is offset from zip timeout */
#define RTMP_INITIAL_SEND_TIMEOUT 30
#define RTMP_SEND_TIMEOUT 10
/* initial zip is for "local" zones */
#define ZIP_INITIAL_TIMEOUT 5
#define ZIP_QUERY_TIMEOUT 10
/* maximum number of routes */
#define NROUTES 500
private struct route_entry *routes; /* chain of routes */
private caddr_t route_htable_handle; /* route table handle */
/* bridge node handling stuff */
/* for now (should be hash table or some such) */
private caddr_t bridgenode_htable_handle;
#define NUMBRNODES 500
struct bridge_node { /* bridge node entry */
NODE id;
PORT_T port;
};
struct bridge_node_key { /* structure to pass a key in */
NODE *idp;
PORT_T port;
};
private int zone_unknown = 0; /* a zone is unknown */
/* same as pstr */
private caddr_t zone_hash_table;
#define NZONES 250 /* random, need not be too big */
private struct chain *zonelist; /* chain of zones */
private int m_route = 0;
private int m_bnode = 0;
private int m_cnode = 0;
private int m_zone = 0;
export void rtmp_init();
export void rtmp_start();
private int route_compare();
private caddr_t route_alloc();
private u_int route_compress();
private void routes_init();
export struct route_entry *route_find();
private struct route_entry *route_create();
private void route_delete();
export struct route_entry *route_list_start();
export struct route_entry *route_next();
export int route_add_host_entry();
export char *node_format();
private int bstrcmp();
private int bstrcmpci();
private int bridgenode_compare();
private caddr_t bridgenode_alloc();
private u_int bridgenode_compress();
private void bridgenode_init();
private NODE *bridgenode_find();
private boolean rtmp_handler();
private void rtmp_dump_entry();
private void rtmp_dump_entry_to_file();
private int rtmp_send_timeout();
private void rtmp_send();
private int rtmp_validity_timeout();
private void rtmp_replace_entry();
private void rtmp_update_entry();
private boolean rtmprq_handler();
private boolean zip_handler();
private int zip_query_timeout();
private int zip_query_handler();
private int zip_reply_handler();
private int zip_atp_handler();
/* private int zone_hash(); */
private caddr_t zone_alloc();
private int pstrc();
private int zone_compare();
private u_int zone_compress();
private void zone_init();
export byte *zone_find();
private void rtmp_format_hash_stats();
export void rtmp_dump_stats();
export void rtmp_dump_table();
/*
* initialize
*
* clear up vars
*/
export void
rtmp_init()
{
routes_init();
bridgenode_init();
zone_init();
}
/*
* rtmp start - fires up the timers
*
*/
export void
rtmp_start()
{
struct timeval tv;
tv.tv_sec = RTMP_VALIDITY_TIMEOUT; /* 20 second validity timer */
tv.tv_usec = 0;
relTimeout(rtmp_validity_timeout, 0, &tv, TRUE);
/* these last two could be combined */
tv.tv_sec = RTMP_INITIAL_SEND_TIMEOUT;
tv.tv_usec = 0;
relTimeout(rtmp_send_timeout, 0, &tv, TRUE);
tv.tv_sec = ZIP_INITIAL_TIMEOUT;
tv.tv_usec = 0;
relTimeout(zip_query_timeout, 0, &tv, TRUE);
}
/* routing table handler */
/*
* compare key net to route entry
*
*/
private int
route_compare(net,re)
word *net;
struct route_entry *re;
{
return(((int)*net) - ((int)(re->re_ddp_net)));
}
/*
* allocate data: create a route
*
*
*/
private caddr_t
route_alloc(net)
word *net;
{
struct route_entry *re;
if ((re = (struct route_entry *)malloc(sizeof(struct route_entry))) == NULL)
return(NULL);
m_route++;
re->re_ddp_net = *net; /* copy in network */
re->re_state = R_NONE; /* set state to none */
re->re_next = routes;
routes = re; /* link to head */
return((caddr_t)re); /* and return */
}
/*
* compress key to u_int
*
*/
private u_int
route_compress(net)
word *net;
{
return(*net);
}
private void
routes_init()
{
routes = NULL; /* no routes */
route_htable_handle =
h_new(HASH_POLICY_CHAIN, HASH_TYPE_MULTIPLICATIVE, NROUTES,
route_compare, route_alloc, route_compress, NULL, NULL, NULL);
ddp_open(rtmpSkt, rtmp_handler);
}
/*
* find route for a particular network
*
*/
export
struct route_entry *
route_find(net)
word net;
{
struct route_entry *re;
re = (struct route_entry *)h_member(route_htable_handle, &net);
if (re && re->re_state) /* we don't really delete */
return(re);
return(NULL);
}
/*
* create a routing entry and initialize it.
*
*/
private struct route_entry *
route_create(net)
word net;
{
register struct route_entry *re;
int d, b;
re = (struct route_entry *)
h_operation(HASH_OP_INSERT, route_htable_handle, &net, -1,-1,&d,&b);
if (re == NULL) /* should not happen, but. */
return(NULL);
logit(LOG_LOTS, "new route for net %d.%d at hash [bkt %d, d %d]\n",
nkipnetnumber(net),nkipsubnetnumber(net),
b,d);
zone_unknown++; /* new route, so set */
re->re_dist = 0; /* assume zero distance */
re->re_bridgeid_p = NULL; /* means self */
/* re->re_ddp_net = net; */ /* done already */
re->re_zip_taken = FALSE; /* make sure */
re->re_zonep = NULL; /* make sure */
return(re);
}
/* delete route - for now just set state to none */
/* may want to time it out at some point */
private void
route_delete(re)
struct route_entry *re;
{
re->re_state = R_NONE;
}
/* return route list start */
export struct route_entry *
route_list_start()
{
return(routes);
}
/* get next in list: hidden in case we want to change way done */
export struct route_entry *
route_next(re)
struct route_entry *re;
{
return(re->re_next);
}
/*
* establish a new port
*
* net in network format (8 bits - word)
* node as bytes (variable length, network order, zero padded in front)
* nodesize - number of bits valid in node
* ddp_node
* zone to set if any
*
*/
export
route_add_host_entry(port, net, zone)
PORT_T port;
word net;
byte *zone;
{
struct route_entry *re;
if (net == 0)
return(-1);
/* if network given, then construct internal route */
/* do a find in case route already acquired */
if ((re = route_find(net)) == NULL)
if ((re = route_create(net)) == NULL)
return(-1);
/* reset or set */
re->re_state = R_GOOD;
re->re_port = port;
re->re_dist = 0;
re->re_bridgeid_p = NULL;
logit(LOG_BASE, "port %d host route added for network %d.%d",
port, nkipnetnumber(net), nkipsubnetnumber(net));
rtmp_dump_entry("host port", re);
if (zone == NULL)
return(0);
/* if zone given for net, then add it */
re->re_zonep = zone_find(zone, TRUE); /* insert zone name */
if (re->re_zonep && zone_unknown > 0) {
logit(LOG_BASE, "port %d zone name %s inserted", port, re->re_zonep+1);
zone_unknown--;
}
return(0);
}
/*
* format node structure for printing
*
*/
export char *
node_format(node)
NODE *node;
{
static char tmpbuf[200];
static char *fmtstr = "%x%x%x%x%x%x%x%x";
byte *id;
int n;
if (node == NULL)
return("self");
id = node->n_id;
/* if less than 5 bytes, convert to network order int and print */
switch (node->n_bytes) {
case 4:
n = ntohl((id[0]<<24)|(id[1]<<16)|(id[2]<<8)|id[3]);
break;
case 3:
n = ntohl((id[0]<<16)|(id[1]<<8)|id[2]);
break;
case 2:
n = ntohs(id[0]<<8|id[1]);
break;
case 1:
n = id[0];
break;
default:
sprintf(tmpbuf, fmtstr+2*(MAXNODEBYTE-node->n_bytes),
node->n_id[0], node->n_id[1],
node->n_id[2], node->n_id[3],
node->n_id[4], node->n_id[5],
node->n_id[6], node->n_id[7]);
return(tmpbuf);
}
sprintf(tmpbuf, "%d", n);
return(tmpbuf);
}
/* like strncmp, but allows "0" bytes */
private int
bstrcmp(a,b,l)
register byte *a;
register byte *b;
register int l;
{
register int c = 0; /* if zero length, then same */
while (l--) /* while data */
if ((c = (*a++ - *b++))) /* compare and get difference */
break; /* return c */
return(c); /* return value */
}
/* like bstrcmp, but case insensitive */
private int
bstrcmpci(a,b,l)
register byte *a;
register byte *b;
register int l;
{
register int c = 0; /* if zero length, then same */
register byte aa, bb;
while (l--) { /* while data */
aa = *a++;
if ( isascii(aa) && isupper(aa) )
aa = tolower(aa);
bb = *b++;
if ( isascii(bb) && isupper(bb) )
bb = tolower(bb);
if ((c = (aa - bb))) /* compare and get difference */
break; /* return c */
}
return(c); /* return value */
}
/* bridge node table handler */
/* compare (port,node) to bridgenode */
private int
bridgenode_compare(k, bn)
struct bridge_node_key *k;
struct bridge_node *bn;
{
if (k->port != bn->port)
return((int)(k->port - bn->port));
if (k->idp->n_size != bn->id.n_size)
return(k->idp->n_size - bn->id.n_size);
return(bstrcmp((caddr_t)k->idp->n_id, (caddr_t)bn->id.n_id, bn->id.n_bytes));
}
/* allocate space for a bridge node */
private caddr_t
bridgenode_alloc(k)
struct bridge_node_key *k;
{
struct bridge_node *bn;
if ((bn = (struct bridge_node *)malloc(sizeof(struct bridge_node))) == NULL)
return(NULL);
m_bnode++;
#ifdef DEBUG
logit(0, "BRIDGE NODE CREATE");
logit(0, "PORT %d", k->port);
logit(0, "ID len %d, byte 0 %d", k->idp->n_size, k->idp->n_id[0]);
#endif
bn->id = *k->idp; /* copy in */
bn->port = k->port;
return((caddr_t)bn);
}
/* compress key to an u_int */
private u_int
bridgenode_compress(k)
struct bridge_node_key *k;
{
u_int r = (u_int)k->port;
int i = k->idp->n_bytes; /* # of bytes */
byte *p = k->idp->n_id; /* data */
/* add in p, but keep rotating r */
r ^= k->idp->n_size; /* xor size in */
while (i--)
r = ((r>>1)|(r<<31)) + *p++;
return(r);
}
/* initialize */
/* should we limit the # of bridge nodes by using a open hash? */
private void
bridgenode_init()
{
bridgenode_htable_handle =
h_new(HASH_POLICY_CHAIN, HASH_TYPE_MULTIPLICATIVE, NUMBRNODES,
bridgenode_compare, bridgenode_alloc,
bridgenode_compress, NULL, NULL, NULL);
}
/* find a bridge node based on port,node key */
private NODE *
bridgenode_find(port, node)
PORT_T port;
NODE *node;
{
struct bridge_node *bn;
struct bridge_node_key bk;
int d,b;
bk.idp = node;
bk.port = port;
bn = (struct bridge_node *)
h_operation(HASH_OP_INSERT, bridgenode_htable_handle, &bk, -1,-1,&d,&b);
if (bn == NULL)
return(NULL);
#ifdef notdef
printf("look bridge node %s at hash [bkt %d, d %d]\n",
node_format(node), b,d);
#endif
return(&bn->id); /* return node id */
}
/* RTMP handling */
/*
* handle incoming rtmp packets
*
*/
/*ARGSUSED*/
private boolean
rtmp_handler(port, ddp, data, len)
PORT_T port;
DDP *ddp; /* not used */
byte *data;
int len;
{
struct route_entry *re;
RTMPtuple tuple;
word net;
NODE id, *sid;
if (ddp->type == ddpRTMP_REQ) /* is it a rtmp request? */
return(rtmprq_handler(port,ddp)); /* yes, handle it */
if (ddp->type != ddpRTMP) /* is it rtmp? */
return(TRUE); /* no, dump it */
if (len < sizeof(net)) /* rtmpSize */
return(TRUE);
/* get net out */
bcopy((caddr_t)data, (caddr_t)&net, sizeof(net));
len -= sizeof(net);
data += sizeof(net);
if (len < 1) /* id len */
return(TRUE);
id.n_size = *data; /* get id length */
id.n_bytes = (id.n_size + 7) / 8; /* make into bytes */
if (len < (id.n_bytes+1)) /* id len + id */
return;
bcopy((caddr_t)data+1, (caddr_t)id.n_id, id.n_bytes); /* copy id */
len -= (id.n_bytes + 1); /* reduce */
data += (id.n_bytes + 1); /* reduce */
sid = bridgenode_find(port, &id); /* canonicalize */
if (sid == NULL) /* ourselves or no room */
return(TRUE);
logit(LOG_BASE, "NEW RTMP: port %d, source %s", port, node_format(sid));
if (!PORT_NET_READY(port, net, sid))
route_add_host_entry(port, net, NULL); /* zone isn't known yet! */
/* 15/06/92 <kenji-i@ascii.co.jp> */
/* if non-extended network, first tuple is always 0.0[$82] */
if (len < rtmpTupleSize)
return(TRUE);
bcopy((caddr_t)data, (caddr_t)&tuple, rtmpTupleSize);
if (tuple.net == 0 && tuple.hops == 0x82) {
logit(LOG_LOTS, "ignore_entry: non-extended network marker");
data += rtmpTupleSize;
len -= rtmpTupleSize;
}
/* use tuplesize because of byte alignment problems */
while (len >= rtmpTupleSize) {
bcopy((caddr_t)data, (caddr_t)&tuple, rtmpTupleSize);
data += rtmpTupleSize, len -= rtmpTupleSize;
re = route_find(tuple.net); /* get entry if any */
if (re) /* update */
rtmp_update_entry(re, port, sid, &tuple);
else { /* create */
re = route_create(tuple.net);
if (!re)
continue;
logit(LOG_LOTS, "create_entry: net %d.%d",
nkipnetnumber(tuple.net),
nkipsubnetnumber(tuple.net));
rtmp_replace_entry(re, port, sid, &tuple, FALSE);
}
}
return(TRUE);
}
/*
* dump rtmp table entry nicely
*
*/
private void
rtmp_dump_entry(msg, re)
char *msg;
struct route_entry *re;
{
if (!re->re_state)
return;
/* fixup */
logit(LOG_LOTS, "%s: net %d.%d, bridge %s, dist %d, port %d, state %s",
msg, nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net),
node_format(re->re_bridgeid_p), re->re_dist, re->re_port,
rtmp_state_msg[re->re_state]);
}
private void
rtmp_dump_entry_to_file(fd, re)
FILE *fd;
struct route_entry *re;
{
if (!re->re_state)
return;
/* fixup */
fprintf(fd, " net %d.%d, bridge %s, dist %d, port %d, state %s, zone %d-%s\n",
nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net),
node_format(re->re_bridgeid_p), re->re_dist, re->re_port,
rtmp_state_msg[re->re_state],
(re->re_zonep ? *re->re_zonep : 0),
(re->re_zonep ? ((char *)re->re_zonep+1) : (char *)"<unknown>"));
}
/*
* timeout: rtmp send - broadcast rtmp's
*
*/
private int
rtmp_send_timeout()
{
PORT_T port;
register struct route_entry *re;
struct timeval tv;
RTMP *rtmp;
RTMPtuple tuple;
char buf[ddpMaxData];
char *p;
int count, rest;
rtmp = (RTMP *)buf;
re = routes;
/* load up rtmp entry */
do {
p = buf + sizeof(RTMP); /* point into buf */
rest = ddpMaxData - sizeof(RTMP);
/* while room in packet */
for (count = 0; re && rest >= rtmpTupleSize; re = re->re_next) {
if (re->re_state != R_GOOD && re->re_state != R_SUSPECT)
continue; /* not good or suspect */
if (re->re_zip_taken) /* in zip takedown */
continue;
/* strict: don't send out updates for routes we don't know */
/* the zone for -- this way bad data may go away */
if (re->re_zonep == NULL)
continue;
if (!(re->re_dist < MAXHOPS))
continue;
tuple.net = re->re_ddp_net;
tuple.hops = re->re_dist;
bcopy((caddr_t)&tuple, p, rtmpTupleSize);
count++; /* found another */
p += rtmpTupleSize;
rest -= rtmpTupleSize;
}
for (port = PORT_LIST_START(); port != NULL; port = PORT_NEXT(port)) {
NODE *pid;
if (!PORT_ISBRIDGING(port))
continue;
if (!PORT_READY(port))
continue;
rtmp->net = PORT_DDPNET(port);
pid = PORT_NODEID(port);
rtmp->idLen = pid->n_size;
bcopy((caddr_t)pid->n_id, (caddr_t)&rtmp->id, pid->n_bytes);
rtmp_send(rtmp, 3+pid->n_bytes, count, rtmp->net, DDP_BROADCAST_NODE);
}
} while (re); /* still routes */
tv.tv_sec = RTMP_SEND_TIMEOUT; /* 10 second send timer */
tv.tv_usec = 0;
relTimeout(rtmp_send_timeout, 0, &tv, TRUE);
}
/*
* send the rtmp packet on the specified port
*
*/
private void
rtmp_send(rtmp, rtmp_size, count, dstnet, dst)
RTMP *rtmp;
int rtmp_size;
int count;
word dstnet;
byte dst;
{
DDP rddp; /* reply ddp header */
int dlen = rtmp_size+rtmpTupleSize*count;
rddp.srcSkt = rtmpSkt;
rddp.dstNet = dstnet;
rddp.dstNode = dst;
rddp.dstSkt = rtmpSkt;
rddp.type = ddpRTMP;
ddp_output(NULL, &rddp, rtmp, dlen);
logit(LOG_LOTS, "RTMP: sent %d length packet with %d tuples",dlen,count);
logit(LOG_LOTS, "\tto net %d, node %d", dstnet, dst);
}
/*
* timeout: rtmp validity
*
* run timer on rtmp validity
*/
private int
rtmp_validity_timeout()
{
register struct route_entry *re;
struct timeval tv;
for (re = routes; re; re = re->re_next) {
switch (re->re_state) {
case R_GOOD:
if (re->re_dist != 0)
re->re_state = R_SUSPECT;
break;
case R_SUSPECT:
rtmp_dump_entry("route went bad", re);
re->re_state = R_BAD;
break;
case R_BAD:
rtmp_dump_entry("route deleted", re);
route_delete(re);
break;
}
}
tv.tv_sec = RTMP_VALIDITY_TIMEOUT; /* 20 second validity timer */
tv.tv_usec = 0;
relTimeout(rtmp_validity_timeout, 0, &tv, TRUE);
}
/*
* rtmp_replace_entry: replace or add a route
*
* if istickler is set then this is a tickler packet that ensures
* route stays good
*
*/
private void
rtmp_replace_entry(re, port, sid, tuple, istickler)
struct route_entry *re;
PORT_T port; /* source port */
NODE *sid; /* source id */
RTMPtuple *tuple;
int istickler; /* true if this replace should be */
/* considered "a tickle" */
{
int rewasthere = re->re_state;
/* dump won't do anything if no state */
if (rewasthere && !istickler)
rtmp_dump_entry("replacing entry", re);
re->re_dist = tuple->hops + 1;
re->re_bridgeid_p = sid;
re->re_port = port;
re->re_state = R_GOOD;
if (!istickler)
rtmp_dump_entry(rewasthere ? "replaced entry" : "new" , re);
}
/*
* rtmp_update_entry - figure out whether the route should be updated
* or not
*
*/
private void
rtmp_update_entry(re, port, sid, tuple)
struct route_entry *re;
PORT_T port; /* source port */
NODE *sid; /* source id */
RTMPtuple *tuple;
{
if (re->re_state == R_BAD && tuple->hops < MAXHOPS) { /* replace entry */
logit(LOG_LOTS, "update_entry: net %d.%d, replacing because bad",
nkipnetnumber(tuple->net),
nkipsubnetnumber(tuple->net));
rtmp_replace_entry(re, port, sid, tuple, FALSE);
return;
}
if (tuple->hops < MAXHOPS && re->re_dist > tuple->hops) {
int istickler;
istickler = (re->re_dist == (tuple->hops+1));
if (!istickler) {
/* if not simple case of updating bad point */
logit(LOG_LOTS, "update_entry: net %d.%d, replacing because better route",
nkipnetnumber(tuple->net),
nkipsubnetnumber(tuple->net));
}
rtmp_replace_entry(re, port, sid, tuple, istickler);
return;
}
/* know we know that hops >= 15 or re->re_dist <= tuple->hops */
/* if re's bridge matches the rmtp source bridge */
/* and in on the same port, then the network is futher away... */
if (re->re_bridgeid_p == sid && re->re_port == port) {
re->re_dist++;
if ((re->re_dist) <= MAXHOPS) {
re->re_state = R_GOOD;
rtmp_dump_entry("hop count increased", re);
} else {
rtmp_dump_entry("too many hops", re);
route_delete(re);
}
}
}
/*
* handle incoming rtmp request packet
*
*/
private int
rtmprq_handler(port, ddp)
PORT_T port;
DDP *ddp;
{
RTMP rtmp;
NODE *pid;
if (!PORT_ISBRIDGING(port)) /* not full bridge */
return(TRUE); /* so, don't advertise */
if (!PORT_READY(port)) /* port isn't fully setup */
return(TRUE);
/* respond with data about the port */
rtmp.net = PORT_DDPNET(port);
pid = PORT_NODEID(port);
rtmp.idLen = pid->n_size;
bcopy((caddr_t)pid->n_id, (caddr_t)&rtmp.id, pid->n_bytes);
/* no tuples */
rtmp_send(&rtmp, 3+pid->n_bytes, 0, ddp->srcNet, ddp->srcNode);
return(TRUE);
}
/*
* handle incoming DDP zip packets
*/
private boolean
zip_handler(port, ddp, data, datalen)
PORT_T port;
DDP *ddp;
byte *data;
int datalen;
{
struct zipddp zd;
if (ddp->type == ddpATP) /* atp? */
return(zip_atp_handler(port, ddp, data, datalen)); /* yes */
if (ddp->type != ddpZIP) /* zip? */
return(TRUE); /* no, dump it */
if (datalen < sizeof(zd))
return(TRUE);
bcopy((caddr_t)data, (caddr_t)&zd, sizeof(zd)); /* get zip header */
datalen -= sizeof(zd), data += sizeof(zd);
switch (zd.zip_cmd) {
case ZIP_query:
zip_query_handler(zd.zip_netcount, port, ddp, data, datalen);
break;
case ZIP_reply:
zip_reply_handler(zd.zip_netcount, port, ddp, data, datalen);
break;
case ZIP_takedown:
break;
case ZIP_bringup:
break;
}
return(TRUE);
}
/*
* ZIP timeout
*
* query routes with unkown zones
*
* algorithm: send zip query to bridge that sent us the rtmp.
* try to enclose as many networks as possible in the query
*
*/
private int
zip_query_timeout()
{
DDP ddp;
struct route_entry *re;
struct route_entry *first;
PORT_T port;
int first_idx;
int j;
NODE *curbridge;
struct zipddp *zd;
word zip_buf[ddpMaxData / sizeof(word)];
int count;
int mainnotknown = FALSE;
if (zone_unknown) { /* set whenever new route is created */
/* initialize helper field, find first to query */
for (first=NULL, re = routes, j = 0; re ; re = re->re_next)
if (re->re_state && !re->re_zonep) {
if (!first) { /* remember first */
first = re;
}
if (re->re_bridgeid_p == NULL) {
if (!mainnotknown) {
first = re; /* reset first one to do */
mainnotknown = TRUE; /* don't know zone of a main interface */
}
}
re->re_zip_helper = 0;
j++; /* mark work */
}
if (j == 0)
zone_unknown = FALSE;
/* query the various bridges */
while (j && first) {
curbridge = first->re_bridgeid_p; /* bridge id pointer */
port = first->re_port; /* get port for this bridge */
count = 1; /* skip first word */
re = first; /* this is where to start */
first = NULL; /* reset start point */
for (; re ; re = re->re_next) {
/* skip if main interf. not known */
if (mainnotknown && re->re_bridgeid_p)
continue; /* and not local interface */
if (!re->re_state || re->re_zonep || re->re_zip_helper) /* ignore */
continue;
if (re->re_bridgeid_p == curbridge &&
(count < (ddpMaxData / sizeof(word)))) {
j--;
re->re_zip_helper = 1; /* mark */
zip_buf[count++] = re->re_ddp_net; /* to get */
logit(LOG_LOTS, "will zip %s for %d.%d", node_format(curbridge),
nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net));
} else if (!first) { /* remember next in sequence */
first = re; /* set first */
}
}
if (count == 1) /* something weird happened */
continue;
zd = (struct zipddp *)zip_buf;
zd->zip_cmd = ZIP_query;
zd->zip_netcount = count - 1;
ddp.dstNet = PORT_DDPNET(port);
/* this will break on extended networks */
ddp.dstNode = curbridge ? curbridge->n_id[0] : DDP_BROADCAST_NODE;
ddp.dstSkt = zipZIS;
ddp.srcSkt = zipZIS;
ddp.type = ddpZIP;
logit(LOG_LOTS, "zipping %s for %d networks", node_format(curbridge),
count-1);
ddp_output(curbridge, &ddp, zip_buf, count*sizeof(word));
}
}
/* restart query */
{ struct timeval tv;
tv.tv_sec = ZIP_QUERY_TIMEOUT;
tv.tv_usec = 0;
relTimeout(zip_query_timeout, 0, &tv, TRUE);
}
}
/*
* zip_query_handler: handle an incoming zip query packet
*
*/
private int
zip_query_handler(count, port, ddp, data, datalen)
int count;
PORT_T port;
DDP *ddp;
byte *data;
int datalen;
{
struct route_entry *re;
DDP sddp;
byte buf[ddpMaxData];
int slen, i, zl;
byte *p;
word net;
struct zipddp *zd;
p = buf;
p += sizeof(struct zipddp);
slen = sizeof(struct zipddp);
zd = (struct zipddp *)buf;
zd->zip_cmd = ZIP_reply; /* set command */
zd->zip_netcount = 0; /* zero nets in response as yet */
/* best effort -- fit as many as possible, but don't bother with */
/* multiple replies -- not clear remote would handle anyway */
while (count--) {
if (datalen < sizeof(net)) /* any data left? */
break; /* no, count is wrong, stop! */
bcopy((caddr_t)data, (caddr_t)&net, sizeof(net));
datalen -= sizeof(net), data += sizeof(net);
if ((re = route_find(net)) == NULL) /* no route skip */
continue;
if (!re->re_zonep)
continue;
i = ((*re->re_zonep) + 1 + sizeof(word));
if ((slen + i) > ddpMaxData)
break;
/* copy in response data */
bcopy((caddr_t)&net, (caddr_t)p, sizeof(net));
p += sizeof(net);
zl = *re->re_zonep + 1; /* get zone length */
bcopy((caddr_t)re->re_zonep, (caddr_t)p, zl); /* copy zone name */
p += zl;
zd->zip_netcount++; /* increment count */
logit(LOG_JUNK, "query on net %d.%d yields zone %s",
nkipnetnumber(net), nkipsubnetnumber(net), re->re_zonep+1);
slen += i;
}
sddp.dstNet = ddp->srcNet;
sddp.dstNode = ddp->srcNode;
sddp.dstSkt = ddp->srcSkt;
sddp.srcSkt = zipZIS;
sddp.type = ddpZIP;
ddp_output(NULL, &sddp, buf, slen);
}
/*
* handle incoming zip reply. basically insert zone names
* into the table if possible
*
*/
private int
zip_reply_handler(count, port, ddp, data, datalen)
int count;
PORT_T port;
DDP *ddp;
byte *data;
int datalen;
{
word net;
struct route_entry *re;
byte *p = data;
byte *pp, *zone;
int zonelen;
while (count--) {
if (datalen < (1+sizeof(net)))
break;
bcopy((caddr_t)p, (caddr_t)&net, sizeof(net)); /* get zone information */
p += sizeof(net); /* move to the name */
datalen -= sizeof(net);
zonelen = 1 + *p;
/* now p points to a pstr */
if (datalen < zonelen) /* no data left? */
break;
zone = p; /* p now points to zone */
p += zonelen;
datalen -= zonelen;
if ((re = route_find(net)) == NULL) {
continue;
}
pp = (byte *)zone_find(zone, TRUE); /* find or insert zone name */
if (pp == NULL) {
logit(LOG_BASE, "ZIP: no room for insert for zone\n");
continue;
}
if (re->re_zonep) { /* zone already known for net */
if (pp && pp != re->re_zonep) {
logit(LOG_BASE, "zone name conflict for %d.%d, received %s had %s\n",
nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net),
pp+1, re->re_zonep+1);
}
continue;
}
/* must have zone for this route (which didn't have one before) */
re->re_zonep = pp; /* mark zone */
/* if zone is known for primary route, say so */
if (re->re_bridgeid_p == NULL && re->re_ddp_net == PORT_DDPNET(port))
PORT_ZONE_KNOWN(port, re->re_zonep);
logit(LOG_BASE, "ZIPPED: from %d.%d.%d network %d.%d for zone %s",
nkipnetnumber(ddp->srcNet), nkipsubnetnumber(ddp->srcNet),
ddp->srcNode,
nkipnetnumber(re->re_ddp_net), nkipsubnetnumber(re->re_ddp_net),
re->re_zonep+1);
}
}
/*
* handle a zip atp command: Get Zone List or Get My Zone
*
*
*/
/* these are only defined in abatp.h not appletalk.h (because nobody) */
/* should need such fine control under normal cirucmstances */
/* put the ifndefs around in case we decide to move them one day */
#ifndef atpCodeMask
# define atpCodeMask 0xc0
#endif
#ifndef atpReqCode
# define atpReqCode 0x40
#endif
#ifndef atpRspCode
# define atpRspCode 0x80
#endif
#ifndef atpEOM
# define atpEOM 0x10
#endif
private int
zip_atp_handler(port, ddp, data, datalen)
PORT_T port;
DDP *ddp;
byte *data;
int datalen;
{
int paranoia = FALSE;
DDP sddp;
ATP *atp; /* pointer to atp header */
zipUserBytes *zub; /* pointer to zip user byte */
char data_buf[ddpMaxData]; /* data buffer */
char *p; /* pointer to data */
int ps = ddpMaxData; /* room left in buffer */
int sidx; /* for getzonelist */
int count, t, i;
struct chain *zp;
if (datalen < sizeof(ATP))
return;
bcopy((caddr_t)data, (caddr_t)data_buf, sizeof(ATP)); /* get bytes */
atp = (ATP *)data_buf; /* should be aligned */
p = data_buf + sizeof(ATP); /* point to data */
ps -= sizeof(ATP);
/* control must hold request and only request */
if ((atp->control & atpCodeMask) != atpReqCode)
return;
/* bitmap should ask for at least one packet */
if ((atp->bitmap & 0x1) == 0)
return;
zub = (zipUserBytes *)&atp->userData;
if (paranoia && zub->zip_zero != 0)
return;
zub->zip_zero = 0; /* ensure */
switch (zub->zip_cmd) {
case zip_GetMyZone:
if (paranoia && ntohs(zub->zip_index) != 0)
return;
count = 1;
zub->zip_cmd = 0; /* zero because gmz */
/* return zone of source network */
/* if given network is 0 (mynet), use that of port */
{ struct route_entry *re;
if (ddp->srcNet == 0) {
if ((re = route_find(PORT_DDPNET(port))) == NULL)
return;
} else {
if ((re = route_find(ddp->srcNet)) == NULL)
return;
}
if (re->re_zonep == NULL)
return;
/* no way we could fill up buffer */
{ int zl;
zl = 1 + *re->re_zonep;
bcopy((caddr_t)re->re_zonep, (caddr_t)p, zl);
ps -= zl;
}
}
break;
case zip_GetZoneList:
sidx = ntohs(zub->zip_index);
sidx--; /* 1 is start */
/* move through zonelist, decrementing sidx as we find a filled slot */
/* a zone name may be sent more than once if we get an incoming zone */
/* between GZL commands */
/* move through sidx items */
for (zp = zonelist; zp && sidx ; zp = zp->c_next)
sidx--;
if (sidx) /* no more zones */
break;
/* i already set, zp already set */
/* assume LastFlag */
zub->zip_cmd = 0xff; /* set every bit because not sure */
/* which bit is lastflag */
count = 0;
while (zp) {
byte *znp = (byte *)zp->c_data; /* get zone name */
t = znp[0] + 1; /* get length of zone name */
if ((ps - t) < 0) {
zub->zip_cmd = 0; /* clear lastflag: one remains */
break;
}
bcopy((caddr_t)znp, (caddr_t)p, t); /* copy data */
count++; /* bump count */
ps -= t; /* reduce available data */
p += t; /* move data pointer */
zp = zp->c_next; /* move to next zone */
}
zub->zip_index = count; /* set count */
break;
default: /* bad type, this is NOT paranoia */
return;
}
zub->zip_index = htons(count); /* set count */
atp->control = atpRspCode|atpEOM;
atp->bitmap = 0; /* sequence 0 */
/* tid already set */
sddp.dstNet = ddp->srcNet;
sddp.dstNode = ddp->srcNode;
sddp.dstSkt = ddp->srcSkt;
sddp.srcSkt = ddp->dstSkt;
sddp.type = ddpATP;
ddp_output(NULL, &sddp, (byte *)data_buf, ddpMaxData - ps);
}
/* keep zone name in a linked list */
/* take a zone pstring and duplicate it -- make sure null terminated */
private caddr_t
zone_alloc(p)
byte *p;
{
int len = (int)*p; /* get length */
struct chain *cnode;
byte *r;
if ((cnode = (struct chain *)malloc(sizeof(struct chain))) == NULL)
return(NULL);
m_cnode++;
if (p == NULL) /* translate NULL string */
p = '\0';
r = (byte *)malloc(len+2); /* one for null, one for lenth */
if (r == NULL) {
free(cnode);
return(NULL);
}
m_zone++;
bcopy(p, r, len+1); /* copy in data */
r[len+1] = '\0'; /* make sure tied off */
cnode->c_next = zonelist; /* link next to head */
zonelist = cnode; /* link link to this */
cnode->c_data = (caddr_t)r; /* copy in data */
return((caddr_t)cnode);
}
/* needs to be case insensitive */
private int
pstrc(p,s)
byte *p, *s;
{
int r = (*p - *s);
if (r)
return(r);
return(bstrcmpci(p+1, s+1, *p));
}
/* needs to be case insensitive */
private int
zone_compare(s,cnode)
byte *s;
struct chain *cnode;
{
return(pstrc(s, cnode->c_data));
}
/* needs to be case insensitive */
private u_int
zone_compress(p)
byte *p;
{
u_int r = 0;
int i = (int) *p++;
byte pp;
/* add in p, but keep rotating r */
while (i--) {
pp = *p++;
if ( isascii(pp) && isupper(pp) )
pp = tolower(pp);
r = ((r>>1)|(r<<31)) + pp;
}
return(r);
}
private void
zone_init()
{
zone_hash_table = h_new(HASH_POLICY_CHAIN, HASH_TYPE_MULTIPLICATIVE,
NZONES, zone_compare, zone_alloc, zone_compress,
NULL, NULL, NULL);
zonelist = NULL;
ddp_open(zipZIS, zip_handler);
}
export byte *
zone_find(name, insert)
byte *name;
int insert;
{
int b, d;
struct chain *cnode;
cnode = (struct chain *)h_operation(insert ? HASH_OP_INSERT : HASH_OP_MEMBER,
zone_hash_table, name, -1,-1,&d,&b);
if (cnode == NULL)
return(NULL);
logit(LOG_LOTS, "%s for %s [%d,%d]\n",
insert ? "insert" : "lookup", cnode->c_data+1, b,d);
return((byte *)cnode->c_data); /* return data */
}
private void
rtmp_format_hash_stats(fd, s)
FILE *fd;
struct hash_statistics *s;
{
fprintf(fd, "\t%d lookups since last rehash, average distance %.02f\n",
s->hs_lnum, s->hs_lnum ? ((float)s->hs_lsum / s->hs_lnum) : 0.0);
fprintf(fd, "\t%d lookups total, average distance %.02f\n",
s->hs_clnum, s->hs_clnum ? ((float)s->hs_clsum / s->hs_clnum) : 0.0);
}
export void
rtmp_dump_stats(fd)
FILE *fd;
{
putc('\n', fd);
fprintf(fd, "Hash table statistics for zone lookups\n");
rtmp_format_hash_stats(fd, h_statistics(zone_hash_table));
fprintf(fd, "\nHash table statistics for routing table lookups\n");
rtmp_format_hash_stats(fd, h_statistics(route_htable_handle));
putc('\n', fd); /* output cr */
fprintf(fd,"%d routes, %d bridge nodes allocated\n", m_route, m_bnode);
fprintf(fd,"%d zones, %d zone chain nodes allocated\n", m_zone, m_cnode);
putc('\n', fd); /* output cr */
}
export void
rtmp_dump_table(fd)
FILE *fd;
{
register struct route_entry *re;
fprintf(fd, "Routing table dump\n");
for (re = routes; re ; re = re->re_next)
if (re->re_state)
rtmp_dump_entry_to_file(fd, re);
putc('\n', fd);
}