home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3
/
hamradioversion3.0examsandprograms1992.iso
/
packet
/
n17jsrc
/
rspf.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-10-18
|
59KB
|
1,868 lines
/* RSPF - Radio Shortest Path First
* This code may be used for non-commercial purposes only.
* Anders Klemets SM0RGV
* 890918 - Version 2.0
* 900816 - Version 2.1
*
*/
#include "global.h"
#include "config.h"
#include "mbuf.h"
#include "proc.h"
#include "timer.h"
#include "netuser.h"
#include "internet.h"
#include "pktdrvr.h"
#include "ip.h"
#include "iface.h"
#include "ax25.h"
#include "arp.h"
#include "icmp.h"
#include "socket.h"
#include "rspf.h"
extern struct route *rt_lookup __ARGS((int32 target));
#ifdef AX25
extern struct lq *al_lookup __ARGS((struct iface *ifp,char *addr,int sort));
#else
static struct lq *al_lookup __ARGS((struct iface *ifp,char *addr,int sort));
#endif /* AX25 */
struct rspf_stat Rspf_stat;
struct rspfreasm *Rspfreasmq = NULLRREASM;
struct rspfiface *Rspfifaces = NULLRIFACE;
struct rspfadj *Adjs = NULLADJ;
struct rspfrouter *Rspfrouters = NULLRROUTER;
struct mbuf *Rspfinq = NULLBUF;
struct timer Rspfreasmt, Susptimer;
char *Rrh_message = NULLCHAR;
unsigned short Rspfpingmax = 5;
static int Keeprouter = 0;
static int16 Envelopeid = 0;
static int Nrspfproc = 0;
static unsigned char Rspfsubseq = 0;
static short Rspfseq = -32768 + 1;
static char *findlowroute __ARGS((int32 addr,int *low,int add,int32 *resaddr,int *resbits));
static void makeroutes __ARGS((void));
static void partialupdate __ARGS((struct rspfrouter *rr,struct mbuf *bp));
static struct rspfrouter *rr_lookup __ARGS((int32 addr));
static void rr_delete __ARGS((int32 addr));
static struct rspfiface *rtif_lookup __ARGS((int32 addr));
static void rspfcsum __ARGS((struct mbuf **bpp,int32 dest));
static void update_in __ARGS((struct mbuf *bp,int32 addr));
static void node_in __ARGS((struct mbuf *bp,int32 addr,int full));
static void sendonerspf __ARGS((int32 addr,int32 dest));
static void sendtoall __ARGS((struct mbuf *bp,int nodecnt,struct rspfiface *riface));
static int sendupdate __ARGS((struct mbuf *bp,int nodecnt,int32 addr));
static int isnewer __ARGS((short a,short b));
static void del_reasm __ARGS((struct rspfreasm *re));
static void reasmtimeout __ARGS((void *t));
static struct mbuf *rspfreasm __ARGS((struct mbuf *bp,struct rspfpacketh *rph,int32 addr));
static struct mbuf *fragmenter __ARGS((struct mbuf *bp,int nodes,int16 mtu));
static struct mbuf *makeadjupdate __ARGS((int32 addr,struct rspfrouter *rr));
static void pushadj __ARGS((struct mbuf **bpp,int32 addr,int bits));
static void sendrrh __ARGS((struct rspfiface *ri));
static void sendrspf __ARGS((void));
static void goodbadnews __ARGS((struct rspfadj *adj));
static void add_rspfadj __ARGS((struct rspfadj *adj));
static void rspfcheck __ARGS((struct rspfadj *adj));
static void rspfping __ARGS((int oldstate,void *p,void *v));
/* IP passes its RSPF packets to this function */
void
rspf_input(iface,ip,bp,rxbroadcast)
struct iface *iface;
struct ip *ip;
struct mbuf *bp;
int rxbroadcast;
{
struct pseudo_header ph; /* Pseudo-header for checksumming */
if(bp == NULLBUF)
return;
if(Rspfifaces == NULLRIFACE){ /* Rspf main loop is not running */
free_p(bp);
return;
}
ph.length = ip->length - IPLEN - ip->optlen;
ph.source = ip->source;
ph.dest = ip->dest;
ph.protocol = ip->protocol;
if(cksum(&ph,bp,ph.length) != 0){
/* Checksum failed, ignore packet completely */
free_p(bp);
++Rspf_stat.badcsum;
return;
}
bp = pushdown(bp,1 + sizeof(ip->source) + sizeof(iface));
*bp->data = RSPFE_PACKET;
memcpy(bp->data + 1,&ip->source,sizeof(ip->source));
memcpy(bp->data + 1 + sizeof(ip->source),&iface,sizeof(iface));
enqueue(&Rspfinq,bp);
}
/* Main loop in RSPF process */
void
rspfmain(v,v1,v2)
int v;
void *v1,*v2;
{
union rspf rspf; /* Internal packet structure */
struct rspfadj *adj = NULLADJ; /* Adjacency */
struct mbuf *bp, *tbp;
struct rspfiface *riface;
struct iface *iface;
struct route *rp;
int32 source; /* Source address */
int cmd;
for(;;) {
if(Rspfinq == NULLBUF)
pwait(&Rspfinq);
bp = dequeue(&Rspfinq); /* at least 5 bytes */
cmd = PULLCHAR(&bp); /* get internal event indicator */
pullup(&bp,(char *)&source,sizeof(source));
switch(cmd) {
case RSPFE_RRH:
sendrrh((struct rspfiface *)source);
break;
case RSPFE_CHECK:
rspfcheck((struct rspfadj *)source);
break;
case RSPFE_UPDATE:
sendrspf();
break;
case RSPFE_ARP:
adj = (struct rspfadj *) source;
source = adj->addr; /* fall through */
case RSPFE_PACKET:
pullup(&bp,(char *)&iface,sizeof(iface));
break;
}
if(cmd != RSPFE_PACKET && cmd != RSPFE_ARP)
continue;
if(cmd == RSPFE_PACKET && ntohrspf(&rspf,&bp) == -1){
free_p(bp);
continue;
}
if(iface != NULLIF) {
for(riface = Rspfifaces; riface != NULLRIFACE; riface =
riface->next)
if(riface->iface == iface)
break;
}
else
/* fails if there is no route or no RSPF interface */
riface = rtif_lookup(source);
if(riface == NULLRIFACE) {
free_p(bp);
if(cmd == RSPFE_PACKET)
++Rspf_stat.norspfiface;
continue; /* We do not use RSPF on this interface */
}
/* If we dont have an entry in the routing table for this station,
* or if the entry is less than 32 bits specific or has a higher
* cost our new route, and is pointing to the wrong interface,
* then add a correct, private, route.
*/
rp = rt_lookup(source);
if(rp == NULLROUTE || (rp != NULLROUTE && rp->iface !=
riface->iface && (rp->bits < 32 || rp->metric >
riface->quality)))
rt_add(source,32,0,iface,riface->quality,0,1);
if(cmd == RSPFE_ARP) {
if(adj != NULLADJ){
adj->cost = riface->quality; /* default cost */
add_rspfadj(adj);
continue;
}
}
switch(rspf.hdr.type){ /* packet types */
case RSPF_RRH:
++Rspf_stat.rrhin;
free_p(bp);
adj = (struct rspfadj *)callocw(1,sizeof(struct rspfadj));
adj->addr = rspf.rrh.addr;
adj->seq = rspf.rrh.seq;
adj->cost = riface->quality; /* Default cost */
adj->state = RSPF_TENTATIVE;
if(rspf.rrh.flags & RSPFMODE)
adj->tos = DELAY;
else
adj->tos = RELIABILITY;
add_rspfadj(adj);
break;
case RSPF_FULLPKT:
++Rspf_stat.updatein;
/* Feed the packet through the reassembly queue */
if((tbp = rspfreasm(bp,&rspf.pkthdr,source)) != NULLBUF)
update_in(tbp,source);
break;
}
}
}
/* This function is called every time an RRH should be broadcasted.
* An RRH is sent on the interface given by ri or on every RSPF interface.
* The broadcast address of the interface must be routed onto the same
* interface (using a static route for instance) otherwise there will be no
* RRH sent on that interface.
*/
static void
sendrrh(ri)
struct rspfiface *ri; /* interface to use, if specified */
{
struct pseudo_header ph;
struct mbuf *bp, *data = NULLBUF;
struct rspfiface *riface;
struct route *rp;
struct rrh rrh;
for(riface = Rspfifaces; riface != NULLRIFACE; riface = riface->next) {
if(ri != NULLRIFACE && riface != ri)
continue;
if((rp = rt_lookup(riface->iface->broadcast)) != NULLROUTE &&
rp->iface == riface->iface){
rrh.version = RSPF_VERSION;
rrh.addr = riface->iface->addr;
ph.length = 0;
if(Rrh_message != NULLCHAR) {
data = ambufw(strlen(Rrh_message));
memcpy(data->data,Rrh_message,strlen(Rrh_message));
ph.length = data->cnt = strlen(Rrh_message);
}
ph.length += RRHLEN;
ph.source = riface->iface->addr;
ph.dest = riface->iface->broadcast;
ph.protocol = RSPF_PTCL;
/* Start the RRH link level packet counter at 1, since adj->seq
* is 0 only for ARP generated adjacencies. This is also correct
* since rawsndcnt will increase by one when the RRH is sent.
*/
rrh.seq = riface->iface->rawsndcnt + 1;
/* Default is to use the same mode as the interface */
if(Rspfownmode == -1)
rrh.flags = !(riface->iface->flags & CONNECT_MODE);
else
rrh.flags = !(Rspfownmode & CONNECT_MODE);
bp = htonrrh(&rrh,data,&ph);
++Rspf_stat.rrhout;
ip_send(riface->iface->addr,riface->iface->broadcast,RSPF_PTCL,
0,2,bp,0,0,0); /* the ttl will be decremented to 1 */
}
}
}
/* This function is called whenever an RRH packet has been received or when
* a new entry is added to the ARP table.
*/
static void
add_rspfadj(adj)
struct rspfadj *adj;
{
struct rspfadj *oldadj, *tmp;
struct rspfiface *riface;
struct arp_tab *atp;
struct lq *alp;
int dif, origdif;
if(adj == NULLADJ)
return; /* sanity but it happens... */
/* Find the previous copy of the adjacency, if there was any */
/* Insert the new adjacency */
adj->next = Adjs;
Adjs = adj;
for(oldadj = Adjs; oldadj->next != NULLADJ; oldadj = oldadj->next)
if(oldadj->next->addr == adj->addr) {
tmp = oldadj->next;
oldadj->next = oldadj->next->next;
oldadj = tmp;
break;
}
if(oldadj->addr != adj->addr || oldadj == adj)
oldadj = NULLADJ;
/* ARP entries give no sequence number, so save the old one */
if(oldadj != NULLADJ) {
if(adj->seq == 0)
adj->seq = oldadj->seq;
if(adj->tos == 0)
adj->tos = oldadj->tos; /* they give no TOS either */
}
switch(adj->state) {
case RSPF_TENTATIVE:
if(oldadj != NULLADJ) {
/* If the adjacency was in OK state, it will remain there.
* An RRH or ARP upcall can never generate an OK -> Tentative
* transition.
*/
if(oldadj->state == RSPF_OK)
adj->state = RSPF_OK;
adj->okcnt = oldadj->okcnt;
/* If old state was Bad, don't announce this adjacency until
* it is known to be OK, to prevent announcing an adjacency
* with an state transition loop such as OK -> Suspect -> Bad ->
* Tentative -> Suspect -> Bad -> Tentative -> ...
*/
if(oldadj->state == RSPF_BAD) {
adj->okcnt = 0;
stop_timer(&oldadj->timer);
/* Enter Suspect state at once, there is no point in
* dwelling as Tentative.
*/
rspfcheck(adj);
} else {
/* Inherit the old timer */
adj->timer.func = rspfevent;
adj->timer.arg = (void *) &adj->timer;
/* continue where the old timer is stopped */
adj->timer.duration = oldadj->timer.duration;
stop_timer(&oldadj->timer);
/* Set the timer to Suspectimer in the unlikely event that
* the timer was stopped and oldadj is not Suspect or Bad.
* Suspect state is dealt with further below.
*/
if(dur_timer(&adj->timer) == 0L)
set_timer(&adj->timer,dur_timer(&Susptimer));
start_timer(&adj->timer);
set_timer(&adj->timer,dur_timer(&oldadj->timer));
}
/* We must now try to figure out from which AX.25 callsign this
* packet came. Looking at the ARP table may sometimes help.
*/
if(oldadj->seq != 0 && adj->seq != 0 &&
(riface = rtif_lookup(adj->addr)) != NULLRIFACE &&
(atp = arp_lookup(ARP_AX25,adj->addr)) != NULLARP &&
atp->state == ARP_VALID &&
(alp = al_lookup(riface->iface,atp->hw_addr,0)) != NULLLQ){
/* The wrapping of the modulus is taken into account here */
if(oldadj->seq > (MAXINT16 - 100) && adj->seq < 100)
origdif = adj->seq + MAXINT16 - oldadj->seq;
else
origdif = adj->seq - oldadj->seq;
if((alp->currxcnt - adj->heard) > (MAXINT16 - 100)
&& adj->seq < 100)
dif = origdif + MAXINT16 - (alp->currxcnt - adj->heard);
else
dif = origdif - (alp->currxcnt - adj->heard);
adj->heard = alp->currxcnt; /* Update the counter */
/* At this point, "origdif" equals the difference in sequence
* numbers between the two latest RRH packets, i.e. the
* number of AX.25 frames the station has sent during since
* the last RRH packet we heard. "dif" equals the number of
* AX.25 frames that we actually heard from that station
* during the same period.
*/
if(origdif > 0 && dif > 0)
/* This algorithm can be improved, see 2.1 spec */
adj->cost = riface->quality*2-riface->quality*dif/origdif;
}
/* Ignore any new RRH's if a pinger process is already running */
if(oldadj->state == RSPF_SUSPECT) {
if(adj->heard != 0) /* save new heard count */
oldadj->heard = adj->heard;
oldadj->next = adj->next; /* adj is at start of chain */
Adjs = oldadj;
stop_timer(&adj->timer);
free((char *)adj);
return;
}
} else { /* oldadj == NULLADJ */
rspfcheck(adj); /* enter Suspect state */
/* A new adjacency may affect the routing table even though no
* routing updates have yet been received from it.
*/
makeroutes();
}
break;
case RSPF_OK:
if(oldadj != NULLADJ) {
adj->okcnt += oldadj->okcnt; /* Do these before possible */
stop_timer(&oldadj->timer); /* killproc() -- KZ1F */
if(oldadj->state == RSPF_SUSPECT){
killproc(oldadj->pinger);
--Nrspfproc; /* Bug fix - N1BEE */
}
}
else
makeroutes(); /* routing table may change */
if(adj->okcnt == 1) /* A previously unkown route */
goodbadnews(adj); /* ..that is good news */
break;
}
stop_timer(&oldadj->timer); /* stop timer before free() -- KZ1F */
free((char *)oldadj);
}
/* Take appropriate action if an adjacency is Bad or Tentative */
static void
rspfcheck(adj)
struct rspfadj *adj;
{
struct rspfadj *prev;
for(prev = Adjs; prev != NULLADJ; prev = prev->next)
if(prev->next == adj)
break;
switch(adj->state){
case RSPF_OK:
adj->state = RSPF_TENTATIVE; /* note fall-thru */
case RSPF_TENTATIVE:
if (Nrspfproc < RSPF_PROCMAX) {
Nrspfproc++;
adj->pinger = newproc("RSPF ping",1024,rspfping,
(int)adj->state,adj,NULL,0);
adj->state = RSPF_SUSPECT; /* The adjacency is now Suspect */
} else { /* Try later */
set_timer(&adj->timer,dur_timer(&Susptimer));
start_timer(&adj->timer);
}
break;
case RSPF_BAD:
rr_delete(adj->addr);
adj->cost = 255;
if(adj->okcnt != 0)
goodbadnews(adj); /* This is bad news... */
if(prev != NULLADJ)
prev->next = adj->next;
else
Adjs = adj->next;
stop_timer(&adj->timer); /* just in case */
free((char *)adj); /* delete the adjacency */
makeroutes(); /* update the routing table */
break;
}
}
/* Send a ping to a suspect or tentative adjacency */
static void
rspfping(oldstate, p, v)
int oldstate;
void *p, *v;
{
int s, fromlen, pcnt;
struct icmp icmp;
struct rspfadj *adj;
struct sockaddr_in from;
struct mbuf *bp;
pause(((ptol(p) & 7)+1)*1000L); /* Pause for 1 to 8 seconds */
adj = (struct rspfadj *) p;
adj->timer.func = rspfevent; /* Fill in timer values */
adj->timer.arg = (void *) &adj->timer;
set_timer(&adj->timer,dur_timer(&Susptimer));
if((s = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) == -1){
--Nrspfproc;
adj->state = oldstate;
start_timer(&adj->timer);
return;
}
fromlen = sizeof(from);
for(pcnt=0; pcnt < Rspfpingmax; ++pcnt) {
pingem(s,adj->addr,0,(int16)s,0);
/* Now collect the reply */
alarm(30 * 1000L);/* Let each ping timeout after 30 seconds */
for(;;) {
if(recv_mbuf(s,&bp,0,(char *)&from,&fromlen) == -1){
if(errno == EALARM){ /* We timed out */
break;
}
alarm(0);
adj->state = oldstate;
close_s(s);
--Nrspfproc;
start_timer(&adj->timer);
return;
}
ntohicmp(&icmp,&bp);
free_p(bp);
if(icmp.type != ICMP_ECHO_REPLY || from.sin_addr.s_addr !=
adj->addr || icmp.args.echo.id != s)
/* Ignore other people's responses */
continue;
alarm(0);
if(++adj->okcnt == 1)
goodbadnews(adj); /* Good news */
close_s(s);
--Nrspfproc;
start_timer(&adj->timer);
adj->state = RSPF_OK; /* Finally change state */
return;
}
}
/* we give up, mark the adjacency as bad */
adj->state = RSPF_BAD;
close_s(s);
--Nrspfproc;
start_timer(&adj->timer);
}
/* ARP upcalls come in two flavors: When an ARP Reply has been received, we
* know for certain that bidirectional communication is possible with the
* particular station. But when an ARP Request is received, or if an ARP
* entry is added manually, it has yet to be determined if the link is
* bidirectional so iface is NULLIF in those cases.
*/
void
rspfarpupcall(addr,hardware,iface)
int32 addr; /* Address being added to ARP table */
int16 hardware; /* Hardware type */
struct iface *iface; /* Interface used, if known */
{
struct rspfadj *adj;
struct mbuf *bp;
struct rspfiface *riface;
if((riface = rtif_lookup(addr)) == NULLRIFACE)
return; /* Not a route to an RSPF interface */
/* Proceed only if the ARP entry is for a hardware type that is relevant
* for the interface onto which IP datagrams are routed.
*/
switch(hardware) {
case ARP_NETROM:
if(riface->iface->type != CL_NETROM)
return;
break;
case ARP_AX25:
if(riface->iface->type != CL_AX25)
return;
break;
case ARP_ETHER:
if(riface->iface->type != CL_ETHERNET)
return;
break;
case NHWTYPES:
break; /* "Any interface type is ok" type entry */
default:
return;
}
if((adj = (struct rspfadj *)calloc(1,sizeof(struct rspfadj)))==NULLADJ)
return;
adj->addr = addr;
if(iface == NULLIF) /* A manual ARP entry or Request, may be no-good */
adj->state = RSPF_TENTATIVE;
else {
adj->state = RSPF_OK;
adj->okcnt++;
adj->timer.func = rspfevent; /* Fill in timer values */
adj->timer.arg = (void *) &adj->timer;
set_timer(&adj->timer,dur_timer(&Susptimer));
start_timer(&adj->timer);
}
if((bp = alloc_mbuf(1+sizeof(int32)+sizeof(iface))) == NULLBUF) {
stop_timer(&adj->timer);
free((char *)adj);
return;
}
*bp->data = RSPFE_ARP;
memcpy(bp->data + 1, (char *) &adj, sizeof(adj));
memcpy(bp->data + 1 + sizeof(adj), (char *) &iface, sizeof(iface));
bp->cnt = bp->size;
enqueue(&Rspfinq,bp);
}
/* Called by "route add" command. */
void
rspfrouteupcall(addr,bits,gateway)
int32 addr; /* Address added to routing table */
unsigned bits; /* Significant bits in address */
int32 gateway; /* Address of gateway station */
{
/* We are only interested in 32 bit specific routes that use a
* gateway. Direct routes will be discovered anyway.
*/
if(bits != 32 || gateway == 0 || gateway == addr)
return;
if(rtif_lookup(addr) == NULLRIFACE) /* not routed onto RSPF interface */
return;
rspfarpupcall(addr,NHWTYPES,NULLIF); /* proceed as an "arp add" upcall */
}
/* When the suspect timer expires, we scan through the routing table for all
* manual 32 bit specific routes through a gateway that are not an adjacency,
* and calls rspfrouteupcall(). A similar thing is done for all manual ARP
* entries. This will make sure that a station that was not reachable when
* the "route add" or "arp add" command was executed will eventually be
* discovered if it joins the network.
*/
void
rspfsuspect(t)
void *t;
{
struct rspfadj *adj;
struct route *rp;
struct arp_tab *ap;
int i;
start_timer(&Susptimer); /* restart the timer */
for(i = 0; i < HASHMOD; i++) /* Check the routing table */
for(rp = Routes[31][i]; rp != NULLROUTE; rp = rp->next) {
if((rp->flags & RTPRIVATE) || dur_timer(&rp->timer) != 0)
continue; /* not this route */
for(adj = Adjs; adj != NULLADJ; adj = adj->next)
if(adj->addr == rp->target)
break;
if(adj == NULLADJ) /* it is not already an adjacency */
rspfrouteupcall(rp->target,32,rp->gateway);
}
for(i=0; i < HASHMOD; ++i) /* scan the ARP table */
for(ap = Arp_tab[i]; ap != NULLARP; ap = ap->next) {
if(dur_timer(&ap->timer)) /* not a manual entry */
continue;
for(adj = Adjs; adj != NULLADJ; adj = adj->next)
if(adj->addr == ap->ip_addr)
break;
if(adj == NULLADJ) /* not already an adjacency */
rspfarpupcall(ap->ip_addr,ap->hardware,NULLIF);
}
}
/* This non-layered function peeks at the routing table to figure out to which
* interface datagrams for addr will be routed. Then it returns the matching
* rspfiface structure.
*/
static
struct rspfiface *
rtif_lookup(addr)
int32 addr;
{
struct rspfiface *riface;
struct route *rp;
if((rp = rt_lookup(addr)) == NULLROUTE)
return NULLRIFACE;
riface = Rspfifaces;
while(riface != NULLRIFACE){
if(riface->iface == rp->iface)
return riface;
riface = riface->next;
}
return NULLRIFACE;
}
/* Send good or bad news partial routing updates. A cost of 255 should be
* interpreted as bad news by the remote station.
*/
static void
goodbadnews(adj)
struct rspfadj *adj;
{
struct rspfnodeh nodeh;
struct rspflinkh linkh;
struct rspfiface *riface;
struct rspfrouter *rr;
struct mbuf *bp, *tbp, *wbp;
int nodecnt = 1;
if((riface = rtif_lookup(adj->addr)) == NULLRIFACE)
return;
bp = ambufw(5);
bp->cnt = bp->size;
*bp->data = 32; /* the number of significant bits in the address */
put32(bp->data+1,adj->addr);
linkh.horizon = riface->horizon;
linkh.erp = riface->erp;
linkh.cost = adj->cost;
linkh.adjn = 1;
tbp = htonrspflink(&linkh,bp);
nodeh.seq = Rspfseq;
nodeh.subseq = ++Rspfsubseq;
nodeh.links = 1;
for(riface = Rspfifaces; riface != NULLRIFACE; riface = riface->next) {
if(dup_p(&wbp,tbp,0,len_p(tbp)) != len_p(tbp)) {
free_p(wbp);
continue;
}
nodeh.addr = riface->iface->addr;
bp = htonrspfnode(&nodeh,wbp);
sendtoall(bp,1,riface);
}
free_p(tbp);
/* If this is a new adjacency, then send it a routing update immediately */
if(adj->cost == 255 || adj->okcnt != 1)
return;
/* When two RSPF stations learn about each others existence, one of
* them will usually have received an RRH from the other. The one that
* received the RRH will send the peer a routing update immediately.
* So in the code below, if no RRH has been received (seq is 0) and no
* routing update has yet been received, we should expect to receive a
* routing update shortly if the adjacency is running RSPF.
* This could fail though if two RSPF stations learn about each other
* solely through ARP upcalls and no RRH's or Updates were exchanged
* prior to or during the establishment of the adjacency.
*/
if(adj->seq == 0 && rr_lookup(adj->addr) == NULLRROUTER) {
if(adj->state != RSPF_SUSPECT) /* running in RSPF process, give up */
return;
alarm(15 * 1000); /* wait for an Update */
if(pwait(adj) == EALARM)
return; /* still no sign of RSPF activity from the adjacency */
alarm(0);
}
++adj->okcnt; /* we don't want to get here again */
if((bp = makeownupdate(adj->addr,1)) == NULLBUF)
return;
for(rr = Rspfrouters; rr != NULLRROUTER; rr = rr->next)
if((tbp = makeadjupdate(get32(rr->data->data),rr)) != NULLBUF){
append(&bp,tbp);
nodecnt++;
}
sendupdate(bp,nodecnt,adj->addr);
}
/* This function is called whenever it is time to send a new RSPF update */
static void
sendrspf()
{
struct rspfrouter *rr;
struct mbuf *bp, *wbp, *tmp = NULLBUF;
struct rspfiface *riface;
struct rspfadj *adj;
int nodecnt, incr = 1;
for(nodecnt = 1, rr = Rspfrouters; rr != NULLRROUTER; rr = rr->next)
if(!rr->sent) /* don't send stale data */
if((bp = makeadjupdate(get32(rr->data->data),rr)) != NULLBUF){
append(&tmp,bp);
nodecnt++;
}
for(riface = Rspfifaces; riface != NULLRIFACE; riface = riface->next) {
/* Find an address that is routed onto this interface */
for(adj = Adjs; adj != NULLADJ; adj = adj->next)
if(rtif_lookup(adj->addr) == riface)
break;
if(adj == NULLADJ) /* no adjacency on that interface? */
continue;
if(dup_p(&wbp,tmp,0,len_p(tmp)) != len_p(tmp)) {
free_p(wbp);
continue;
}
if((bp = makeownupdate(adj->addr,incr)) != NULLBUF) {
incr = 0; /* sequence number is incremented only once */
append(&bp,wbp);
}
else
break;
sendtoall(bp,nodecnt,riface);
}
free_p(tmp);
for(rr = Rspfrouters; rr != NULLRROUTER; rr = rr->next)
if(!rr->sent) /* Mark router data as propagated */
++rr->sent;
}
/* This function is used to answer "poll" messages */
static void
sendonerspf(addr,dest)
int32 addr; /* address of station whose routing update we will make */
int32 dest; /* address of station to send the update to */
{
struct mbuf *bp;
if(ismyaddr(addr)){
if((bp = makeownupdate(dest,0)) == NULLBUF)
return;
sendupdate(bp,1,dest);
return;
}
if((bp = makeadjupdate(addr,NULLRROUTER)) == NULLBUF)
return;
sendupdate(bp,1,dest);
}
/* send an update to all adjacencies on an RSPF interface */
static void
sendtoall(bp,nodecnt,riface)
struct mbuf *bp;
int nodecnt; /* number of reporting nodes in update */
struct rspfiface *riface; /* interface to sent to */
{
struct rspfadj *adj;
struct mbuf *wbp;
int broad;
for(broad = 0, adj = Adjs; adj != NULLADJ; adj = adj->next)
/* If adj->seq is 0 we have never received an RRH from the
* adjacency, and if there is no stored routing update, then
* it is not known if the adjacency understands RSPF.
*/
if((adj->seq != 0 || rr_lookup(adj->addr) != NULLRROUTER) &&
riface == rtif_lookup(adj->addr)) {
if((adj->tos & RELIABILITY) && !(adj->tos & DELAY)) {
if(dup_p(&wbp,bp,0,len_p(bp)) != len_p(bp)) {
free_p(wbp);
continue;
}
sendupdate(wbp,nodecnt,adj->addr); /* private copy */
}
else
++broad; /* send to broadcast IP address instead */
}
if(broad != 0)
if(dup_p(&wbp,bp,0,len_p(bp)) != len_p(bp))
free_p(wbp);
else
sendupdate(wbp,nodecnt,riface->iface->broadcast);
free_p(bp);
}
/* This function sends a routing update to the adjacencies that we have
* recevied RRH's from.
*/
static int
sendupdate(bp,nodecnt,addr)
struct mbuf *bp; /* Full packet, except for the packet header */
int nodecnt; /* Number of reporting nodes in the packet */
int32 addr; /* Station to send update to */
{
struct rspfadj *adj;
struct mbuf *tmp;
int tos = 0;
/* See if we are sending to a known adjacency, in use its TOS in
* that case.
*/
for(adj = Adjs; adj != NULLADJ; adj = adj->next)
if(adj->addr == addr) {
tos = adj->tos;
break;
}
if((tmp = fragmenter(bp,nodecnt,ip_mtu(addr) - IPLEN)) == NULLBUF)
return -1;
while((bp = dequeue(&tmp)) != NULLBUF){
rspfcsum(&bp,addr); /* Calculate the checksum */
++Rspf_stat.updateout;
ip_send(INADDR_ANY,addr,RSPF_PTCL,INET_CTL | tos,2,bp,0,0,0);
}
return 0;
}
/* Fragment a large mbuf if necessary into packets of maximum mtu bytes.
* Each packet is prepended a RSPF routing update header, without the checksum.
*/
static struct mbuf *
fragmenter(bp,nodes,mtu)
struct mbuf *bp; /* packet to send, without packet header */
int nodes; /* The number of reporting nodes in the routing update */
int16 mtu; /* The maximum transmission unit */
{
struct rspfnodeh nodeh;
struct rspflinkh linkh;
struct rspfpacketh pkth;
struct mbuf *tbp, *tmp, *bpq = NULLBUF;
int n, nodecnt, linkcnt, adjcnt, nodemax, sync;
char *cp, fragn = 1;
if(mtu < RSPFPKTLEN + RSPFNODELEN || bp == NULLBUF) {
free_p(bp);
return NULLBUF;
}
++Envelopeid;
nodemax = nodes; /* total number of nodes in envelope */
nodecnt = nodes; /* nodes left to packetize */
linkcnt = adjcnt = 0;
while(len_p(bp) != 0){
n = min(mtu,len_p(bp)+RSPFPKTLEN); /* length of this packet */
n -= RSPFPKTLEN;
tbp = NULLBUF;
sync = 0;
if(adjcnt){
tbp = ambufw(min(adjcnt*5,n/5*5));
tbp->cnt = tbp->size;
cp = tbp->data;
}
while(n > 0){
while(adjcnt){
if((n -= 5) < 0)
break;
pullup(&bp,cp,5);
cp += 5;
adjcnt--;
}
if(linkcnt && n > 0){
if((n -= RSPFLINKLEN) < 0)
break;
ntohrspflink(&linkh,&bp);
adjcnt = linkh.adjn;
tmp = htonrspflink(&linkh,NULLBUF);
append(&tbp,tmp);
tmp = ambufw(min(5*adjcnt,n/5*5));
tmp->cnt = tmp->size;
cp = tmp->data;
append(&tbp,tmp);
linkcnt--;
continue;
}
if(nodecnt && linkcnt == 0 && n > 0){
if((n -= RSPFNODELEN) < 0)
break;
if(nodecnt == nodes) /* Set the sync octet */
sync = len_p(tbp) + 4;
ntohrspfnode(&nodeh,&bp);
linkcnt = nodeh.links;
tmp = htonrspfnode(&nodeh,NULLBUF);
append(&tbp,tmp);
nodecnt--;
}
}
pkth.version = RSPF_VERSION; /* The version number */
pkth.type = RSPF_FULLPKT; /* The packet type */
pkth.fragn = fragn++; /* The fragment number */
pkth.fragtot = 0; /* The total number of fragments */
pkth.csum = 0; /* The checksum */
pkth.sync = sync < 256 ? sync : 0; /* The sync octet */
pkth.nodes = nodemax; /* The number of nodes in envelope */
pkth.envid = Envelopeid; /* The envelope-ID */
tmp = htonrspf(&pkth,tbp); /* add packet header */
enqueue(&bpq,tmp); /* add packet to chain */
nodes = nodecnt; /* number of nodes left */
}
free_p(bp);
for(tbp = bpq; tbp != NULLBUF; tbp = tbp->anext)
*(tbp->data + 3) = len_q(bpq); /* Set the fragment total counter */
return bpq;
}
/* Calculate the checksum on an RSPF routing update packet */
static void
rspfcsum(bpp,dest)
struct mbuf **bpp;
int32 dest;
{
struct pseudo_header ph;
int16 checksum;
ph.length = len_p(*bpp);
ph.source = locaddr(dest);
ph.dest = dest;
ph.protocol = RSPF_PTCL;
if((checksum = cksum(&ph,*bpp,ph.length)) == 0)
checksum = 0xffffffff;
/* This assumes that the checksum really is at this position */
put16((*bpp)->data+4,checksum);
}
/* This function creates our own routing update and returns it in an mbuf */
struct mbuf *
makeownupdate(dest,new)
int32 dest; /* Address of a station that will get this update */
int new; /* True if the sequence number should be incremented */
{
struct rspfadj *adj;
struct rspflinkh linkh;
struct rspfnodeh nodeh;
struct rspfiface *riface, rifdefault;
struct mbuf *bp = NULLBUF, *tbp, *tmp;
struct route *rp;
int i, adjcnt, lnkcnt, bits;
int32 prev, low, cur;
/* Set default values to apply to non-RSPF interfaces */
rifdefault.horizon = 32;
rifdefault.erp = 0;
rifdefault.quality = 0;
/* Our adjacencies has to be sorted into groups of the same horizon,
* ERP factor and cost. This is done by combining these numbers into a
* single one, modulo 256 and take the lowest number first.
*/
low = 0;
lnkcnt = 0;
for(;;){
prev = low;
low = 255*65536L+255*256+255;
for(adj = Adjs; adj != NULLADJ; adj = adj->next)
/* Include all adjacecies that have been or are in OK state
* in the update. Bad adjacencies are also included to give
* them a chance to recover. Hopelessly Bad adjacencies are
* eventually deleted elsewhere.
*/
if(adj->okcnt != 0 && (riface = rtif_lookup(adj->addr)) !=
NULLRIFACE) {
cur = riface->horizon*65536L+riface->erp*256+adj->cost;
if(cur > prev && cur < low)
low = cur;
}
/* Add any manual public, 1-31 bit specific routes.
* Use the route metric only if it is greater than the default
* quality to lessen a possible "wormhole" effect.
*/
for(bits = 1; bits <= 31; bits++)
for(i = 0; i < HASHMOD; i++)
for(rp = Routes[bits-1][i]; rp != NULLROUTE; rp = rp->next)
if(!(rp->flags & RTPRIVATE) && !dur_timer(&rp->timer)) {
if((riface = rtif_lookup(rp->target)) == NULLRIFACE)
riface = &rifdefault;
cur = riface->horizon*65536L+riface->erp*256+
(rp->metric > riface->quality ? rp->metric :
riface->quality);
if(cur > prev && cur < low)
low = cur;
}
/* Add any 32 bit routes on interfaces not using RSPF */
for(i = 0; i < HASHMOD; i++)
for(rp = Routes[31][i]; rp != NULLROUTE; rp = rp->next)
if(!(rp->flags & RTPRIVATE) && rtif_lookup(rp->target)
== NULLRIFACE) {
cur = rifdefault.horizon*65536L+rifdefault.erp*256+
(rp->metric > rifdefault.quality ? rp->metric :
rifdefault.quality);
if(cur > prev && cur < low)
low = cur;
}
/* Add the default route */
if((rp = rt_blookup(0,0)) != NULLROUTE && !(rp->flags & RTPRIVATE) &&
!dur_timer(&rp->timer)) {
if((riface = rtif_lookup(0)) == NULLRIFACE)
riface = &rifdefault;
cur = riface->horizon*65536L+riface->erp*256+
(rp->metric > riface->quality ? rp->metric : riface->quality);
if(cur > prev && cur < low)
low = cur;
}
if(low == 255*65536L+255*256+255) /* All done */
break;
/* now start adding the routes */
adjcnt = 0;
tbp = NULLBUF;
for(adj = Adjs; adj != NULLADJ; adj = adj->next)
if(adj->okcnt != 0 && (riface = rtif_lookup(adj->addr)) !=
NULLRIFACE)
if(riface->horizon*65536L+riface->erp*256+adj->cost == low){
pushadj(&tbp,adj->addr,32);
adjcnt++;
}
for(bits = 1; bits <= 31; bits++)
for(i = 0; i < HASHMOD; i++)
for(rp = Routes[bits-1][i]; rp != NULLROUTE; rp = rp->next)
if(!(rp->flags & RTPRIVATE) && !dur_timer(&rp->timer)){
if((riface = rtif_lookup(rp->target)) == NULLRIFACE)
riface = &rifdefault;
/* Manually entered local routes only */
if(riface->horizon*65536L+riface->erp*256+
(rp->metric > riface->quality ? rp->metric :
riface->quality) == low){
pushadj(&tbp,rp->target,bits);
adjcnt++;
}
}
for(i = 0; i < HASHMOD; i++) /* 32 bit specific routes */
for(rp = Routes[31][i]; rp != NULLROUTE; rp = rp->next)
if(!(rp->flags & RTPRIVATE) && rtif_lookup(rp->target)
== NULLRIFACE)
if(rifdefault.horizon*65536L+rifdefault.erp*256+
(rp->metric > rifdefault.quality ? rp->metric :
rifdefault.quality) == low){
pushadj(&tbp,rp->target,bits);
adjcnt++;
}
/* Add the default route */
if((rp = rt_blookup(0,0)) != NULLROUTE && !(rp->flags & RTPRIVATE) &&
!dur_timer(&rp->timer)) {
if((riface = rtif_lookup(0)) == NULLRIFACE)
riface = &rifdefault;
if(riface->horizon*65536L+riface->erp*256+ (rp->metric >
riface->quality ? rp->metric : riface->quality) == low){
pushadj(&tbp,0,0);
adjcnt++;
}
}
if(adjcnt != 0){
/* Prepend the link header */
linkh.horizon = ((low >> 16) & 255);/* Horizon value */
linkh.erp = ((low >> 8) & 255); /* ERP factor */
linkh.cost = (low & 255); /* Link cost */
linkh.adjn = adjcnt; /* Number of adjacencies */
tmp = htonrspflink(&linkh,tbp);
append(&bp,tmp);
lnkcnt++;
}
}
/* Prepend the node header */
if(lnkcnt != 0){
/* Set our address to the IP address used on the destinations
* interface.
*/
if(dest == INADDR_ANY || (riface = rtif_lookup(dest)) == NULLRIFACE)
nodeh.addr = Ip_addr;
else
nodeh.addr = riface->iface->addr;
if(new){ /* increase sequence number, clear subseq. number */
if(Rspfseq == 32768 - 2 || Rspfseq == -32768 + 1)
Rspfseq = 0; /* wrap around */
else
++Rspfseq;
Rspfsubseq = 0;
}
nodeh.seq = Rspfseq;
nodeh.subseq = 0;
nodeh.links = lnkcnt;
return htonrspfnode(&nodeh,bp);
}
else {
free_p(bp);
return NULLBUF;
}
}
/* Prepends an adjacency to bpp. Allocates bpp in large chunk for efficency */
static void
pushadj(bpp,addr,bits)
struct mbuf **bpp;
int32 addr;
int bits;
{
if(bpp == NULLBUFP)
return;
if(*bpp == NULLBUF) {
*bpp = ambufw(60); /* good for 12 adjacencies */
(*bpp)->data += 55;
(*bpp)->cnt = 5;
}
else
*bpp = pushdown(*bpp,5);
*(*bpp)->data = bits;
put32((*bpp)->data+1,addr);
}
/* This function returns the latest routing update in network fromat from
* the adjacency denoted by addr.
*/
static struct mbuf *
makeadjupdate(addr,rr)
int32 addr;
struct rspfrouter *rr; /* pointer to stored routing entry, if known */
{
struct mbuf *tmp, *tmp2, *tmp3, *bp = NULLBUF;
struct rspfnodeh nodeh;
struct rspflinkh linkh;
int linkcnt = 0;
if(rr == NULLRROUTER && (rr = rr_lookup(addr)) == NULLRROUTER)
return NULLBUF;
if(dup_p(&tmp,rr->data,0,len_p(rr->data)) != len_p(rr->data)) {
free_p(tmp);
return NULLBUF;
}
ntohrspfnode(&nodeh,&tmp); /* Get the node header */
while(nodeh.links--){
ntohrspflink(&linkh,&tmp);
/* Decrement and check the horizon value */
if(--linkh.horizon > 0){
/* Duplicate the adjacencies */
if(dup_p(&tmp2,tmp,0,5*linkh.adjn) != 5*linkh.adjn){
free_p(tmp);
free_p(tmp2);
free_p(bp);
return NULLBUF;
}
/* Prepend the link header */
tmp3 = htonrspflink(&linkh,tmp2);
append(&bp,tmp3);
linkcnt++;
}
pullup(&tmp,NULLCHAR,5*linkh.adjn); /* Skip the adjacencies */
}
free_p(tmp);
if(linkcnt == 0){ /* No links at all? */
free_p(bp);
return NULLBUF;
}
nodeh.subseq = rr->subseq;
nodeh.links = linkcnt;
/* Prepend the node header */
return htonrspfnode(&nodeh,bp);
}
/* Returns 1 if sequence number a is newer than b. Sequence numbers start
* at -32768 + 1 and then continues with 0 and the positive integer numbers
* until reaching 32768 - 2 after which they continue with 0.
* Algorithm taken from RFC-1131 but fixed bug when a or b is 0.
*/
static int
isnewer(a,b)
short a,b;
{
if((b < 0 && a > b) ||
(a >= 0 && b >= 0 && /* bug corrected here */
(((32768 - 1)/2 > (a-b) && (a-b) > 0) ||
(a-b) < -(32768 - 1)/2)))
return 1;
return 0;
}
/* Reassemble possibly fragmented packets into a single large one */
static struct mbuf *
rspfreasm(bp,rph,addr)
struct mbuf *bp; /* Routing update packet without packet header */
struct rspfpacketh *rph; /* Packet header */
int32 addr; /* Address of station we got the packet from */
{
union rspf rspf;
struct rspfreasm *re, *rtmp;
struct mbuf *tbp, *bp1, *bp2;
for(re = Rspfreasmq; re != NULLRREASM; re = re->next)
if(re->addr == addr){ /* found another fragment */
if(dup_p(&tbp,re->data,0,RSPFPKTLEN) != RSPFPKTLEN) {
free_p(tbp);
free_p(bp);
return NULLBUF;
}
ntohrspf(&rspf,&tbp); /* get its packet header */
if(rph->envid != rspf.pkthdr.envid) {
del_reasm(re); /* an obsolete entry, delete it */
break;
}
/* Now try to find a place where this fragment fits in the chain */
bp1 = re->data;
bp2 = NULLBUF;
while(rph->fragn > rspf.pkthdr.fragn && bp1->anext != NULLBUF){
bp2 = bp1;
bp1 = bp1->anext;
if(dup_p(&tbp,bp1,0,RSPFPKTLEN) != RSPFPKTLEN) {
free_p(bp);
free_p(tbp);
return NULLBUF;
}
ntohrspf(&rspf,&tbp);
}
if(rspf.pkthdr.fragn == rph->fragn) {
free_p(bp);
return NULLBUF; /* We already had this one */
}
bp = htonrspf(rph,bp); /* Put the packet header back on */
/* Insert the fragment at the right place in the chain */
if(rph->fragn > rspf.pkthdr.fragn){
bp1->anext = bp;
bp->anext = NULLBUF;
}
else {
if(bp2 != NULLBUF)
bp2->anext = bp;
else
re->data = bp;
bp->anext = bp1;
}
if(len_q(re->data) == rspf.pkthdr.fragtot){
bp1 = NULLBUF; /* we have all the fragments */
while((bp2 = dequeue(&re->data)) != NULLBUF){
pullup(&bp2,NULLCHAR,RSPFPKTLEN); /* strip the headers */
append(&bp1,bp2); /* and form a single packet */
}
del_reasm(re);
stop_timer(&Rspfreasmt);
reasmtimeout(NULL); /* restarts timer if necessary */
return bp1; /* return the completed packet */
}
re->time = secclock(); /* Update the timestamp */
goto timstart; /* We have to wait for fragments */
}
/* At this point we know that there is no matching entry in the
reassembly queue (any more) */
if(rph->fragtot == 1) /* Simple case, an un-fragmented packet */
return bp;
tbp = htonrspf(rph,bp); /* Put the packet header back on */
rtmp = (struct rspfreasm *) callocw(1,sizeof(struct rspfreasm));
/* The values are filled in */
rtmp->addr = addr;
rtmp->time = secclock();
rtmp->data = tbp;
rtmp->next = Rspfreasmq;
Rspfreasmq = rtmp;
timstart: /* Start the timer if needed */
if(!run_timer(&Rspfreasmt)){
Rspfreasmt.func = reasmtimeout;
Rspfreasmt.arg = NULL;
set_timer(&Rspfreasmt,RSPF_RTIME*1000L); /* The reassembly timeout */
start_timer(&Rspfreasmt);
}
return NULLBUF;
}
/* Delete a reassembly descriptor from the queue */
static void
del_reasm(re)
struct rspfreasm *re;
{
struct rspfreasm *r, *prev = NULLRREASM;
for(r = Rspfreasmq; r != NULLRREASM; prev = r, r = r->next)
if(r == re){
free_q(&re->data);
if(prev != NULLRREASM)
prev->next = re->next;
else
Rspfreasmq = re->next;
free((char *)re);
break;
}
}
/* When the reassembly timer times out, this function tries to make use of
* any fragments in the reassembly queue.
*/
static void
reasmtimeout(t)
void *t;
{
union rspf rspf;
struct rspfreasm *re;
struct mbuf *bp, *tbp;
int last = 0;
int32 low;
re = Rspfreasmq;
while(re != NULLRREASM)
if((secclock() - re->time) >= RSPF_RTIME){
/* Try to use what we have */
bp = NULLBUF;
while((tbp = dequeue(&re->data)) != NULLBUF){
ntohrspf(&rspf,&tbp);
if(rspf.pkthdr.fragn != (last+1)){ /* a missing fragment */
if(bp != NULLBUF)
update_in(bp,re->addr);
bp = NULLBUF;
if(rspf.pkthdr.sync != 0)
pullup(&tbp,NULLCHAR,rspf.pkthdr.sync - 4);
else { /* no sync possible in this fragment */
free_p(tbp);
continue;
}
}
append(&bp,tbp);
last = rspf.pkthdr.fragn;
}
if(bp != NULLBUF)
update_in(bp,re->addr);
del_reasm(re);
re = Rspfreasmq; /* start over again */
}
else
re = re->next;
/* Find the oldest fragment and restart the reassembly timer */
low = 0;
for(re = Rspfreasmq; re != NULLRREASM; re = re->next)
if(re->time < low || low == 0)
low = re->time;
if(low){
set_timer(&Rspfreasmt,RSPF_RTIME*1000L - clock() + low);
if(dur_timer(&Rspfreasmt) > 0) /* just to be safe */
start_timer(&Rspfreasmt);
}
}
/* Handle incoming routing updates (a reassembled envelope) */
static void
update_in(bp,addr)
struct mbuf *bp; /* Reassembled data packet, without packet header */
int32 addr; /* Senders address (in host order) */
{
struct rspfnodeh nodeh;
struct rspflinkh linkh;
struct rspfadj *adj;
struct mbuf *tbp, *tmp, *b;
int linkcnt = 0, adjcnt = 0;
int16 offset = 0;
tbp = b = NULLBUF;
/* Check to see if the sender is an adjacency */
for(adj = Adjs; adj != NULLADJ; adj = adj->next)
if(adj->addr == addr)
break;
/* One may argue that updates from non-adjacencies should not be
* accepted since they will not contribute to the routing table.
* But it might happen that the sender will very shortly become an
* adjacency, and then its routing update will come handy. By increasing
* Keeprouter, routing updates from disjoint routers will not be not be
* purged when makeroutes() is called this time.
*/
if(adj == NULLADJ) {
++Rspf_stat.noadjupdate;
Keeprouter += 2;
}
else
psignal(adj,1); /* alert anyone waiting for the update */
while(offset < len_p(bp)){
if(adjcnt){
if(dup_p(&tmp,bp,offset,5) == 5){
append(&tbp,tmp);
offset += 5;
adjcnt--;
continue;
}
else break;
}
if(tbp != NULLBUF){
tmp = htonrspflink(&linkh,tbp);
append(&b,tmp);
tbp = NULLBUF;
}
if(linkcnt){
if(dup_p(&tbp,bp,offset,RSPFLINKLEN) == RSPFLINKLEN){
ntohrspflink(&linkh,&tbp);
adjcnt = linkh.adjn;
offset += RSPFLINKLEN;
linkcnt--;
continue;
}
else
break;
}
if(b != NULLBUF){
tmp = htonrspfnode(&nodeh,b);
node_in(tmp,addr,1); /* a full router update */
b = NULLBUF;
}
if(dup_p(&tmp,bp,offset,RSPFNODELEN) == RSPFNODELEN){
ntohrspfnode(&nodeh,&tmp);
linkcnt = nodeh.links;
offset += RSPFNODELEN;
}
else {
free_p(bp);
free_p(tmp);
return;
}
}
if(tbp != NULLBUF){
/* adjust the adjacency counter in the link header */
linkh.adjn -= adjcnt;
tmp = htonrspflink(&linkh,tbp);
append(&b,tmp);
}
/* adjust the link counter in the node header */
nodeh.links -= linkcnt;
tmp = htonrspfnode(&nodeh,b);
free_p(bp);
if(linkcnt || adjcnt)
node_in(tmp,addr,0); /* a partial router update */
else
node_in(tmp,addr,1);
}
static void
node_in(bp,addr,full)
struct mbuf *bp; /* A single update, starting with the node header */
int32 addr; /* Address of station we got packet from */
int full; /* False if we got a partial update */
{
struct mbuf *tbp;
struct rspfnodeh nodeh, rnodeh;
struct rspfrouter *rr;
if(dup_p(&tbp,bp,0,RSPFNODELEN) != RSPFNODELEN) {
free_p(bp);
free_p(tbp);
return;
}
ntohrspfnode(&nodeh,&tbp);
if(ismyaddr(nodeh.addr)){
/* If another station thinks our routing update sequence number is
* larger than it really is, this might be because we have had
* a fast system reset and routing updates from the old "epoch"
* are still present in the network.
*/
if(isnewer(nodeh.seq,Rspfseq)) {
Rspfseq = nodeh.seq + 1; /* Catch up */
if(nodeh.seq == 32768 - 2 || nodeh.seq == -32768 + 1)
Rspfseq = 0; /* Wrap around */
sendonerspf(nodeh.addr,addr); /* Send him the latest packet */
}
free_p(bp); /* We already know our own adjacencies! */
return;
}
if((rr = rr_lookup(nodeh.addr)) != NULLRROUTER) {
if(dup_p(&tbp,rr->data,0,RSPFNODELEN) != RSPFNODELEN) {
free_p(bp);
free_p(tbp);
return;
}
ntohrspfnode(&rnodeh,&tbp);
if(nodeh.seq == rnodeh.seq && nodeh.subseq <= rr->subseq){
free_p(bp);
return; /* We already have this one */
}
if(isnewer(rnodeh.seq,nodeh.seq)){
/* Send him the latest packet */
sendonerspf(nodeh.addr,addr);
free_p(bp);
++Rspf_stat.oldreport;
return;
}
if(nodeh.subseq > rr->subseq && nodeh.seq == rnodeh.seq){
rr->subseq = nodeh.subseq;
partialupdate(rr,bp);
makeroutes();
return;
}
if(isnewer(nodeh.seq,rnodeh.seq) && !full){
partialupdate(rr,bp);
makeroutes();
/* Send a "poll" packet */
--nodeh.seq;
nodeh.subseq = 0;
nodeh.links = 0;
tbp = htonrspfnode(&nodeh,NULLBUF);
sendupdate(tbp,1,addr);
++Rspf_stat.outpolls;
return;
}
}
else {
rr = (struct rspfrouter *) callocw(1,sizeof(struct rspfrouter));
rr->next = Rspfrouters;
Rspfrouters = rr;
}
free_p(rr->data);
rr->data = bp;
rr->subseq = nodeh.subseq;
rr->time = secclock();
rr->sent = 0;
makeroutes();
return;
}
/* Return the matching RSPF route entry for addr (in host order) */
static struct rspfrouter *
rr_lookup(addr)
int32 addr;
{
struct rspfrouter *rr;
for(rr = Rspfrouters; rr != NULLRROUTER; rr = rr->next)
/* this assumes that the first word of the header is the address */
if(rr->data != NULLBUF && get32(rr->data->data) == addr)
return rr;
return NULLRROUTER;
}
/* Delete the route entry for address addr */
static void
rr_delete(addr)
int32 addr;
{
struct rspfrouter *rr, *prev = NULLRROUTER;
for(rr = Rspfrouters; rr != NULLRROUTER; prev = rr, rr = rr->next)
/* this assumes that the first word of the header is the address */
if(rr->data != NULLBUF && get32(rr->data->data) == addr) {
if(prev != NULLRROUTER)
prev->next = rr->next;
else
Rspfrouters = rr->next;
free_p(rr->data);
free((char *)rr);
return;
}
}
/* Handle incoming partial routing updates. Adjacencies from bp will be
* merged into rr->data
*/
static void
partialupdate(rr,bp)
struct rspfrouter *rr; /* current node entry in routing table */
struct mbuf *bp; /* data packet, starting with node header */
{
struct rspflinkh linkh, rlinkh;
struct rspfnodeh rnodeh;
struct mbuf *wbp, *tbp, *tmp, *b;
int adjcnt = 0, radjcnt, rlinkcnt = 0, bits, rbits, added = 0;
int32 addr, raddr;
rlinkh.adjn = 0;
rr->time = secclock();
rr->sent = 0;
/* Make a working copy of the stored routing update */
if(dup_p(&wbp,rr->data,0,len_p(rr->data)) != len_p(rr->data)) {
free_p(wbp);
free_p(bp);
return;
}
ntohrspfnode(&rnodeh,&wbp);
pullup(&bp,NULLCHAR,RSPFNODELEN); /* Strip off the node header */
while(len_p(bp) > 0)
if(adjcnt == 0) {
if(ntohrspflink(&linkh,&bp) == -1) {
free_p(wbp);
free_p(bp);
return;
}
adjcnt = linkh.adjn;
}
else {
bits = PULLCHAR(&bp); /* get one adjacency to merge */
if(pullup(&bp,(char *)&addr,4) != 4) {
free_p(wbp);
free_p(bp);
return;
}
addr = get32((char *)&addr); /* convert to host order */
--adjcnt;
radjcnt = 0;
b = tbp = NULLBUF;
for(;;) { /* search through stored update */
if(radjcnt == 0 || len_p(wbp) == 0) {
if(tbp != NULLBUF){
rlinkh.adjn -= radjcnt;
tmp = htonrspflink(&rlinkh,tbp);
++rlinkcnt;
append(&b,tmp);
tbp = NULLBUF;
}
if(len_p(wbp) == 0)
break;
}
if(radjcnt == 0) {
ntohrspflink(&rlinkh,&wbp);
radjcnt = rlinkh.adjn;
if(rlinkh.horizon == linkh.horizon && rlinkh.cost ==
linkh.cost && rlinkh.erp == linkh.erp) {
pushadj(&tbp,addr,bits);
++rlinkh.adjn;
added = 1;
}
}
else {
rbits = PULLCHAR(&wbp);
raddr = pull32(&wbp);
--radjcnt;
if(bits != rbits || addr != raddr)
pushadj(&tbp,raddr,rbits); /* Put it back */
else
--rlinkh.adjn; /* deleted one adjacency */
}
}
wbp = b; /* wbp now keeps link headers and adjacencies */
if(linkh.cost < 255 && !added){ /* Append the new link */
++rnodeh.links; /* We will add one extra link */
tmp = ambufw(5);
*tmp->data = bits;
put32(tmp->data+1,addr);
tmp->cnt = tmp->size;
tbp = htonrspflink(&linkh,tmp);
append(&wbp,tbp);
}
added = 0;
}
free_p(rr->data);
rnodeh.links = rlinkcnt;
rr->data = htonrspfnode(&rnodeh,wbp);
}
/* The shortest path first algorithm */
static void
makeroutes()
{
int bits, i, low, adjcnt;
struct mbuf *bp;
struct route *rp, *rp2, *saved[HASHMOD];
struct rspfadj *adj, *lowadj, *gateway;
char *lowp, *r;
int32 lowaddr;
struct rspflinkh linkh;
struct rspfrouter *rr, *rrprev;
if(Keeprouter) /* if false, purge unreachable router entries */
--Keeprouter;
/* Before we change anything in the routing table, we have to save
* each local adjacencies riface pointer
*/
for(adj = Adjs; adj != NULLADJ; adj = adj->next)
adj->scratch = (void *) rtif_lookup(adj->addr);
/* Remove all non-manual routes */
for(bits = 1; bits <= 32; bits++)
for(i = 0; i < HASHMOD; i++)
for(rp = Routes[bits-1][i]; rp != NULLROUTE; rp = rp->next)
if(dur_timer(&rp->timer) != 0)
rt_drop(rp->target,bits);
if((rp = rt_blookup(0,0)) != NULLROUTE && dur_timer(&rp->timer) != 0)
rt_drop(0,0); /* delete non-manual default route */
/* Temporarily remove all 32-bit specific manual routes. This will make
* it possible to prevent loops in findlowroute()
*/
for(i = 0; i < HASHMOD; i++) {
saved[i] = Routes[31][i];
Routes[31][i] = NULLROUTE;
}
for(;;) {
lowadj = NULLADJ;
lowp = NULLCHAR;
low = 255;
for(adj = Adjs; adj != NULLADJ; adj = adj->next){
if(adj->scratch == NULL)
continue; /* skip unreachable adjacency */
if(!adj->added){
if(adj->cost <= low){
low = adj->cost;
lowadj = adj;
lowp = NULLCHAR;
}
}
else
if((r = findlowroute(adj->addr,&low,adj->cost,&lowaddr,&bits))
!= NULLCHAR) {
lowp = r;
gateway = adj;
lowadj = NULLADJ;
}
}
if(lowadj != NULLADJ){
lowadj->added++;
rp = rt_add(lowadj->addr,32,0,
((struct rspfiface *)lowadj->scratch)->iface,
(int32)lowadj->cost,0,0);
/* set the timer to a dummy value. This makes it possible to
* distinguish between manual and RSPF-generated routes.
* The timer is never started however since RSPF handles the
* expiration of routes itself.
*/
set_timer(&rp->timer,MSPTICK);
continue;
}
if(lowp != NULLCHAR){
/* Check if we already have this one */
rp = rt_blookup(lowaddr,bits);
if((rp == NULLROUTE || (rp != NULLROUTE &&
rp->metric > (int32) low)) && !ismyaddr(lowaddr)) {
/* This is a new route, or a route with strict lower cost than
* the previous route (possible only if it was manual)
*/
rp = rt_add(lowaddr,bits,gateway->addr,
((struct rspfiface *)gateway->scratch)->iface,
(int32)low,0,0);
set_timer(&rp->timer,MSPTICK); /* a dummy value */
}
*lowp |= 128; /* mark the route as added in any case */
}
else
break; /* no more routes */
}
/* Add the saved 32 bit routes, if there isn't now a better route */
for(i = 0; i < HASHMOD; i++) {
rp = saved[i];
while(rp != NULLROUTE) {
rp2 = rt_blookup(rp->target,32);
if(rp2 == NULLROUTE || (rp2 != NULLROUTE && rp2->metric >= rp->metric)) {
rp2 = rt_add(rp->target,32,rp->gateway,rp->iface,rp->metric,
0,rp->flags & RTPRIVATE);
rp2->uses = rp->uses;
}
rp2 = rp->next;
stop_timer(&rp->timer); /* Stop timer before free() -- KZ1F */
free((char *)rp);
rp = rp2;
}
}
/* Reset all flags */
for(adj = Adjs; adj != NULLADJ; adj = adj->next) {
adj->added = 0;
adj->scratch = NULL;
}
for(rr = Rspfrouters; rr != NULLRROUTER; rr = rr->next){
i = len_p(rr->data) - RSPFNODELEN;
/* jump past the node header */
if(dup_p(&bp,rr->data,RSPFNODELEN,i) != i) {
free_p(bp);
continue;
}
adjcnt = 0;
while(i > 0){
if(adjcnt){
if(!Keeprouter && (*bp->data & 128) == 0) {
/* This router has an adjacency that was not added. That
* means that the router is unreachable. Mark the
* stored routing update for deletion.
*/
free_p(bp);
free_p(rr->data);
rr->data = NULLBUF; /* indicates disposal */
break;
}
*bp->data &= ~128; /* Clear the "added" bit */
pullup(&bp,NULLCHAR,5);
i -= 5;
--adjcnt;
continue;
}
ntohrspflink(&linkh,&bp);
adjcnt = linkh.adjn;
i -= RSPFLINKLEN;
}
}
if(Keeprouter) /* nothing more to do */
return;
/* Delete any routers that were discovered as being unreachable */
rrprev = NULLRROUTER;
rr = Rspfrouters;
for(;;) {
for(rrprev = NULLRROUTER, rr = Rspfrouters; rr != NULLRROUTER;
rrprev = rr, rr = rr->next)
if(rr->data == NULLBUF) {
if(rrprev != NULLRROUTER)
rrprev->next = rr->next;
else
Rspfrouters = rr->next;
free((char *)rr);
break;
}
if(rr == NULLRROUTER)
break;
}
}
/* Find a route from addr with the lowest cost. Returns a pointer to the
* buffer that keeps the significant bit count of the selected route.
*/
static char *
findlowroute(addr,low,add,resaddr,resbits)
int32 addr; /* The node whose routes will be examined */
int *low; /* Lowest cost so far */
int add; /* Cost of this node */
int32 *resaddr; /* Resulting route stored here */
int *resbits; /* Significant bits of resulting route */
{
struct mbuf *bp;
struct rspfrouter *rr;
struct rspflinkh linkh;
struct route *rp;
char *r, *retval = NULLCHAR;
int adjcnt = 0;
linkh.cost = 0;
if((rr = rr_lookup(addr)) == NULLRROUTER)
return NULLCHAR;
if((rp = rt_blookup(addr,32)) != NULLROUTE && rp->metric < add)
return NULLCHAR; /* already added this one, no loops thanks */
if(dup_p(&bp,rr->data,RSPFNODELEN,len_p(rr->data) - RSPFNODELEN) !=
len_p(rr->data) - RSPFNODELEN) {
free_p(bp);
return NULLCHAR;
}
while(len_p(bp) > 0){
if(adjcnt){
if(*bp->data & 128) {
(void) PULLCHAR(&bp);
if((r = findlowroute(pull32(&bp),low,add+linkh.cost,resaddr,
resbits)) != NULLCHAR)
retval = r;
}
else {
*low = add + linkh.cost;
retval = bp->data;
*resbits = PULLCHAR(&bp);
*resaddr = pull32(&bp);
pullup(&bp,NULLCHAR,5*(adjcnt-1));
adjcnt = 1; /* No need to check the rest of this link */
}
--adjcnt;
continue;
}
ntohrspflink(&linkh,&bp);
if((add + linkh.cost) <= *low)
adjcnt = linkh.adjn;
else
pullup(&bp,NULLCHAR,5*linkh.adjn);
}
return retval;
}
#ifndef AX25
/*
* Dummy stub for RSPF if AX25 is not configured.
*/
static struct lq *
al_lookup(ifp,addr,sort)
struct iface *ifp;
char *addr;
int sort;
{
return NULLLQ;
}
#endif /* AX25 */