/*		     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 "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));
extern struct lq *al_lookup __ARGS((struct iface *ifp,char *addr));
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;
	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;		/* 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) {
		  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;
    /* 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.start = read_timer(&oldadj->timer);
		  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(adj->timer.start == 0)
		       adj->timer.start = dur_timer(&Susptimer);
		  start_timer(&adj->timer);
		  adj->timer.start = oldadj->timer.start;
	    }
	    /* 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)) != 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) {
	    if(oldadj->state == RSPF_SUSPECT)
		killproc(oldadj->pinger);
	    adj->okcnt += oldadj->okcnt;
	    stop_timer(&oldadj->timer);
	}
	else
	     makeroutes();			/* routing table may change */
	if(adj->okcnt == 1)			/* A previously unkown route */
	    goodbadnews(adj);			/* ..that is good news */
	break;
    }
    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 */
	      adj->timer.start = 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)*1000/MSPTICK);	/* Pause for 1 to 8 seconds */
    adj = (struct rspfadj *) p;
    adj->timer.func = rspfevent;		/* Fill in timer values */
    adj->timer.arg = (void *) &adj->timer;
    adj->timer.start = 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 * 1000/MSPTICK);/* 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;
	adj->timer.start = 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 < ARPSIZE; ++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/MSPTICK);	/* 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*65536+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*65536+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*65536+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*65536+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*65536+riface->erp*256+
		(rp->metric > riface->quality ? rp->metric : riface->quality);
	     if(cur > prev && cur < low)
		  low = cur;
	}
	if(low == 255*65536+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*65536+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*65536+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*65536+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*65536+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 = Clock;		/* 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 = Clock;
    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*1000);	/* 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((Clock - re->time) >= RSPF_RTIME*1000/MSPTICK){
	    /* 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){
	Rspfreasmt.start = RSPF_RTIME*1000/MSPTICK - Clock + low;
	if(Rspfreasmt.start > 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 = Clock;
    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 = Clock;
    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;
	     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;
}
