/*
 * slsp_db.c,v 1.12 1994/02/25 22:10:32 jch Exp
 */

/* Gated Release 3.5 */
/* Copyright (c) 1990,1991,1992,1993,1994 by Cornell University. All rights */
/* reserved. Refer to Particulars and other Copyright notices at the end of this */
/* file. */

#include "include.h"
#include "inet.h"
#include "slsp_proto.h"
#include "slsp.h"
#include "slsp_var.h"


/*
 * Number of LSPs we send per second.
 */
#define	SLSP_DB_MAX_XMT		20

/*
 * Memory allocation.  We allocate AS and link structures and
 * the database node structures themselves.
 */
static block_t slsp_db_as_block = (block_t) 0;
static block_t slsp_db_link_block = (block_t) 0;
static block_t slsp_db_node_block = (block_t) 0;
static block_t slsp_db_fsq_block = (block_t) 0;

/*
 * Allocate them.
 */
#define	SLSP_DB_AS_ALLOC() \
    ((slsp_db_as *) task_block_alloc(slsp_db_as_block))

#define	SLSP_DB_LINK_ALLOC() \
    ((slsp_db_link *) task_block_alloc(slsp_db_link_block))

#define	SLSP_DB_NODE_ALLOC() \
    ((slsp_db_node *) task_block_alloc(slsp_db_node_block))

#define	SLSP_DB_FSQ_ALLOC() \
    ((slsp_full_seq_q *) task_block_alloc(slsp_db_fsq_block))

/*
 * Initialize them
 */
#define	SLSP_DB_AS_INIT() \
    (slsp_db_as_block = task_block_init(sizeof(slsp_db_as), "slsp_db_as"))

#define	SLSP_DB_LINK_INIT() \
    (slsp_db_link_block = task_block_init(sizeof(slsp_db_link), "slsp_db_link"))

#define	SLSP_DB_NODE_INIT() \
    (slsp_db_node_block = task_block_init(sizeof(slsp_db_node), "slsp_db_node"))

#define	SLSP_DB_FSQ_INIT() \
    (slsp_db_fsq_block = task_block_init(sizeof(slsp_full_seq_q), "slsp_full_seq_q"))

/*
 * Free them
 */
#define	SLSP_DB_AS_FREE(dbas) \
    task_block_free(slsp_db_as_block, (void_t)(dbas))

#define	SLSP_DB_LINK_FREE(dblink) \
    task_block_free(slsp_db_link_block, (void_t)(dblink))

#define	SLSP_DB_NODE_FREE(dbnp) \
    task_block_free(slsp_db_node_block, (void_t)(dbnp))

#define	SLSP_DB_FSQ_FREE(fsqp) \
    task_block_free(slsp_db_fsq_block, (void_t)(fsqp))

/*
 * Lazy initialization/alloc's
 */
#define	SLSP_DB_AS_GET(dbas) \
    do { \
	if (!slsp_db_as_block) { \
	    SLSP_DB_AS_INIT(); \
	} \
	(dbas) = SLSP_DB_AS_ALLOC(); \
    } while (0)

#define	SLSP_DB_LINK_GET(dblink) \
    do { \
	if (!slsp_db_link_block) { \
	    SLSP_DB_LINK_INIT(); \
	} \
	(dblink) = SLSP_DB_LINK_ALLOC(); \
    } while (0)

#define	SLSP_DB_NODE_GET(dbnp) \
    do { \
	if (!slsp_db_node_block) { \
	    SLSP_DB_NODE_INIT(); \
	} \
	(dbnp) = SLSP_DB_NODE_ALLOC(); \
    } while (0)

#define	SLSP_DB_FSQ_GET(fsqp) \
    do { \
	if (!slsp_db_fsq_block) { \
	    SLSP_DB_FSQ_INIT(); \
	} \
	(fsqp) = SLSP_DB_FSQ_ALLOC(); \
    } while (0)

/*
 * Reference/dereference nodes, used when links which reference
 * the nodes are added/deleted.
 */
#define	SLSP_NODE_REFERENCE(np)	((np)->slsp_node_refcount++)

#define	SLSP_NODE_DEREFERENCE(np) \
    do { \
	assert((np)->slsp_node_refcount); \
	if ((np)->slsp_node_refcount == 1 \
	  && (np)->slsp_node_type == SLSP_LSP_PLACEHOLDER) { \
	    slsp_db_dereference(np); \
	} else { \
	    (np)->slsp_node_refcount--; \
	} \
    } while (0)

/*
 * Support for building/sending partial sequence number packets.
 * We do sort of a lazy build here, collecting everything to send
 * to a neighbour until either we need to send to another neighbour,
 * or input completes.  We currently build this in the send packet.
 */
#define	SLSP_DB_MAXENTRIES	100

#define	slsp_db_ack_pkt		((byte *) task_send_buffer)

slsp_neighbour *slsp_db_ack_nbr = (slsp_neighbour *) 0;
static byte *slsp_db_ack_pkt_start;
static byte *slsp_db_ack_pkt_end;

#define	SLSP_DB_ADD_ACK(nbr, enttype, addr, seqnum, lifetime, cksum) \
    do { \
	register slsp_neighbour *Xnbr = (nbr); \
	if (slsp_db_ack_nbr != Xnbr \
	  || slsp_db_ack_pkt_start >= slsp_db_ack_pkt_end) { \
	    slsp_db_ack_flush(Xnbr); \
	} \
	SLSP_PUT_SEQ_ENT((enttype), \
			 (addr), \
			 (seqnum), \
			 (lifetime), \
			 (cksum), \
			 slsp_db_ack_pkt_start); \
    } while (0)


/*
 * Schedule the transmit queue
 */
#define	SLSP_DB_XMT_SCHEDULE(inp) \
    do { \
	if (!BIT_TEST((inp)->slsp_flags, SLSPF_XMT_JOB|SLSPF_XMT_THROTTLE)) { \
	    slsp_db_xmt_schedule((inp)); \
	} \
    } while (0)

/*
 * Schedule the lifetime queue
 */
#define	SLSP_DB_LIFE_SCHEDULE(inp) \
    do { \
	register slsp_instance *Xinp = (inp); \
	if (Xinp->slsp_next_life_timeout) { \
	    register slsp_db_node *Xnp = SLSP_LIFE_NEXT(Xinp, Xinp); \
	    if (!Xnp || \
	      Xinp->slsp_next_life_timeout <= Xnp->slsp_node_life_timeout) { \
		break; \
	    } \
	} \
	slsp_db_life_schedule(Xinp); \
    } while (0)

/*
 * Forward declarations
 */
PROTOTYPE(slsp_db_xmt_timer_create,
	  static void,
	  (slsp_instance *,
	   time_t));
PROTOTYPE(slsp_db_life_schedule,
	  static void,
	  (slsp_instance *));
	  


/*
 * Lookup table to do an ffs() from the high order end (the ffs() routine
 * runs from the other end), on a byte.  This is useful to find the first
 * differing bit for the patricia tree.
 */
const byte slsp_bit_table[256] = {
    0x00, 0x01, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};


/*
 * Find the lowest order bit set in a byte
 */
static const slsp_bit_t slsp_db_first_set[256] = {
    0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};


/*
 * slsp_node_insert - add a node to the node tree for this instance.  Note that
 *		   found_np points to the node that was "found" when the
 *		   database was searched for the node we are adding.
 *		   If it is NULL we do the search on our own.
 */
static void
slsp_node_insert __PF2(new_np, slsp_db_node *,
		       found_np, slsp_db_node *)
{
    register slsp_db_node *np = new_np;
    register slsp_db_node *nprev, *ncurr;
    register u_long key;
    register u_long mask, omask;
    slsp_instance *slsp;

    /*
     * Check to make sure this guy isn't in the tree already.  Also
     * check that the instance pointer is set in the node.
     */
    assert(np->slsp_node_mask == 0);
    slsp = np->slsp_node_instance;
    assert(slsp != NULL);

    /*
     * Fetch his key.  This is the node address in host byte order.
     */
    key = ntohl(sock2ip(np->slsp_node_addr));

    /*
     * If he didn't include a "found" node, but there are nodes
     * to "find", "find" it.  If there are no nodes to find then
     * this is the first node in the tree and just needs to get
     * initialized appropriately.
     */
    if (found_np == NULL) {
	if ((ncurr = slsp->slsp_nodes) == NULL) {
	    slsp->slsp_nodes = np->slsp_node_right = np->slsp_node_left = np;
	    np->slsp_node_mask = SLSP_NOMASK;
	    return;
        }
	mask = SLSP_NOMASK;
	while (mask > ncurr->slsp_node_mask) {
	    mask = ncurr->slsp_node_mask;
	    if (key & mask) {
		ncurr = np->slsp_node_right;
	    } else {
		ncurr = np->slsp_node_left;
	    }
	}
	found_np = ncurr;
    }

    /*
     * Here we have an insertion to do.  Figure out the first
     * (most significant) bit which differs from the "found" node.  This
     * tells us the bit we need to test in the node we insert.
     *
     * Do a binary search to find the byte the first bit is in, then use
     * the table to form the mask.
     */
    mask = key ^ ntohl(sock2ip(found_np->slsp_node_addr));
    if (mask >= 0x10000) {
	if (mask >= 0x1000000) {
	    mask = ((u_long)(slsp_bit_table[mask >> 24])) << 24;
	} else {
	    mask = ((u_long)(slsp_bit_table[mask >> 16])) << 16;
	}
    } else {
	if (mask >= 0x100) {
	    mask = ((u_long)(slsp_bit_table[mask >> 8])) << 8;
	} else {
	    mask = (u_long)(slsp_bit_table[mask]);
	}
    }
    /*
     * XXX Sanity.  This could only happen if a node with the same key
     * is in the tree already, which should never happen.
     */
    assert(mask != 0);
    np->slsp_node_mask = mask;

    /*
     * Now we locate where this guy needs to be inserted.  Search down
     * the tree until we find either an end node (i.e. an upward link)
     * or a node with a mask smaller than ours.  We insert our node
     * above this (watch for the case where the node we find is the
     * current root node), making one of the pointers an uplink pointing
     * at the node itself.
     */
    nprev = NULL;
    ncurr = slsp->slsp_nodes;
    omask = SLSP_NOMASK;
    while (omask > ncurr->slsp_node_mask) {
	omask = ncurr->slsp_node_mask;
	if (mask >= omask) {
	    break;
	}
	nprev = ncurr;
	if (key & omask) {
	    ncurr = ncurr->slsp_node_right;
	} else {
	    ncurr = ncurr->slsp_node_left;
	}
    }
    assert(mask != ncurr->slsp_node_mask);

    /*
     * Point the new node at the current node, and at itself.  The
     * pointer to the current node in the previous node is changed to
     * point at the new node.  Simple(?).
     */
    if (key & mask) {
	np->slsp_node_right = np;
	np->slsp_node_left = ncurr;
    } else {
	np->slsp_node_right = ncurr;
	np->slsp_node_left = np;
    }

    if (nprev == NULL) {
	/* New root node */
	slsp->slsp_nodes = np;
    } else if (nprev->slsp_node_right == ncurr) {
	nprev->slsp_node_right = np;
    } else {
	nprev->slsp_node_left = np;
    }
    /* done */
}


/*
 * slsp_node_remove - remove a node from the node tree for this instance.
 */
static void
slsp_node_remove __PF1(remove_np, slsp_db_node *)
{
    register slsp_db_node *np = remove_np;
    register slsp_db_node *tmp;
    register slsp_db_node *nprev, *ncurr;
    register u_long key;
    slsp_db_node *intprev;
    slsp_instance *slsp;

    /*
     * Check to make sure this guy is in the tree already.
     */
    assert(np->slsp_node_mask != 0);

    /*
     * Detect the trivial case, that there is only one guy in the tree,
     * and fix this up.
     */
    slsp = np->slsp_node_instance;
    ncurr = slsp->slsp_nodes;
    if (ncurr->slsp_node_mask == SLSP_NOMASK) {
	assert(ncurr == np);
	np->slsp_node_mask = 0;
	slsp->slsp_nodes = NULL;
	return;
    }

    /*
     * We will be in the tree twice, once as an internal node and once
     * as an external (or up-)node.  Find the guy who points to our man
     * in the tree in both cases, and the guy who points to him in the
     * latter.
     */
    nprev = NULL;
    intprev = NULL;
    key = ntohl(sock2ip(np->slsp_node_addr));
    for (;;) {
	if (key & ncurr->slsp_node_mask) {
	    tmp = ncurr->slsp_node_right;
	} else {
	    tmp = ncurr->slsp_node_left;
	}
	if (tmp == np) {
	    if (np->slsp_node_mask >= ncurr->slsp_node_mask) {
		break;		/* got it */
	    }
	    intprev = ncurr;	/* current is in front of internal */
	} else {
	    assert(tmp->slsp_node_mask < ncurr->slsp_node_mask);
	}
	nprev = ncurr;
	ncurr = tmp;
    }

    /*
     * Now we have ncurr pointing at the node which contains the up-link
     * to our deleted node, nprev pointing at the node which points to
     * ncurr and intprev pointing at the guy who precedes our node in its
     * internal incarnation.
     *
     * There are several cases here.  We know the information in ncurr
     * is no longer needed, and that the sibling pointer in ncurr will
     * be moved to nprev (replacing ncurr).  If our node is the special
     * one with no internal information, we make ncurr the special.
     * If ncurr is our node (i.e. intprev == nprev) then we simply
     * point nprev at our sibling and we're done.  Otherwise we
     * additionally copy our information to ncurr, and point intprev
     * at ncurr.
     *
     * There is probably a better way to do this, but I'm an engineer,
     * not a (mere) programmer.
     */
    if (ncurr->slsp_node_right == np) {
	tmp = ncurr->slsp_node_left;
    } else {
	tmp = ncurr->slsp_node_right;
    }

    if (nprev == NULL) {
	slsp->slsp_nodes = tmp;
    } else if (nprev->slsp_node_right == ncurr) {
	nprev->slsp_node_right = tmp;
    } else {
	nprev->slsp_node_left = tmp;
    }
    if (ncurr != np) {
	if (np->slsp_node_mask == SLSP_NOMASK) {
	    ncurr->slsp_node_mask = SLSP_NOMASK;
	    ncurr->slsp_node_left = ncurr->slsp_node_right = ncurr;
	} else {
	    ncurr->slsp_node_mask = np->slsp_node_mask;
	    ncurr->slsp_node_left = np->slsp_node_left;
	    ncurr->slsp_node_right = np->slsp_node_right;
	    if (intprev == NULL) {
		slsp->slsp_nodes = ncurr;
	    } else if (intprev->slsp_node_right == np) {
		intprev->slsp_node_right = ncurr;
	    } else {
		intprev->slsp_node_left = ncurr;
	    }
	}
    }

    /*
     * Set the bit to 0 so we know it is deleted.
     */
    np->slsp_node_mask = 0;
}


/*
 * slsp_db_ack_flush - flush the pending partial sequence numbers
 *		       packet if we have one, because we want to
 *		       build one for a different neighbour or
 *		       because we're done with input for now
 */
void
slsp_db_ack_flush __PF1(nbr, slsp_neighbour *)
{
    register size_t entries, len;

    if (slsp_db_ack_nbr) {
	entries = slsp_db_ack_pkt_end - slsp_db_ack_pkt_start;
	assert(!(entries % SLSP_SEQ_ENT_LEN));
	entries = SLSP_DB_MAXENTRIES - (entries / SLSP_SEQ_ENT_LEN);
	if (entries) {
	    len = slsp_db_ack_pkt_start - slsp_db_ack_pkt;
	    assert(len == SLSP_SEQ_LENGTH(entries));
	    SLSP_SET_SEQ_LEN(len, entries, slsp_db_ack_pkt);
	    slsp_send(slsp_db_ack_nbr, slsp_db_ack_pkt, len);
	}
	if (nbr) {
	    slsp_db_ack_pkt_start = slsp_db_ack_pkt + SLSP_SEQ_FIXED_LEN;
	    if (nbr != slsp_db_ack_nbr) {
		register byte *cp = slsp_db_ack_pkt + 7;

		SLSP_PUT_IPADDR(nbr->slsp_nbr_instance->slsp_addr, cp);
	    }
	}
    } else if (nbr) {
	SLSP_INIT_SEQ(nbr->slsp_nbr_instance->slsp_addr,
		      SLSP_SEQ_PARTIAL,
		      slsp_db_ack_pkt);
	slsp_db_ack_pkt_start = slsp_db_ack_pkt + SLSP_SEQ_FIXED_LEN;
	slsp_db_ack_pkt_end = slsp_db_ack_pkt
	  + SLSP_SEQ_LENGTH(SLSP_DB_MAXENTRIES);
    }
    slsp_db_ack_nbr = nbr;
}


/*
 * slsp_db_full_seq_format - format a full sequence numbers packet from
 *			     the data on this instance's full list.
 */
static size_t
slsp_db_full_seq_format __PF2(inp, slsp_instance *,
			      pkt, byte *)
{
    register slsp_db_node *np;
    register byte *cp;
    register size_t entries = inp->slsp_full_seq_entries;
    size_t len;

    /*
     * Do the fixed part
     */
    cp = pkt;
    len = SLSP_SEQ_LENGTH(entries);
    SLSP_PUT_SEQ(inp->slsp_addr, SLSP_SEQ_FULL, len, entries, cp);

    /*
     * Now walk the full list adding them to the packet
     * XXX note that sometimes we won't know a cksum for the
     * local node.  I think this is okay. XXX
     */
    for (np = SLSP_FULL_NEXT(inp, inp);
	 np;
	 np = SLSP_FULL_NEXT(inp, np)) {
	SLSP_PUT_SEQ_ENT(SLSP_SEQ_ENT_TYPE_ROUTER,
			 np->slsp_node_addr,
			 np->slsp_node_seqnum,
			 ((np->slsp_node_life > time_sec) ?
			  (np->slsp_node_life - time_sec) : (time_t) 4),
			 (np->slsp_node_cksum ? np->slsp_node_cksum : 0xffff),
			 cp);
    }

    assert(len == (cp - pkt));
    return len;
}


/*
 * slsp_db_bit_alloc - allocate a bit for a neighbour
 */
void
slsp_db_bit_alloc __PF1(nbr, slsp_neighbour *)
{
    slsp_instance *inp;
    register u_short i;

    assert(nbr->slsp_nbr_index == 0);

    inp = nbr->slsp_nbr_instance;
    for (i = 0; i < SLSP_MAX_NEIGHBOURS; i++) {
	if (!(inp->slsp_bit_nbrs[i])) {
	    break;
	}
    }

    assert(i < SLSP_MAX_NEIGHBOURS);
    nbr->slsp_nbr_index = i + 1;
    nbr->slsp_nbr_offset = SLSP_WORDINDEX(i);
    nbr->slsp_nbr_bitmask = SLSP_BITINMASK(i);
    inp->slsp_bit_nbrs[i] = nbr;
}


/*
 * slsp_db_bit_free - dump a neighbour's bit
 */
void
slsp_db_bit_free __PF1(nbr, slsp_neighbour *)
{
    slsp_instance *inp;
    register u_short i;

    inp = nbr->slsp_nbr_instance;
    i = nbr->slsp_nbr_index;
    assert(i);
    assert(inp->slsp_bit_nbrs[i - 1] == nbr);
    inp->slsp_bit_nbrs[i - 1] = (slsp_neighbour *) 0;
    nbr->slsp_nbr_index = 0;
    nbr->slsp_nbr_offset = 0;
    nbr->slsp_nbr_bitmask = 0;
}


/*
 * slsp_db_xmt_flush - flush the transmit queue for this instance,
 *		       sending ready LSP's from the front of the
 *		       queue.
 */
static void
slsp_db_xmt_flush __PF1(inp, slsp_instance *)
{
    register byte *cp;
    register slsp_db_link *lp;
    register int i;
    int tosend;
    byte *cpstart;
    slsp_db_node *np, *np_next;
    slsp_full_seq_q *fsp, *fsp_next;
    size_t length;
    time_t lifetime;
    int sent = 0;

    np_next = SLSP_XMT_NEXT(inp, inp);
    while ((np = np_next)
      && sent < SLSP_DB_MAX_XMT
      && np->slsp_node_xmt_time <= time_sec) {
	sent++;
	np_next = SLSP_XMT_NEXT(inp, np_next);
	assert(np->slsp_node_xmt_nbits);

	/*
	 * We have a guy to send.  Work on formatting the packet.  AS payload
	 * goes in first.
	 */
	cp = ((byte *) task_send_buffer) + SLSP_LSP_FIXED_LEN;
	if (np->slsp_node_aslist.slspdb_as_count) {
	    register slsp_db_as *asp;

	    cpstart = (byte *) 0;
	    if (np->slsp_node_type != SLSP_LSP_LOCALRTR) {
		asp = np->slsp_node_aslist.slspdb_as_next;

		do {
		    if (asp->slspdb_as_newbucket) {
			if (cpstart) {
			    *(cpstart - 1) = (byte)(cp - cpstart);
			}
			*cp++ = SLSP_LSP_CODE_AS;
			cpstart = ++cp;
		    }
		    SLSP_PUT_LSP_AS(asp->slspdb_as, cp);
		} while ((asp = asp->slspdb_as_next));
	    } else {
		int in_bucket = 0;

		for (asp = np->slsp_node_aslist.slspdb_as_next;
		     asp != &(np->slsp_node_aslist);
		     asp = asp->slspdb_as_next) {
		    if (!in_bucket) {
			if (cpstart) {
			    *(cpstart - 1) = (byte)(cp - cpstart);
			}
			*cp++ = SLSP_LSP_CODE_AS;
			cpstart = ++cp;
			in_bucket = 255 / SLSP_LSP_AS_LEN;
		    }
		    in_bucket--;
		    SLSP_PUT_LSP_AS(asp->slspdb_as, cp);
		}
	    }
	    if (cpstart) {
		*(cpstart - 1) = (byte)(cp - cpstart);
	    }
	}

	/*
	 * Now the links payload.  See if we have any
	 */
	cpstart = (byte *) 0;
	for (lp = np->slsp_node_linklist.slspdb_link_next;
	     lp && lp != &(np->slsp_node_linklist);
	     lp = lp->slspdb_link_next) {
	    if (lp->slspdb_link_newbucket) {
		if (cpstart) {
		    *(cpstart - 1) = (byte)(cp - cpstart);
		}
		*cp++ = SLSP_LSP_CODE_LINKS;
		cpstart = ++cp;
		SLSP_PUT_LINK_COST(lp->slspdb_link_cost, cp);
	    }
	    SLSP_PUT_LINK_ID(lp->slspdb_link_node->slsp_node_addr, cp);
	}
	if (cpstart) {
	    *(cpstart - 1) = (byte)(cp - cpstart);
	}

	/*
	 * Compute the length while we're here.  Then restart at the
	 * front of the packet.
	 */
	length = cp - ((byte *) task_send_buffer);
	cp = (byte *) task_send_buffer;
	assert(!(np->slsp_node_cksum) || np->slsp_node_len == length);

	/*
	 * Compute the lifetime
	 */
	if (np->slsp_node_type == SLSP_LSP_TIMEDOUT) {
	    assert(length == SLSP_LSP_FIXED_LEN);
	    lifetime = 0;
	} else if (np->slsp_node_life <= (time_sec + (time_t) 4)) {
	    lifetime = (time_t) 4;
	} else {
	    lifetime = np->slsp_node_life - time_sec;
	}

	/*
	 * If we need to compute the checksum, set the lifetime
	 * to zero in the header.  Then compute the checksum and
	 * put the real time back in
	 */
	SLSP_PUT_LSP(length,
		     ((np->slsp_node_cksum) ? lifetime : 0),
		     np->slsp_node_addr,
		     (BIT_TEST(np->slsp_node_flags, SLSP_NODEF_RTTYPE_ALT)
		       ? SLSP_LSP_RTTYPE_ALT : SLSP_LSP_RTTYPE),
		     np->slsp_node_seqnum,
		     np->slsp_node_cksum,
		     cp);

	if (!(np->slsp_node_cksum)) {
	    cp = (byte *) task_send_buffer;
	    np->slsp_node_cksum = SLSP_SET_LSP_CKSUM(cp, length);
	    np->slsp_node_len = length;
	    SLSP_PUT_LSP_LIFETIME(lifetime, cp);
	}

	/*
	 * Okdk.  Here we have a complete packet, and we know its
	 * length.  Send it to everyone who is waiting with baited
	 * breath for this.  We do this by walking his transmit
	 * bit array looking for guys with their bit set.
	 */
	tosend = np->slsp_node_xmt_nbits;
	cp = np->slsp_node_xmt_bits.slsp_bit_set;
	for (i = 0; tosend && i < SLSP_MAX_NEIGHBOURS; i += SLSP_BITSIZE) {
	    register slsp_bit_t tbit = *cp;
	    while (tbit) {
		register slsp_bit_t ibit = slsp_db_first_set[tbit];
		register slsp_neighbour *nbr = inp->slsp_bit_nbrs[i + ibit];

		assert(nbr);
		slsp_send(nbr, (byte *) task_send_buffer, length);
		tosend--;
		ibit = ~(nbr->slsp_nbr_bitmask);
		tbit &= ibit;
		if (!SLSP_NBR_PTP_TYPE(nbr)) {
		    *cp &= ibit;
		    np->slsp_node_xmt_nbits--;
		}
	    }
	    cp++;
	}

	/*
	 * All sent.  Mark this node so we don't send it again for
	 * a while.  Then pull it off the queue.  If it still has
	 * bits set put it at the end.
	 */
	np->slsp_node_xmt_time = time_sec + SLSP_MIN_SEND_INTERVAL;
	SLSP_XMT_REMOVE(np);
	if (np->slsp_node_xmt_nbits) {
	    register slsp_db_node *np_prev = SLSP_XMT_PREV(inp, inp);

	    while (np_prev
	      && np_prev->slsp_node_xmt_time > np->slsp_node_xmt_time) {
		np_prev = SLSP_XMT_PREV(inp, np_prev);
	    }
	    if (np_prev) {
		SLSP_XMT_APPEND(np_prev, np);
	    } else {
		SLSP_XMT_APPEND(inp, np);
	    }
	}
    }


    /*
     * Same thing for the sequence numbers queue.
     */
    length = 0;
    fsp = inp->slsp_full_queue.slsp_fq_next;
    while (fsp != &(inp->slsp_full_queue)) {
	register slsp_neighbour *nbr;

	if (fsp->slsp_fq_time > time_sec) {
	    break;
	}
	fsp_next = fsp->slsp_fq_next;
	if (!length) {
	    length = slsp_db_full_seq_format(inp, (byte *) task_send_buffer);
	}
	nbr = fsp->slsp_fq_nbr;
	slsp_send(nbr, (byte *) task_send_buffer, length);
	fsp->slsp_fq_count++;
	if (SLSP_NBR_PTP_TYPE(nbr)
	  && fsp->slsp_fq_count >= SLSP_FULL_SEQ_COUNT) {
	    SLSP_FSQ_REMOVE(fsp);
	    nbr->slsp_nbr_fsq = (slsp_full_seq_q *) 0;
	    SLSP_DB_FSQ_FREE(fsp);
	} else {
	    register slsp_full_seq_q *fsp_prev;

	    fsp->slsp_fq_time = time_sec + MAX(nbr->slsp_nbr_hellointerval,
					       SLSP_FULL_SEQ_COUNT);
	    fsp_prev = inp->slsp_full_queue.slsp_fq_prev;
	    while (fsp_prev->slsp_fq_time > fsp->slsp_fq_time) {
		fsp_prev = fsp_prev->slsp_fq_prev;
	    }
	    SLSP_FSQ_REMOVE(fsp);
	    SLSP_FSQ_PREPEND(&(inp->slsp_full_queue), fsp);
	}
	fsp = fsp_next;
    }


    /*
     * Done for now.  Figure out when we should do this next.  Terminate
     * any task job.  If the queue is now empty reset the timer.  If
     * we broadcast our share don't send anything for a second.  Otherwise
     * set the timer for the next expiry.
     */
    if (BIT_TEST(inp->slsp_flags, SLSPF_XMT_JOB)) {
	task_job_delete(inp->slsp_job_xmt);
	BIT_RESET(inp->slsp_flags, SLSPF_XMT_JOB);
	inp->slsp_job_xmt = (task_job *) 0;
    }
    np = SLSP_XMT_NEXT(inp, inp);
    fsp = inp->slsp_full_queue.slsp_fq_next;
    if (fsp == &(inp->slsp_full_queue)) {
	fsp = (slsp_full_seq_q *) 0;
    }
    if (!np && !fsp) {
	if (BIT_TEST(inp->slsp_flags, SLSPF_XMT_TIMER)) {
	    task_timer_reset(inp->slsp_timer_xmt);
	    BIT_RESET(inp->slsp_flags, SLSPF_XMT_TIMER);
	    inp->slsp_next_xmt_time = (time_t) 0;
	}
	lifetime = (time_t) 0;
    } else if (sent >= SLSP_DB_MAX_XMT) {
	BIT_SET(inp->slsp_flags, SLSPF_XMT_THROTTLE);
	if (!(inp->slsp_next_xmt_time)
	  || inp->slsp_next_xmt_time != (time_sec + 1)) {
	    lifetime = (time_t) 1;
	} else {
	    lifetime = (time_t) 0;
	}
    } else {
	lifetime = (time_t) 0;
	if (np && np->slsp_node_xmt_time > time_sec) {
	    lifetime = np->slsp_node_xmt_time;
	}
	if (fsp && fsp->slsp_fq_time > time_sec) {
	    if (!lifetime || lifetime > fsp->slsp_fq_time) {
		lifetime = fsp->slsp_fq_time;
	    }
	}
	if (lifetime) {
	    lifetime -= time_sec;
	} else {
	    lifetime = (time_t) 1;
	}
    }

    if (lifetime) {
	if (!(inp->slsp_timer_xmt)) {
	    slsp_db_xmt_timer_create(inp, lifetime);
	} else if (lifetime == (time_t) 1
	  || !(inp->slsp_next_xmt_time)
	  || inp->slsp_next_xmt_time > (time_sec + lifetime)) {
	    task_timer_set(inp->slsp_timer_xmt,
			   (time_t) 0,
			   lifetime);
	    inp->slsp_next_xmt_time = time_sec + lifetime;
	    BIT_SET(inp->slsp_flags, SLSPF_XMT_TIMER);
	}
    }
}


/*
 * slsp_db_xmt_timeout - process a transmit timer expiry.  Basically
 *			 we blow out any competing task job, then
 *			 call the flush routine to flush out some routes.
 */
static void
slsp_db_xmt_timeout __PF2(tip, task_timer *,
			  interval, time_t)
{
    slsp_instance *inp = (slsp_instance *)(tip->task_timer_data);

    if (BIT_TEST(inp->slsp_flags, SLSPF_XMT_JOB)) {
	task_job_delete(inp->slsp_job_xmt);
	inp->slsp_job_xmt = (task_job *) 0;
    }

    BIT_RESET(inp->slsp_flags,
	      SLSPF_XMT_JOB|SLSPF_XMT_TIMER|SLSPF_XMT_THROTTLE);
    inp->slsp_next_xmt_time = (time_t) 0;
    slsp_db_xmt_flush(inp);
}


/*
 * slsp_db_xmt_timer_create - create and set the transmit timer
 */
static void
slsp_db_xmt_timer_create __PF2(inp, slsp_instance *,
			       offset, time_t)
{
    if (!(inp->slsp_timer_xmt)) {
	inp->slsp_timer_xmt = task_timer_create(inp->slsp_task,
						"Transmit",
						(flag_t) 0,
						(time_t) 0,
						offset,
						slsp_db_xmt_timeout,
						(void_t) inp);
    } else {
	task_timer_set(inp->slsp_timer_xmt, (time_t) 0, offset);
    }

    inp->slsp_next_xmt_time = time_sec + offset;
    BIT_SET(inp->slsp_flags, SLSPF_XMT_TIMER);
}


/*
 * slsp_db_xmt_shutdown - shut down the transmission jobs
 */
static void
slsp_db_xmt_shutdown __PF1(inp, slsp_instance *)
{
    if (inp->slsp_timer_xmt) {
	task_timer_delete(inp->slsp_timer_xmt);
	inp->slsp_timer_xmt = (task_timer *) 0;
	inp->slsp_next_xmt_time = (time_t) 0;
	BIT_RESET(inp->slsp_flags, SLSPF_XMT_TIMER|SLSPF_XMT_THROTTLE);
    }
    if (inp->slsp_job_xmt) {
	task_job_delete(inp->slsp_job_xmt);
	inp->slsp_job_xmt = (task_job *) 0;
	BIT_RESET(inp->slsp_flags, SLSPF_XMT_JOB);
    }
}


/*
 * slsp_db_xmt_job_run - transmit job run as a task job
 */
static void
slsp_db_xmt_job_run __PF1(tjp, task_job *)
{
    slsp_instance *inp = (slsp_instance *)(tjp->task_job_data);

    BIT_RESET(inp->slsp_flags, SLSPF_XMT_JOB);
    inp->slsp_job_xmt = (task_job *) 0;
    if (BIT_TEST(inp->slsp_flags, SLSPF_XMT_THROTTLE)) {
	/*
	 * Don't bother, wait for the timer.
	 */
	assert(BIT_TEST(inp->slsp_flags, SLSPF_XMT_TIMER));
    } else {
	slsp_db_xmt_flush(inp);
    }
}


/*
 * slsp_db_xmt_schedule - (re)schedule a run of the transmit queue
 */
static void
slsp_db_xmt_schedule __PF1(inp, slsp_instance *)
{
    slsp_db_node *np;
    slsp_full_seq_q *fsp;
    time_t nexttime;

    if (BIT_TEST(inp->slsp_flags, SLSPF_XMT_THROTTLE|SLSPF_XMT_JOB)
      || (inp->slsp_next_xmt_time && inp->slsp_next_xmt_time <= time_sec)) {
	return;
    }

    np = SLSP_XMT_NEXT(inp, inp);
    fsp = inp->slsp_full_queue.slsp_fq_next;
    if (fsp == &(inp->slsp_full_queue)) {
	fsp = (slsp_full_seq_q *) 0;
    }
    if (!np && !fsp) {
	return;
    }
    if (np) {
	nexttime = np->slsp_node_xmt_time;
    } else {
	nexttime = (time_t) 0;
    }
    if (fsp) {
	if (!nexttime || nexttime > fsp->slsp_fq_time) {
	    nexttime = fsp->slsp_fq_time;
	}
    }

    if (nexttime <= time_sec) {
	inp->slsp_job_xmt = task_job_create(inp->slsp_task,
					    TASK_JOB_FG,
					    "LSP Transmit",
					    slsp_db_xmt_job_run,
					    (void_t) inp);
	BIT_SET(inp->slsp_flags, SLSPF_XMT_JOB);
    } else if (!(inp->slsp_next_xmt_time)) {
	if (inp->slsp_timer_xmt) {
	    inp->slsp_next_xmt_time = nexttime;
	    task_timer_set(inp->slsp_timer_xmt,
			   (time_t) 0,
			   (nexttime - time_sec));
	    BIT_SET(inp->slsp_flags, SLSPF_XMT_TIMER);
	} else {
	    slsp_db_xmt_timer_create(inp, (nexttime - time_sec));
	}
    } else if (inp->slsp_next_xmt_time > (nexttime + 1)) {
	inp->slsp_next_xmt_time = nexttime;
	task_timer_set(inp->slsp_timer_xmt,
		       (time_t) 0,
		       (nexttime - time_sec));
    }
}


/*
 * slsp_db_sendto - send an LSP to a particular neighbour
 */
static void
slsp_db_sendto __PF2(nbr, slsp_neighbour *,
		     np, slsp_db_node *)
{
    if (np->slsp_node_xmt_nbits) {
	SLSP_BIT_SET(&(np->slsp_node_xmt_bits), nbr);
    } else {
	register slsp_instance *inp = nbr->slsp_nbr_instance;

	if (np->slsp_node_xmt_time > time_sec) {
	    register slsp_db_node *np_prev = SLSP_XMT_PREV(inp, inp);

	    while (np_prev) {
		if (np_prev->slsp_node_xmt_time <= np->slsp_node_xmt_time) {
		    break;
		}
		np_prev = SLSP_XMT_PREV(inp, np_prev);
	    }
	    if (np_prev) {
		SLSP_XMT_APPEND(np_prev, np);
	    } else {
		SLSP_XMT_APPEND(inp, np);
	    }
	} else {
	    np->slsp_node_xmt_time = time_sec;
	    SLSP_XMT_APPEND(inp, np);
	}
	np->slsp_node_xmt_bits.slsp_bit_set[nbr->slsp_nbr_offset]
	  = nbr->slsp_nbr_bitmask;
	np->slsp_node_xmt_nbits = 1;
	SLSP_DB_XMT_SCHEDULE(inp);
    }
}


#ifdef	notdef
/*
 * slsp_db_sendall - send all the LSP's we know to a particular neighbour
 */
static void
slsp_db_sendall __PF1(nbr, slsp_neighbour *)
{
    slsp_instance *inp;
    register int bword = nbr->slsp_nbr_offset;
    register byte bmask = nbr->slsp_nbr_bitmask;
    register slsp_db_node *np, *np_prev;
    int added = 0;

    inp = nbr->slsp_nbr_instance;
    np = SLSP_FULL_NEXT(inp, inp);
    while (np) {
	if (np->slsp_node_xmt_nbits) {
	    if (!(np->slsp_node_xmt_bits.slsp_bit_set[bword] & bmask)) {
		np->slsp_node_xmt_bits.slsp_bit_set[bword] |= bmask;
		np->slsp_node_xmt_nbits++;
	    }
	} else {
	    added++;
	    np->slsp_node_xmt_bits.slsp_bit_set[bword] = bmask;
	    np->slsp_node_xmt_nbits = 1;
	    if (np->slsp_node_xmt_time <= time_sec) {
		np->slsp_node_xmt_time = time_sec;
		SLSP_XMT_APPEND(inp, np);
	    } else {
		np_prev = SLSP_XMT_PREV(inp, inp);

		while (np_prev) {
		    if (np_prev->slsp_node_xmt_time <= np->slsp_node_xmt_time) {
			break;
		    }
		    np_prev = SLSP_XMT_PREV(inp, np_prev);
		}
		if (np_prev) {
		    SLSP_XMT_APPEND(np_prev, np);
		} else {
		    SLSP_XMT_APPEND(inp, np);
		}
	    }
	}
	np = SLSP_FULL_NEXT(inp, np);
    }

    if (added) {
	SLSP_DB_XMT_SCHEDULE(inp);
    }
}
#endif	/* notdef */


/*
 * slsp_db_flood - flood this LSP to all our up neighbours except (maybe)
 *		   the guy who send it to us.
 */
static void
slsp_db_flood __PF2(np, slsp_db_node *,
		    nbr_nosend, slsp_neighbour *)
{
    register slsp_instance *inp;
    register slsp_neighbour *nbr;
    register slsp_bits *sbits;
    int on_list = 0;

    inp = np->slsp_node_instance;
    if (np->slsp_node_xmt_nbits) {
	on_list = 1;
    }
    sbits = &(np->slsp_node_xmt_bits);

    for (nbr = inp->slsp_nbrs; nbr; nbr = nbr->slsp_nbr_next) {
	if (nbr->slsp_nbr_state == SLSP_NBR_STATE_ADJACENT) {
	    register slsp_neighbour *send_nbr;
	    if (nbr->slsp_nbr_type == SLSP_NTYPE_BCAST) {
		send_nbr = nbr->slsp_nbr_dr;
	    } else {
		send_nbr = nbr;
	    }
	    if (send_nbr != nbr_nosend) {
		SLSP_BIT_SET(sbits, send_nbr);
	    } else {
		SLSP_BIT_CLR(sbits, send_nbr);
	    }
	}
    }

    if (!on_list && np->slsp_node_xmt_nbits) {
	if (np->slsp_node_xmt_time <= time_sec) {
	    np->slsp_node_xmt_time = time_sec;
	    SLSP_XMT_APPEND(inp, np);
	} else {
	    register slsp_db_node *np_prev = SLSP_XMT_PREV(inp, inp);

	    while (np_prev) {
		if (np_prev->slsp_node_xmt_time <= np->slsp_node_xmt_time) {
		    break;
		}
		np_prev = SLSP_XMT_PREV(inp, np_prev);
	    }
	    if (np_prev) {
		SLSP_XMT_APPEND(np_prev, np);
	    } else {
		SLSP_XMT_APPEND(inp, np);
	    }
	}
	SLSP_DB_XMT_SCHEDULE(inp);
    } else if (on_list && !(np->slsp_node_xmt_nbits)) {
	SLSP_XMT_REMOVE(np);
    }
}


/*
 * slsp_db_reoriginate - originate a local LSP with an updated sequence number
 */
static void
slsp_db_reoriginate __PF2(np, slsp_db_node *,
			  seqnum, slsp_seqnum_t)
{
    slsp_seqnum_t nseqnum;
    slsp_instance *inp = np->slsp_node_instance;
    register slsp_db_node *np_next;

    assert(SLSP_IS_LOCAL_LSP(np));

    nseqnum = SLSP_SEQNUM_NEXT(seqnum);
    np->slsp_node_seqnum = nseqnum;
    np->slsp_node_cksum = 0;
    np->slsp_node_len = 0;
    np->slsp_node_life = time_sec + SLSP_LIFE_MAX;
    if (np->slsp_node_life_timeout) {
	np_next = SLSP_LIFE_NEXT(inp, np);
	SLSP_LIFE_REMOVE(np);
    } else {
	np_next = SLSP_LIFE_NEXT(inp, inp);
    }
    np->slsp_node_life_timeout = time_sec + SLSP_LIFE_REORIGINATE;

    while (np_next
      && np_next->slsp_node_life_timeout < np->slsp_node_life_timeout) {
	np_next = SLSP_LIFE_NEXT(inp, np_next);
    }
    SLSP_LIFE_PREPEND((np_next ? np_next : ((slsp_db_node *) inp)), np);

    if (((slsp_db_node *)(inp->slsp_lists.slsp_life_next)) == np) {
	SLSP_DB_LIFE_SCHEDULE(inp);
    }

    slsp_db_flood(np, (slsp_neighbour *) 0);
}


/*
 * slsp_db_dereference - dereference a node, deleting it if it is
 *			 a placeholder and the refcount is zero.
 */
static void
slsp_db_dereference __PF1(np, slsp_db_node *)
{
    assert(np->slsp_node_refcount);

    if (--(np->slsp_node_refcount) == 0
      && np->slsp_node_type == SLSP_LSP_PLACEHOLDER) {
	slsp_node_remove(np);
	if (np->slsp_node_addr) {
	    sockfree(np->slsp_node_addr);
	}
	SLSP_DB_NODE_FREE(np);
    }
}


/*
 * slsp_db_expire - expire an LSP.  Blow off all the links and AS numbers,
 *		    move it around on the lifetime queue and flood it
 *		    out to our neighbours.
 */
static void
slsp_db_expire __PF3(np, slsp_db_node *,
		     seqnum, slsp_seqnum_t,
		     nbr, slsp_neighbour *)
{
    slsp_instance *inp = np->slsp_node_instance;
    slsp_db_link *lp;
    slsp_db_node *lnp;

    /*
     * First blow off the AS number queue.  Local and remote nodes
     * do this differently.
     */
    if (np->slsp_node_aslist.slspdb_as_count) {
	register slsp_db_as *asp = np->slsp_node_aslist.slspdb_as_next;

	do {
	    register slsp_db_as *as_next = asp->slspdb_as_next;

	    SLSP_DB_AS_FREE(asp);
	    asp = as_next;
	} while (asp && asp != &(np->slsp_node_aslist));
	np->slsp_node_aslist.slspdb_as_count = 0;
	np->slsp_node_aslist.slspdb_as_next = (slsp_db_as *) 0;
    }
    np->slsp_node_aslist.slspdb_as_next
      = np->slsp_node_aslist.slspdb_as_prev = (slsp_db_as *) 0;

    /*
     * Now the links
     */
    lp = np->slsp_node_linklist.slspdb_link_next;
    while (lp && lp != &(np->slsp_node_linklist)) {
	register slsp_db_link *lp_next = lp->slspdb_link_next;

	if (lp->slspdb_link_node) {
	    SLSP_NODE_DEREFERENCE(lp->slspdb_link_node);
	}
	if (lp->slspdb_link_nbr) {
	    lp->slspdb_link_nbr->slsp_nbr_link = (slsp_db_link *) 0;
	}
	SLSP_DB_LINK_FREE(lp);
	lp = lp_next;
    }
    np->slsp_node_linklist.slspdb_link_prev
      = np->slsp_node_linklist.slspdb_link_next = (slsp_db_link *) 0;
    np->slsp_node_n_nexthops = 0;

    /*
     * If he is on the route list take him off.  If there is a route,
     * blow it out now.  Schedule an SPF since he was previously
     * reachable.
     */
    if (np->slsp_node_onlist) {
	if (np->slsp_node_rt) {
	    rt_open(inp->slsp_task);
	    (void) rt_delete(np->slsp_node_rt);
	    rt_close(inp->slsp_task, &(inp->slsp_gw), 1, NULL);
	    np->slsp_node_rt = (rt_entry *) 0;
	}
	SLSP_RT_REMOVE(np);
	np->slsp_node_onlist = SLSP_NRT_NONE;
	SLSP_SPF_SCHEDULE(inp);
    }

    /*
     * Remove him from the full list and the lifetime queue if he's on either.
     * Note that he is timed out.
     */
    if (np->slsp_node_type != SLSP_LSP_PLACEHOLDER) {
	np->slsp_node_cksum = 0;
	np->slsp_node_len = 0;
	np->slsp_node_life = (time_t) 0;
	SLSP_LIFE_REMOVE(np);
	if (np->slsp_node_type != SLSP_LSP_TIMEDOUT) {
	    SLSP_FULL_REMOVE(np);
	    inp->slsp_full_seq_entries--;
	}
    }
    np->slsp_node_type = SLSP_LSP_TIMEDOUT;

    /*
     * Set the sequence number as requested, then put this back on
     * the lifetime list.  Search the list from the front since the
     * timeout time is relatively short.
     */
    np->slsp_node_seqnum = seqnum;
    np->slsp_node_life_timeout = time_sec + SLSP_LIFE_EXPIRED;
    lnp = SLSP_LIFE_NEXT(inp, inp);
    while (lnp) {
	if (lnp->slsp_node_life_timeout >= np->slsp_node_life_timeout) {
	    break;
	}
	lnp = SLSP_LIFE_NEXT(inp, lnp);
    }
    SLSP_LIFE_PREPEND((lnp ? lnp : ((slsp_db_node *) inp)), np);

    if (((slsp_db_node *)(inp->slsp_lists.slsp_life_next)) == np) {
	SLSP_DB_LIFE_SCHEDULE(inp);
    }

    /*
     * Finally, flood this out to all our guys except maybe the
     * neighbour who sent it to us.
     */
    slsp_db_flood(np, nbr);
}


/*
 * slsp_db_life_timeout - process a lifetime timer time out.  
 */
static void
slsp_db_life_timeout __PF2(tip, task_timer *,
			   interval, time_t)
{
    slsp_instance *inp = (slsp_instance *)(tip->task_timer_data);
    slsp_db_node *np, *np_next;

    np_next = SLSP_LIFE_NEXT(inp, inp);
    while ((np = np_next)) {
	np_next = SLSP_LIFE_NEXT(inp, np);
	if (np->slsp_node_life_timeout > time_sec) {
	    break;
	}
	/*
	 * Got one.  If this is a local node, reoriginate it.
	 * If this is an expired node, remove it from the tree.
	 * Otherwise expire the node.
	 */
	if (SLSP_IS_LOCAL_LSP(np)) {
	    if (!(np->slsp_node_cksum)) {
		SLSP_LIFE_REMOVE(np);
		if (!(np->slsp_node_life_timeout)) {
		    trace_tf(inp->slsp_trace_options,
			     TR_ALL,
			     LOG_WARNING,
			     ("slsp_db_life_timeout: local node ID %A not being timed out!!?",
			      np->slsp_node_addr));
		} else {
		    register slsp_db_node *np_prev;

		    trace_tf(inp->slsp_trace_options,
			     TR_ALL,
			     LOG_WARNING,
			     ("slsp_db_life_timeout: local node ID %A has no checksum!! Why?",

			      np->slsp_node_addr));
		    np->slsp_node_life_timeout = time_sec
		      + SLSP_LIFE_REORIGINATE;
		    np_prev = SLSP_LIFE_PREV(inp, inp);
		    while (np_prev && np_prev->slsp_node_life_timeout
		      > np->slsp_node_life_timeout) {
			np_prev = SLSP_LIFE_PREV(inp, np_prev);
		    }
		    SLSP_LIFE_APPEND((np_prev ? np_prev : (slsp_db_node *)inp),
				     np);
		}
	    } else {
		trace_tf(inp->slsp_trace_options,
			 TR_NORMAL|TR_STATE,
			 0,
			("slsp_db_life_timeout: reoriginating local node ID %A",
			 np->slsp_node_addr));
		slsp_db_reoriginate(np, np->slsp_node_seqnum);
	    }
	} else if (SLSP_IS_ACTIVE_LSP(np)) {
	    trace_tf(inp->slsp_trace_options,
		     TR_NORMAL|TR_STATE,
		     0,
		     ("slsp_db_life_timeout: node ID %A expired",
		      np->slsp_node_addr));
	    slsp_db_expire(np, np->slsp_node_seqnum, (slsp_neighbour *) 0);
	} else {
	    assert(np->slsp_node_type == SLSP_LSP_TIMEDOUT);
	    SLSP_LIFE_REMOVE(np);
	    if (np->slsp_node_xmt_nbits) {
		SLSP_XMT_REMOVE(np);
		bzero((caddr_t)&(np->slsp_node_xmt_bits), sizeof(slsp_bits));
	    }
	    if (np->slsp_node_refcount) {
		trace_tf(inp->slsp_trace_options,
			 TR_NORMAL|TR_STATE,
			 0,
			 ("slsp_db_life_timeout: node ID %A remnant timed out with refcount %u, becoming placeholder",
			  np->slsp_node_addr,
			  np->slsp_node_refcount));
		np->slsp_node_type = SLSP_LSP_PLACEHOLDER;
		np->slsp_node_cksum = 0;
		np->slsp_node_life = (time_t) 0;
		np->slsp_node_life_timeout = (time_t) 0;
	   } else {
		trace_tf(inp->slsp_trace_options,
			 TR_NORMAL|TR_STATE,
			 0,
			 ("slsp_db_life_timeout: node ID %A remnant timed out and removed",
			  np->slsp_node_addr));
		slsp_node_remove(np);
		if (np->slsp_node_addr) {
		    sockfree(np->slsp_node_addr);
		}
		SLSP_DB_NODE_FREE(np);
	    }
	}
    }

    np = SLSP_LIFE_NEXT(inp, inp);
    if (!np) {
	inp->slsp_next_life_timeout = (time_t) 0;
    } else {
	task_timer_set(inp->slsp_timer_life,
		       (time_t) 0,
		       (np->slsp_node_life_timeout - time_sec));
	inp->slsp_next_life_timeout = np->slsp_node_life_timeout;
    }
}


/*
 * slsp_db_life_schedule - (re)schedule the lifetime expiry
 */
static void
slsp_db_life_schedule __PF1(inp, slsp_instance *)
{
    slsp_db_node *np;

    np = SLSP_LIFE_NEXT(inp, inp);
    if (np) {
	if (inp->slsp_timer_life) {
	    if (!(inp->slsp_next_life_timeout)
	      || inp->slsp_next_life_timeout > np->slsp_node_life_timeout) {
		time_t offset = (np->slsp_node_life_timeout > time_sec)
		  ? (np->slsp_node_life_timeout - time_sec) : ((time_t) 1);
		task_timer_set(inp->slsp_timer_life, (time_t) 0, offset);
		inp->slsp_next_life_timeout = time_sec + offset;
	    }
	} else {
	    time_t offset = (np->slsp_node_life_timeout > time_sec)
	      ? (np->slsp_node_life_timeout - time_sec) : ((time_t) 1);
	    inp->slsp_timer_life = task_timer_create(inp->slsp_task,
						     "Lifetime",
						     (flag_t) 0,
						     (time_t) 0,
						     offset,
						     slsp_db_life_timeout,
						     (void_t) inp);
	    inp->slsp_next_life_timeout = time_sec + offset;
	}
    }
}



/*
 * slsp_db_send_stop - stop sending to this neighbour.  Remove his bit
 *		       from all nodes on the transmit queue, removing
 *		       any nodes which end up with no bit set.
 */
static void
slsp_db_send_stop __PF1(nbr, slsp_neighbour *)
{
    slsp_instance *inp = nbr->slsp_nbr_instance;
    register int bword = nbr->slsp_nbr_offset;
    register byte bmask = nbr->slsp_nbr_bitmask;
    register slsp_db_node *np, *np_next;

    np = SLSP_XMT_NEXT(inp, inp);
    while (np) {
	np_next = SLSP_XMT_NEXT(inp, np);
	if ((np->slsp_node_xmt_bits.slsp_bit_set[bword] & bmask)) {
	    np->slsp_node_xmt_bits.slsp_bit_set[bword] &= ~bmask;
	    np->slsp_node_xmt_nbits--;
	    if (!(np->slsp_node_xmt_nbits)) {
		SLSP_XMT_REMOVE(np);
	    }
	}
	np = np_next;
    }

    if (nbr->slsp_nbr_fsq) {
	SLSP_FSQ_REMOVE(nbr->slsp_nbr_fsq);
	SLSP_DB_FSQ_FREE(nbr->slsp_nbr_fsq);
	nbr->slsp_nbr_fsq = (slsp_full_seq_q *) 0;
    }
}


/*
 * slsp_db_add_placeholder - add a placeholder node to the tree
 *			     for reference by other nodes.  This is actually
 *			     the only way we add nodes, things that
 *			     want other than a placeholder convert it
 *			     to something else.
 */
static slsp_db_node *
slsp_db_add_placeholder __PF3(inp, slsp_instance *,
			      addr, sockaddr_un *,
			      found_np, slsp_db_node *)
{
    slsp_db_node *np;
    sockaddr_un *mask;

    /*
     * Decide what the mask should be.  Either the route mask, if the
     * address is "local" to the instance, or the natural network mask.
     */
    if ((sock2ip(addr) & sock2ip(inp->slsp_mask))
      == (sock2ip(inp->slsp_addr) & sock2ip(inp->slsp_mask))) {
	mask = inp->slsp_rtmask;
    } else {
	mask = inet_mask_natural(addr);
    }


    /*
     * Fetch a node.  Initialize what we can, then stick it in the tree.
     */
    np = SLSP_DB_NODE_ALLOC();
    np->slsp_node_addr = sockdup(addr);
    np->slsp_node_rtmask = mask;
    np->slsp_node_type = SLSP_LSP_PLACEHOLDER;
    np->slsp_node_onlist = SLSP_NRT_NONE;
    np->slsp_node_instance = inp;

    slsp_node_insert(np, found_np);

    /*
     * Return the result, let the caller refcount it if he wants
     */
    return np;
}


/*
 * slsp_db_init - initialize this instance's SLSP database.  What we
 *		  mostly do is create our local node and add it to the
 *		  tree.
 */
void
slsp_db_init __PF1(inp, slsp_instance *)
{
    slsp_db_node *np;

    assert(!(inp->slsp_local_node) && !(inp->slsp_nodes));

    /*
     * Fetch a node structure
     */
    SLSP_DB_NODE_GET(np);

    /*
     * Fill it in with stuff about ourselves.  At this point we
     * have no links in the node, and no AS numbers.
     */
    np->slsp_node_instance = inp;
    np->slsp_node_addr = sockdup(inp->slsp_addr);
    np->slsp_node_rtmask = inp->slsp_rtmask;
    np->slsp_node_type = SLSP_LSP_LOCALRTR;
    np->slsp_node_onlist = SLSP_NRT_NONE;
    np->slsp_node_seqnum = SLSP_SEQNUM_MIN;
    np->slsp_node_aslist.slspdb_as_prev
      = np->slsp_node_aslist.slspdb_as_next = &(np->slsp_node_aslist);
    np->slsp_node_linklist.slspdb_link_prev
      = np->slsp_node_linklist.slspdb_link_next = &(np->slsp_node_linklist);
    inp->slsp_local_node = np;

    /*
     * Put it in the tree, and on the full list.  This'll do for now.
     */
    slsp_node_insert(np, (slsp_db_node *) 0);
    SLSP_FULL_APPEND(inp, np);
    inp->slsp_full_seq_entries++;
}

/*
 * slsp_db_down - a neighbour went down
 */
void
slsp_db_down __PF1(nbr, slsp_neighbour *)
{
    slsp_instance *inp;
    slsp_db_node *np;
    slsp_db_link *lp;
    int changed_local = 0;
    int need_spf = 0;

    assert(nbr->slsp_nbr_state == SLSP_NBR_STATE_ADJACENT);
    nbr->slsp_nbr_state = SLSP_NBR_STATE_DOWN;
    inp = nbr->slsp_nbr_instance;
    np = inp->slsp_local_node;

    switch (nbr->slsp_nbr_type) {
    case SLSP_NTYPE_PTP:
    case SLSP_NTYPE_PTP_PEER:
	/*
	 * Make sure we don't send him any more stuff.
	 */
	slsp_db_send_stop(nbr);

	/*
	 * Deference his node and pull the link out of the
	 * local node.  Make the local node changed.  Blow away
	 * the link structure.
	 */
	lp = nbr->slsp_nbr_link;
	if (lp->slspdb_link_newbucket
	  && (lp->slspdb_link_next->slspdb_link_cost == lp->slspdb_link_cost)) {
	    assert(!(lp->slspdb_link_next->slspdb_link_newbucket));
	    lp->slspdb_link_next->slspdb_link_newbucket = 1;
	}
	changed_local = 1;
	if (SLSP_IS_ACTIVE_LSP(lp->slspdb_link_node)) {
	    need_spf = 1;
	}
	SLSP_LINK_REMOVE(lp);
	SLSP_NODE_DEREFERENCE(lp->slspdb_link_node);
	SLSP_DB_LINK_FREE(lp);
	nbr->slsp_nbr_link = (slsp_db_link *) 0;

	/*
	 * All done, all that remains is to flood the local node.  Done below.
	 */
	break;

    case SLSP_NTYPE_BCAST_NBR:
	/*XXX*/
    default:
	break;
    }

    /*
     * If all neighbours are gone, pull it off the lifetime expiry list.
     * We'll reoriginate it when a link is added.
     */
    if (np->slsp_node_linklist.slspdb_link_next == &(np->slsp_node_linklist)) {
	SLSP_LIFE_REMOVE(np);
	np->slsp_node_life_timeout = (time_t) 0;
    } else if (changed_local && np->slsp_node_cksum) {
	slsp_db_reoriginate(np, np->slsp_node_seqnum);
    }
    if (need_spf) {
	SLSP_SPF_SCHEDULE(inp);
    }
}


/*
 * slsp_db_ptp_up - a PTP neighbour came up
 */
void
slsp_db_ptp_up __PF1(nbr, slsp_neighbour *)
{
    slsp_instance *inp;
    slsp_db_node *np, *lcl_np;
    slsp_db_link *lp, *lp_prev;
    slsp_full_seq_q *fsp;

    assert(!(nbr->slsp_nbr_link));

    /*
     * First find him in the tree.  If he isn't there
     * add a placeholder.
     */
    inp = nbr->slsp_nbr_instance;
    SLSP_NODE_LOCATE(inp, nbr->slsp_nbr_id, np);

    if (!np || sock2ip(np->slsp_node_addr) != sock2ip(nbr->slsp_nbr_id)) {
	np = slsp_db_add_placeholder(inp,
				     nbr->slsp_nbr_id,
				     np);
    }
    SLSP_NODE_REFERENCE(np);

    /*
     * Fetch a link structure and fill it in.  Attach this to
     * the local node.
     */
    SLSP_DB_LINK_GET(lp);
    nbr->slsp_nbr_link = lp;
    lp->slspdb_link_cost = nbr->slsp_nbr_cost;
    lp->slspdb_link_node = np;
    lp->slspdb_link_nbr = nbr;
    lcl_np = inp->slsp_local_node;
    lp_prev = lcl_np->slsp_node_linklist.slspdb_link_prev;

    while (lp_prev != &(lcl_np->slsp_node_linklist)) {
	if (lp_prev->slspdb_link_cost <= lp->slspdb_link_cost) {
	    break;
	}
	lp_prev = lp_prev->slspdb_link_prev;
    }
    if (lp_prev->slspdb_link_cost != lp->slspdb_link_cost) {
	lp->slspdb_link_newbucket = 1;
    }
    SLSP_LINK_APPEND(lp_prev, lp);

    /*
     * Okay, so far so good.  Bump the sequence number on the
     * local node and mark it changed.  Then flood the local
     * node and send the full database to the guy who just came
     * up.
     */
    if (lcl_np->slsp_node_cksum || !lcl_np->slsp_node_life_timeout) {
	slsp_db_reoriginate(lcl_np, lcl_np->slsp_node_seqnum);
    }
#ifdef	notdef
    slsp_db_sendall(nbr);
#endif	/* notdef */
    SLSP_DB_FSQ_GET(fsp);
    nbr->slsp_nbr_fsq = fsp;
    fsp->slsp_fq_time = time_sec;
    fsp->slsp_fq_nbr = nbr;
    SLSP_FSQ_APPEND(&(inp->slsp_full_queue), fsp);

    /*
     * Finally, decide whether we should run the SPF or not.  If none
     * is currently scheduled, see if we have a back link or not.
     */
    if (BIT_TEST(inp->slsp_flags, SLSPF_SPF)) {
	inp->slsp_spf_requested = time_sec;
    } else if (np->slsp_node_type == SLSP_LSP_ROUTER) {
	lp = np->slsp_node_linklist.slspdb_link_next;
	while (lp) {
	    if (lp->slspdb_link_node == lcl_np) {
		break;
	    }
	    lp = lp->slspdb_link_next;
	}
	if (lp) {
	    slsp_spf_schedule(inp);
	}
    }
}


/*
 * slsp_db_bcast_up - a broadcast neighbour came up
 */
void
slsp_db_bcast_up __PF1(nbr, slsp_neighbour *)
{
    /* TBD */
}


/*
 * slsp_db_dr_inform - we have a new designated router on one of our if's
 */
void
slsp_db_dr_inform  __PF2(nbr, slsp_neighbour *,
			 nbr_dr, slsp_neighbour *)
{
    nbr->slsp_nbr_dr = nbr_dr;
    /* TBD */
}


/*
 * slsp_db_link_changed - the cost of a neighbour's link changed,
 *			  update the local lsp and arrange to reflood
 *			  it.
 */
void
slsp_db_link_changed __PF1(nbr, slsp_neighbour *)
{
    register slsp_db_link *lp, *lp_next;
    register slsp_instance *inp;
    register slsp_db_node *np;

    assert(nbr->slsp_nbr_link);
    lp = nbr->slsp_nbr_link;
    if (lp->slspdb_link_cost == nbr->slsp_nbr_cost) {
	return;
    }

    /*
     * First update the link in the local node
     */
    inp = nbr->slsp_nbr_instance;
    np = inp->slsp_local_node;
    lp_next = lp->slspdb_link_next;
    SLSP_LINK_REMOVE(lp);
    if (lp->slspdb_link_cost == lp_next->slspdb_link_cost) {
	lp_next->slspdb_link_newbucket = lp->slspdb_link_newbucket;
    }

    lp->slspdb_link_cost = nbr->slsp_nbr_cost;
    for (lp_next = np->slsp_node_linklist.slspdb_link_prev;
	 lp_next != &(np->slsp_node_linklist);
	 lp_next = lp_next->slspdb_link_prev) {
	if (lp_next->slspdb_link_cost <= lp->slspdb_link_cost) {
	    break;
	}
    }
    SLSP_LINK_APPEND(lp_next, lp);
    if (lp_next->slspdb_link_cost != lp->slspdb_link_cost) {
	lp->slspdb_link_newbucket = 1;
    } else {
	lp->slspdb_link_newbucket = 0;
    }

    /*
     * Now arrange for the node to be updated and reflooded, unless
     * it is already in the process of being reflooded.
     */
    if (np->slsp_node_cksum) {
	slsp_db_reoriginate(np, np->slsp_node_seqnum);
    }

    /*
     * And, schedule an SPF to compute the new routes
     */
    SLSP_SPF_SCHEDULE(inp);
}


/*
 * slsp_ibgp_rt_change - add/remove an AS reference from our AS database
 *  XXX blow away when rcp_routed is gone XXX
 */
void
slsp_ibgp_rt_change __PF3(ibgp_as, as_t,
			  old_rt, rt_entry *,
			  new_rt, rt_entry *)
{
    register slsp_db_as *asdbp;
    register slsp_db_as *asdb_head, *asdb_next;
    register as_t asn_old = 0;
    register as_t asn_new = 0;
    slsp_db_node *np;
    slsp_instance *inp = slsp_instance_list;
    rt_changes *rtc;
    gw_entry *gwp;
    as_path *asp;
    int changed = 0;

    if (!inp) {		/* XXX */
	return;		/* XXX */
    }			/* XXX */

    if (old_rt) {
	gwp = old_rt->rt_gwp;
	if (gwp->gw_local_as && gwp->gw_local_as != ibgp_as) {
	    asn_old = gwp->gw_local_as;
	} else {
	    rtc = old_rt->rt_head->rth_changes;
	    if (!rtc || !BIT_TEST(rtc->rtc_flags, RTCF_ASPATH)) {
		asp = old_rt->rt_aspath;
	    } else {
		asp = rtc->rtc_aspath;
	    }
	    if (asp && asp->path_len) {
		asn_old = ntohl(*PATH_SHORT_PTR(asp));
	    }
	}
    }

    if (new_rt) {
	gwp = new_rt->rt_gwp;
	if (gwp->gw_local_as && gwp->gw_local_as != ibgp_as) {
	    asn_new = gwp->gw_local_as;
	} else {
	    asp = new_rt->rt_aspath;
	    if (asp && asp->path_len) {
		asn_new = ntohl(*PATH_SHORT_PTR(asp));
	    }
	}
    }

    if (asn_old == asn_new) {
	return;
    }

    np = inp->slsp_local_node;
    asdb_head = &(np->slsp_node_aslist);
    for (asdbp = asdb_head->slspdb_as_next;
	 asdbp != asdb_head;
	 asdbp = asdb_next) {
	asdb_next = asdbp->slspdb_as_next;
	if (asn_new && asdbp->slspdb_as == asn_new) {
	    asdbp->slspdb_as_refcount++;
	    asn_new = 0;
	    if (!asn_old) {
		break;
	    }
	}
	if (asn_old && asdbp->slspdb_as == asn_old) {
	    assert(asdbp->slspdb_as_refcount);
	    if (--(asdbp->slspdb_as_refcount) == 0) {
		SLSP_ASL_REMOVE(asdbp);
		asdb_head->slspdb_as_count--;
		changed++;
		SLSP_DB_AS_FREE(asdbp);
	    }
	    asn_old = 0;
	    if (!asn_new) {
		break;
	    }
	}
    }

    assert(!asn_old);

    if (asn_new) {
	SLSP_DB_AS_GET(asdbp);
	asdbp->slspdb_as_refcount = 1;
	asdbp->slspdb_as = asn_new;
	SLSP_ASL_PREPEND(asdb_head, asdbp);
	asdb_head->slspdb_as_count++;
	changed++;
    }

    if (changed && np->slsp_node_cksum) {
	slsp_db_reoriginate(np, np->slsp_node_seqnum);
    }
}



/*
 * slsp_db_lsp - receive an LSP from a neighbour
 */
void
slsp_db_lsp __PF3(nbr, slsp_neighbour *,
		  pkt, byte *,
		  pktlen, size_t)
{
    register byte *cp = pkt;
    register size_t length;
    byte *cptmp;
    slsp_db_node *np, *fnp;
    slsp_instance *inp;
    slsp_db_link *lp, *lplist, *lplast;
    slsp_db_as *aslist, *asp;
    int lifetime;
    sockaddr_un addr;
    slsp_seqnum_t seqnum;
    slsp_cksum_t cksum;
    size_t len, plen;
    time_t his_life, my_life;
    int rttype;
    int pcode;
    int last_pcode;
    int last_cost;
    int changes = 0;
    int links = 0;

    inp = nbr->slsp_nbr_instance;

    /*
     * Sanity check the beginning of the LSP
     */
    if (pktlen < SLSP_LSP_MIN_LEN) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp: truncated LSP from %A: length %u (< %u)",
		  nbr->slsp_nbr_addr,
		  pktlen,
		  SLSP_LSP_MIN_LEN));
	goto dumppkt;
    }

    if (SLSP_GET_LEADER_LENGTH(cp) != SLSP_LSP_FIXED_LEN) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp: LSP from %A: fixed length screwed (%u != %u)",
		  nbr->slsp_nbr_addr,
		  SLSP_GET_LEADER_LENGTH(cp),
		  SLSP_LSP_FIXED_LEN));
	goto dumppkt;
    }

    if (!SLSP_CHK_LSP(cp)) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp: LSP from %A failed fixed fields check",
		  nbr->slsp_nbr_addr));
	goto dumppkt;
    }

    /*
     * Okay.  Fetch the guts from the packet header.
     */
    sockclear_in(&addr);
    SLSP_GET_LSP(length, lifetime, &addr, rttype, seqnum, cksum, cp);

    /*
     * Check to make sure the total length doesn't exceed what we have,
     * and is greater than or equal to the fixed length of the packet.
     * Make sure the lifetime is sensible.
     */
    if (length > pktlen) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp: LSP from %A: LSP length %u greater than packet length %u",
		  nbr->slsp_nbr_addr,
		  length,
		  pktlen));
	goto dumppkt;
    }

    if (length < SLSP_LSP_MIN_LEN) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp: LSP from %A: LSP length %u less than minimum length %u (packet %u)",
		  nbr->slsp_nbr_addr,
		  length,
		  SLSP_LSP_MIN_LEN,
		  pktlen));
	goto dumppkt;
    }

    if (lifetime > SLSP_LIFE_MAX) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp: LSP from %A has lifetime %u greater than protocol maximum %u",
		  nbr->slsp_nbr_addr,
		  lifetime,
		  SLSP_LIFE_MAX));
	goto dumppkt;
    }

    /*
     * Check the checksum using the length found in the packet.  Zero the
     * lifetime to do this right.
     */
    SLSP_PUT_LSP_LIFETIME(0, pkt);
    if (!SLSP_CHK_LSP_CKSUM(length, pkt)) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp: LSP from %A checksum failed",
		  nbr->slsp_nbr_addr));
	goto dumppkt;
    }

    /*
     * Finally, check the payload for fixed field sensibility.  The
     * AS payloads should come before the link payloads.
     */
    len = length - SLSP_LSP_FIXED_LEN;
    last_pcode = 0;
    last_cost = 256;
    cptmp = cp;
    while (len) {
	if (len == 1) {
	    trace_tf(nbr->slsp_nbr_trace_options,
		     TR_ALL,
		     LOG_WARNING,
		     ("slsp_db_lsp: LSP from %A ID %A one byte remaining in payload",
		      nbr->slsp_nbr_addr,
		      &addr));
	    goto dumppkt;
	}
	pcode = (int) *cp++;
	plen = (size_t) *cp++;
	len -= 2;

	if (plen > len) {
	    trace_tf(nbr->slsp_nbr_trace_options,
		     TR_ALL,
		     LOG_WARNING,
		     ("slsp_db_lsp: LSP from %A ID %A payload length %u but %u byte%s left in packet",
		      nbr->slsp_nbr_addr,
		      &addr,
		      plen,
		      len,
		      ((len == 1) ? "" : "s")));
		goto dumppkt;
	}
	len -= plen;
	if (plen == 0) {
	    trace_tf(nbr->slsp_nbr_trace_options,
		     TR_ALL,
		     LOG_WARNING,
		     ("slsp_db_lsp: LSP from %A ID %A payload type %u zero length",
		      nbr->slsp_nbr_addr,
		      &addr,
		      pcode));
	    goto dumppkt;
	}
	if (pcode == SLSP_LSP_CODE_AS) {
	    if (last_pcode == SLSP_LSP_CODE_LINKS) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_ALL,
			 LOG_WARNING,
			 ("slsp_db_lsp: LSP from %A ID %A has AS links after router links!",
			  nbr->slsp_nbr_addr,
			  &addr));
		goto dumppkt;
	    }
	    last_pcode = SLSP_LSP_CODE_AS;
	    if ((plen % SLSP_LSP_AS_LEN)) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_ALL,
			 LOG_WARNING,
			 ("slsp_db_lsp: LSP from %A ID %A AS links payload length %u should be multiple of %u",
			  nbr->slsp_nbr_addr,
			  &addr,
			  plen,
			  SLSP_LSP_AS_LEN));
		goto dumppkt;
	    }
	    do {
		if (!SLSP_CHK_LSP_AS(cp)) {
		    trace_tf(nbr->slsp_nbr_trace_options,
			     TR_ALL,
			     LOG_WARNING,
			     ("slsp_db_lsp: LSP from %A ID %A AS fixed field check failed",
			      nbr->slsp_nbr_addr,
			      &addr));
		    goto dumppkt;
		}
		cp += SLSP_LSP_AS_LEN;
		plen -= SLSP_LSP_AS_LEN;
	    } while (plen);
	} else if (pcode == SLSP_LSP_CODE_LINKS) {
	    if (plen < (SLSP_LSP_LINK_MIN_LEN + SLSP_LSP_LINK_ID_LEN)) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_ALL,
			 LOG_WARNING,
			 ("slsp_db_lsp: LSP from %A ID %A link payload length %u too small (< %u)",
			  nbr->slsp_nbr_addr,
			  &addr,
			  plen,
			  (SLSP_LSP_LINK_MIN_LEN + SLSP_LSP_LINK_ID_LEN)));
		goto dumppkt;
	    }
	    plen -= SLSP_LSP_LINK_MIN_LEN;
	    if ((plen % SLSP_LSP_LINK_ID_LEN)) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_ALL,
			 LOG_WARNING,
			 ("slsp_db_lsp: LSP from %A ID %A link payload length %u improper",
			  nbr->slsp_nbr_addr,
			  &addr,
			  (plen + SLSP_LSP_MIN_LEN)));
		goto dumppkt;
	    }
	    if (*cp++ != 0) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_ALL,
			 LOG_WARNING,
			 ("slsp_db_lsp: LSP from %A ID %A link leader byte value %u should be zero",
			  nbr->slsp_nbr_addr,
			  &addr,
			  *(--cp)));
		goto dumppkt;
	    }
	    if (last_pcode != SLSP_LSP_CODE_LINKS) {
		last_cost = (int) *cp;
	    } else if ((last_cost == 0 || *cp == 0)
	      && (int)(*cp) != last_cost) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_ALL,
			 LOG_WARNING,
			 ("slsp_db_lsp: LSP from %A ID %A has both zero cost and non-zero cost links",
			  nbr->slsp_nbr_addr,
			  &addr));
		goto dumppkt;
	    }
	    cp++;
	    last_pcode = SLSP_LSP_CODE_LINKS;

	    do {
		if (!SLSP_CHK_LINK_ID(cp)) {
		    trace_tf(nbr->slsp_nbr_trace_options,
			     TR_ALL,
			     LOG_WARNING,
			     ("slsp_db_lsp: LSP from %A ID %A router link entry failed fixed field checks",
			      nbr->slsp_nbr_addr,
			      &addr));
		    goto dumppkt;
		}
		cp += SLSP_LSP_LINK_ID_LEN;
		plen -= SLSP_LSP_LINK_ID_LEN;
	    } while (plen);
	} else {
	    trace_tf(nbr->slsp_nbr_trace_options,
		     TR_ALL,
		     LOG_WARNING,
		     ("slsp_db_lsp: LSP from %A ID %A unknown payload type %u",
		      nbr->slsp_nbr_addr,
		      &addr,
		      pcode));
	    goto dumppkt;
	}
    }
    cp = cptmp;

    /*
     * Okay, this packet is what the guy sent, and seems to be one
     * we can rebuild.  See if we have an existing node for this guy.
     * If so, see if his is older than ours, if so we can send him ours
     * and forget about it.
     */
    fnp = (slsp_db_node *) 0;
    SLSP_NODE_LOCATE(inp, &addr, np);
    if (sock2ip(np->slsp_node_addr) != sock2ip(&addr)) {
	fnp = np;
	np = (slsp_db_node *) 0;
    }

    if (lifetime == 0) {
	if (np && np->slsp_node_type != SLSP_LSP_PLACEHOLDER) {
	    /*
	     * If his sequence number is older than ours, or our
	     * LSP is relatively new, send him ours instead.
	     */
	    if ((np->slsp_node_type != SLSP_LSP_TIMEDOUT
		&& np->slsp_node_life > (time_sec + SLSP_LIFE_MAXDIFF))
	      || SLSP_SEQNUM_OLDER(seqnum, np->slsp_node_seqnum)) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_NORMAL,
			 0,
			 ("slsp_db_lsp: expired LSP from %A for node %A his sequence %08x ours %08x sending him ours%s",
			  nbr->slsp_nbr_addr,
			  np->slsp_node_addr,
			  seqnum,
		 	  np->slsp_node_seqnum,
			  ((np->slsp_node_type == SLSP_LSP_TIMEDOUT) ?
			    "" : " (unexpired)")));
		slsp_db_sendto(nbr, np);
		return;
	    }

	    /*
	     * If this is a local LSP, reoriginate it
	     */
	    if (SLSP_IS_LOCAL_LSP(np)) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_NORMAL,
			 0,
			 ("slsp_db_lsp: expired LSP from %A local ID %A, reoriginating",
			  nbr->slsp_nbr_addr,
			  np->slsp_node_addr,
			  np->slsp_node_seqnum,
			  seqnum));
		slsp_db_reoriginate(np, seqnum);
		return;
	    }

	    /*
	     * If his sequence number is newer, or if it is the
	     * same but we are unexpired, (re)expire it.  Otherwise
	     * treat this as an ack.
	     */
	    if (seqnum != np->slsp_node_seqnum
	      || np->slsp_node_type != SLSP_LSP_TIMEDOUT) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_NORMAL,
			 0,
			 ("slsp_db_lsp: expired LSP from %A for node %A %sexpiring ours too",
			  nbr->slsp_nbr_addr,
			  np->slsp_node_addr,
			  ((np->slsp_node_type == SLSP_LSP_TIMEDOUT)
			   ? "re" : "")));
		slsp_db_expire(np, seqnum, nbr);
	    } else {
		/*
		 * Treat this as an ack for the zero age LSP.  This
		 * may cause us to remove it from the database or
		 * turn it into a placeholder.
		 */
		SLSP_BIT_CLR(&(np->slsp_node_xmt_bits), nbr);
		if (np->slsp_node_xmt_nbits == 0) {
		    trace_tf(nbr->slsp_nbr_trace_options,
			     TR_NORMAL,
			     0,
			     ("slsp_db_lsp: expired LSP from %A for node %A completely acks our LSP",
			      nbr->slsp_nbr_addr,
			      np->slsp_node_addr));
		    SLSP_XMT_REMOVE(np);
		    if (np->slsp_node_type == SLSP_LSP_TIMEDOUT) {
			SLSP_LIFE_REMOVE(np);
			if (np->slsp_node_refcount) {
			    np->slsp_node_type = SLSP_LSP_PLACEHOLDER;
			    np->slsp_node_cksum = 0;
			    np->slsp_node_life = (time_t) 0;
			    np->slsp_node_life_timeout = (time_t) 0;
			} else {
			    slsp_node_remove(np);
			    if (np->slsp_node_addr) {
				sockfree(np->slsp_node_addr);
			    }
			    SLSP_DB_NODE_FREE(np);
			}
		    }
		} else {
		    trace_tf(nbr->slsp_nbr_trace_options,
			     TR_NORMAL,
			     0,
			     ("slsp_db_lsp: expired LSP from %A for node %A acks ours, %u remaining",
			      nbr->slsp_nbr_addr,
			      np->slsp_node_addr,
			      np->slsp_node_xmt_nbits));
		}
	    }
	} else {
	    trace_tf(nbr->slsp_nbr_trace_options,
		     TR_NORMAL,
		     0,
		     ("slsp_db_lsp: expired LSP from %A for node %A unknown to us, just acking/ignoring",
		      nbr->slsp_nbr_addr,
		      &addr));
	}

	/*
	 * Send an ack to shut him up
	 */
	if (SLSP_NBR_PTP_TYPE(nbr)) {
	    SLSP_DB_ADD_ACK(nbr,
			    SLSP_SEQ_ENT_TYPE_ROUTER,
			    &addr,
			    seqnum,
			    lifetime,
			    cksum);
	}
	return;
    }

    his_life = (time_t) lifetime + time_sec;
    if (np->slsp_node_type == SLSP_LSP_TIMEDOUT) {
	my_life = time_sec;
    } else {
	my_life = np->slsp_node_life;
    }
    if (np && np->slsp_node_type != SLSP_LSP_PLACEHOLDER
      && his_life < (my_life + SLSP_LIFE_MAXDIFF)) {
	if (my_life > (his_life + SLSP_LIFE_MAXDIFF)
	  || SLSP_SEQNUM_OLDER(seqnum, np->slsp_node_seqnum)) {
	    /*
	     * His is older than ours, send him
	     * our node instead and forget about it.
	     */
	    trace_tf(nbr->slsp_nbr_trace_options,
		     TR_NORMAL,
		     0,
		     ("slsp_db_lsp: LSP from %A for node %A his is older sending him ours",
		      nbr->slsp_nbr_addr,
		      np->slsp_node_addr));
	    slsp_db_sendto(nbr, np);
	    return;
	}
	if (seqnum == np->slsp_node_seqnum) {
	    /*
	     * Same sequence number, and he has some life left.
	     * Check the checksums.  If they match the only thing we'll
	     * do is use his lifetime if it is smaller than ours.
	     */
	    if (np->slsp_node_type != SLSP_LSP_TIMEDOUT
	      && cksum == np->slsp_node_cksum) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_NORMAL,
			 0,
			 ("slsp_db_lsp: LSP from %A for node %A same as ours, his lifetime %s",
			  nbr->slsp_nbr_addr,
			  np->slsp_node_addr,
			  ((his_life < np->slsp_node_life)
			    ? "shorter" : "same or longer")));
		if (his_life < np->slsp_node_life && !SLSP_IS_LOCAL_LSP(np)) {
		    np->slsp_node_life = his_life;
		    np->slsp_node_life_timeout = his_life;
		    fnp = SLSP_LIFE_PREV(inp, np);
		    if (fnp && fnp->slsp_node_life_timeout > his_life) {
			SLSP_LIFE_REMOVE(np);
			while ((fnp = SLSP_LIFE_PREV(inp, fnp))) {
			    if (fnp->slsp_node_life_timeout <= his_life) {
				break;
			    }
			}
			if (fnp) {
			    SLSP_LIFE_APPEND(fnp, np);
			} else {
			    SLSP_LIFE_APPEND(inp, np);
			}
		    }
		    if (!fnp) {
			SLSP_DB_LIFE_SCHEDULE(inp);
		    }
		}

		/*
		 * If he is a point-to-point neighbour, we'll need to
		 * ack to shut him up.  Do it now.
		 */
		if (SLSP_NBR_PTP_TYPE(nbr)) {
		    SLSP_DB_ADD_ACK(nbr,
				    SLSP_SEQ_ENT_TYPE_ROUTER,
				    &addr,
				    seqnum,
				    lifetime,
				    cksum);
		}

		if (np->slsp_node_xmt_nbits) {
		    SLSP_BIT_CLR(&(np->slsp_node_xmt_bits), nbr);
		    if (np->slsp_node_xmt_nbits == 0) {
			SLSP_XMT_REMOVE(np);
		    }
		}
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_NORMAL,
			 0,
			 ("slsp_db_lsp: LSP from %A ID %A one we know already",
			  nbr->slsp_nbr_addr,
			  np->slsp_node_addr));
		return;
	    }

	    /*
	     * Here we have the same sequence numbers but a mismatched
	     * checksum.  Expire this node, unless it is local or
	     * already expired.
	     */
	    if (np->slsp_node_type == SLSP_LSP_TIMEDOUT) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_NORMAL,
			 0,
			 ("slsp_db_lsp: LSP from %A for node %A ours expired, sending him ours",
			  nbr->slsp_nbr_addr,
			  np->slsp_node_addr));
		slsp_db_sendto(nbr, np);
		return;
	    }
	    if (!SLSP_IS_LOCAL_LSP(np)) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_NORMAL,
			 0,
			 ("slsp_db_lsp: LSP from %A for node %A same sequence number but mismatched checksum: expired",
			  nbr->slsp_nbr_addr,
			  np->slsp_node_addr));
		slsp_db_expire(np, seqnum, (slsp_neighbour *) 0);
		return;
	    }
	}

    }

    /*
     * Okay, here we have a situation where his LSP is better than
     * the one we know already.  If this is a locally-originated
     * LSP then re-originate it with a better sequence number.
     */
    if (SLSP_IS_LOCAL_LSP(np)) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_NORMAL,
		 0,
		 ("slsp_db_lsp: LSP from %A local ID %A our sequence %08x his %08x, reoriginating",
		  nbr->slsp_nbr_addr,
		  np->slsp_node_addr,
		  np->slsp_node_seqnum,
		  seqnum));
	slsp_db_reoriginate(np, seqnum);
	return;
    }

    /*
     * Okay, we need to dig the useful data out of this packet.  By now
     * we know this is not a local LSP.  Just record the AS list as
     * it comes out of the packet, but check through the links list so
     * we can tell if it changed.
     */
    if (np) {
	aslist = np->slsp_node_aslist.slspdb_as_next;
	np->slsp_node_aslist.slspdb_as_next = (slsp_db_as *) 0;
	np->slsp_node_aslist.slspdb_as_count = 0;
	lplist = np->slsp_node_linklist.slspdb_link_next;
	lplast = np->slsp_node_linklist.slspdb_link_next = (slsp_db_link *) 0;
    } else {
	aslist = (slsp_db_as *) 0;
	lplast = lplist = (slsp_db_link *) 0;
	np = slsp_db_add_placeholder(inp, &addr, fnp);
    }
    np->slsp_node_seqnum = seqnum;
    np->slsp_node_cksum = cksum;
    np->slsp_node_len = length;
    np->slsp_node_life = time_sec + lifetime;
    if (rttype == SLSP_LSP_RTTYPE_ALT) {
	BIT_SET(np->slsp_node_flags, SLSP_NODEF_RTTYPE_ALT);
    } else {
	BIT_RESET(np->slsp_node_flags, SLSP_NODEF_RTTYPE_ALT);
    }

    len = length - SLSP_LSP_FIXED_LEN;
    asp = (slsp_db_as *) 0;
    while (len) {
	pcode = (int)(*cp++);
	plen = (size_t)(*cp++);
	last_pcode = 1;
	len -= (2 + plen);
	if (pcode == SLSP_LSP_CODE_AS) {
	    do {
		if (!aslist) {
		    SLSP_DB_AS_GET(aslist);
		}
		if (asp) {
		    asp->slspdb_as_next = aslist;
		} else {
		    np->slsp_node_aslist.slspdb_as_next = aslist;
		}
		asp = aslist;
		aslist = asp->slspdb_as_next;
		asp->slspdb_as_next = (slsp_db_as *) 0;
		asp->slspdb_as_newbucket = last_pcode;
		last_pcode = 0;
		SLSP_GET_LSP_AS(asp->slspdb_as, cp);
		np->slsp_node_aslist.slspdb_as_count++;
	    } while ((plen -= SLSP_LSP_AS_LEN));
	} else {
	    register byte cost;

	    SLSP_GET_LINK_COST(cost, cp);
	    plen -= 2;
	    do {
		SLSP_GET_LINK_ID(&addr, cp);

		if (lplist) {
		    register slsp_db_link *lp_prev = (slsp_db_link *) 0;

		    lp = lplist;
		    do {
			if (lp->slspdb_link_cost == cost
			  && sock2ip(lp->slspdb_link_node->slsp_node_addr)
			  == sock2ip(&addr)) {
			    break;
			}
			lp_prev = lp;
			lp = lp->slspdb_link_next;
		    } while (lp);

		    if (lp) {
			if (!lp_prev) {
			    lplist = lp->slspdb_link_next;
			} else {
			    lp_prev->slspdb_link_next = lp->slspdb_link_next;
			}
			lp->slspdb_link_next = (slsp_db_link *) 0;
		    }
		} else {
		    lp = (slsp_db_link *) 0;
		}

		if (!lp) {
		    register slsp_db_node *link_np;

		    SLSP_DB_LINK_GET(lp);
		    lp->slspdb_link_cost = cost;
		    SLSP_NODE_LOCATE(inp, &addr, link_np);
		    if (!link_np
		      || sock2ip(link_np->slsp_node_addr) != sock2ip(&addr)) {
			link_np = slsp_db_add_placeholder(inp, &addr, link_np);
		    } else if (np->slsp_node_refcount) {
			changes++;	/* assume changed */
		    }
		    SLSP_NODE_REFERENCE(link_np);
		    lp->slspdb_link_node = link_np;
		}

		lp->slspdb_link_newbucket = last_pcode;
		last_pcode = 0;
		if (lplast) {
		    lplast->slspdb_link_next = lp;
		} else {
		    np->slsp_node_linklist.slspdb_link_next = lp;
		}
		lplast = lp;
		links++;
	    } while ((plen -= SLSP_LSP_LINK_ID_LEN));
	}
    }

    /*
     * Any links remaining on the list were links he didn't include.
     * Dereference the guys they point to and free the structures.
     * Note if this may have caused changes to our routing.
     */
    while (lplist) {
	lp = lplist;
	lplist = lp->slspdb_link_next;
	if (SLSP_IS_ACTIVE_LSP(lp->slspdb_link_node)) {
	    changes++;
	}
	SLSP_NODE_DEREFERENCE(lp->slspdb_link_node);
	SLSP_DB_LINK_FREE(lp);
    }


    trace_tf(nbr->slsp_nbr_trace_options,
	     TR_NORMAL,
	     0,
	     ("slsp_db_lsp: LSP from %A ID %A received: ases %u links %u",
	      nbr->slsp_nbr_addr,
	      np->slsp_node_addr,
	      np->slsp_node_aslist.slspdb_as_count,
	      links));

    /*
     * Okay, got a new LSP.  Flood it to everyone except the guy
     * who sent it.  Send him an ACK if he's a point-to-point man.
     * Make sure an SPF is scheduled if something changed.
     */
    if (np->slsp_node_life_timeout) {
	SLSP_LIFE_REMOVE(np);
    }
    np->slsp_node_life_timeout = np->slsp_node_life;
    fnp = SLSP_LIFE_PREV(inp, inp);
    while (fnp) {
	if (fnp->slsp_node_life_timeout <= np->slsp_node_life_timeout) {
	    break;
	}
	fnp = SLSP_LIFE_PREV(inp, fnp);
    }
    SLSP_LIFE_APPEND((fnp ? fnp : ((slsp_db_node *) inp)), np);
    if (!fnp) {
	SLSP_DB_LIFE_SCHEDULE(inp);
    }
    if (!SLSP_IS_ACTIVE_LSP(np)) {
	SLSP_FULL_PREPEND(inp, np);
	inp->slsp_full_seq_entries++;
    }
    np->slsp_node_type = SLSP_LSP_ROUTER;	/* XXX */
    slsp_db_flood(np, nbr);
    if (SLSP_NBR_PTP_TYPE(nbr)) {
	SLSP_DB_ADD_ACK(nbr,
			SLSP_SEQ_ENT_TYPE_ROUTER,
			np->slsp_node_addr,
			seqnum,
			lifetime,
			cksum);
    }
    if (changes) {
	SLSP_SPF_SCHEDULE(inp);
    }
    return;

dumppkt:
    if (nbr->slsp_nbr_trace_options) {
	slsp_pktdump(nbr->slsp_nbr_trace_options,
		     "SLSP ERR ",
		     pkt,
		     pktlen);
    }
}


/*
 * slsp_db_seq - receive a neighbour's sequence number packet
 */
void
slsp_db_seq __PF3(nbr, slsp_neighbour *,
		  pkt, byte *,
		  pktlen, size_t)
{
    register byte *cp;    
    register slsp_db_node *np;
    size_t length;
    sockaddr_un addr, naddr;
    slsp_list list;
    slsp_seqnum_t seqnum;
    slsp_cksum_t cksum;
    time_t lifetime;
    slsp_instance *inp;
    int stype, etype;
    int count;
    int ours_newer;
    int acks = 0;
    int requests = 0;
    int sends = 0;

    /*
     * Sanity check, then fetch the parameters out of the fixed part.
     */
    if (pktlen < SLSP_SEQ_FIXED_LEN) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_seq: sequence numbers packet from %A length %u less than minimum of %u",
		  nbr->slsp_nbr_addr,
		  pktlen,
		  SLSP_SEQ_FIXED_LEN));
	goto dumppkt;
    }
    cp = pkt;
    if (!SLSP_CHK_SEQ(cp)) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_seq: sequence numbers packet from %A failed fixed field check",
		  nbr->slsp_nbr_addr));
	goto dumppkt;
    }

    sockclear_in(&addr);
    SLSP_GET_SEQ(&addr, stype, length, count, cp);
    if (stype != SLSP_SEQ_FULL && stype != SLSP_SEQ_PARTIAL) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_seq: sequence numbers packet from %A ID %A has unknown type %u",
		  nbr->slsp_nbr_addr,
		  &addr,
		  stype));
	goto dumppkt;
    }
    stype -= SLSP_SEQ_PARTIAL;

    if (length > pktlen) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_seq: sequence numbers packet from %A ID %A indicated length %u exceeds packet length %u",
		  nbr->slsp_nbr_addr,
		  &addr,
		  length,
		  pktlen));
	goto dumppkt;
    }
    if (SLSP_SEQ_LENGTH(count) != length) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_seq: sequence numbers packet from %A ID %A indicated length %u inconsistent with %u entr%s (expect %u)",
		  nbr->slsp_nbr_addr,
		  &addr,
		  length,
		  count,
		  ((count == 1) ? "y" : "ies"),
		  SLSP_SEQ_LENGTH(count)));
	goto dumppkt;
    }
    /* XXX do something with ID? */

    /*
     * Okay, we seem to have a sane packet.  If this is a full sequence
     * numbers packet we need to compare it to our full database, otherwise
     * just worry about the stuff he actually sent.
     *
     * For every entry we look up the node in our database.  If we don't
     * have the node, or if we have an older one than he does, we request
     * he send it in an ACK (i.e. our own sequence numbers packet).  If
     * his is the same as ours, we pull this node from the retransmit
     * queue if it is there.  If his is older than ours we send him ours.
     *
     * Don't bother checking the fixed fields, not sure what this could
     * help anyway.  If the packet is mangled we'll send a bit of extra
     * stuff, but it'll work itself out.
     */
    inp = nbr->slsp_nbr_instance;
    if (stype) {
	list.slsp_full_prev = inp->slsp_lists.slsp_full_prev;
	list.slsp_full_prev->slsp_full_next = &list;
	list.slsp_full_next = inp->slsp_lists.slsp_full_next;
	list.slsp_full_next->slsp_full_prev = &list;
	SLSP_FULL_INIT(&(inp->slsp_lists));
    }
    sockclear_in(&naddr);

    while (count--) {
	SLSP_GET_SEQ_ENT(etype, &naddr, seqnum, lifetime, cksum, cp);
	if (etype) {
	    trace_tf(nbr->slsp_nbr_trace_options,
		     TR_NORMAL,
		     0,
		     ("slsp_db_seq: sequence numbers packet from %A ID %A entry %A type %s ignored",
		      nbr->slsp_nbr_addr,
		      &addr,
		      &naddr,
		      ((etype == SLSP_SEQ_ENT_TYPE_EXT)
			? "useless ext" : "unknown")));
	    continue;
	}

	SLSP_NODE_LOCATE(inp, &naddr, np);
	if (!np
	  || sock2ip(np->slsp_node_addr) != sock2ip(&naddr)
	  || np->slsp_node_type == SLSP_LSP_PLACEHOLDER) {
	    /*
	     * One we don't know, ask him for it.
	     */
	    trace_tf(nbr->slsp_nbr_trace_options,
		     TR_NORMAL,
		     0,
		     ("slsp_db_seq: sequence numbers from %A ID %A not known to us, requesting it",
		      nbr->slsp_nbr_addr,
		      &addr));
	    SLSP_DB_ADD_ACK(nbr,
			    SLSP_SEQ_ENT_TYPE_ROUTER,
			    &naddr,
			    SLSP_SEQNUM_MIN,
			    0,
			    0);
	    requests++;
	    continue;
	}

	if (stype && np->slsp_node_type != SLSP_LSP_TIMEDOUT) {
	    SLSP_FULL_REMOVE(np);
	    SLSP_FULL_PREPEND(inp, np);
	}
	ours_newer = 0;
	if (np->slsp_node_type == SLSP_LSP_TIMEDOUT) {
	    if (lifetime > SLSP_LIFE_MAXDIFF) {
		ours_newer = (-1);
	    }
	} else {
	    if ((np->slsp_node_life + SLSP_LIFE_MAXDIFF) <
	      (lifetime + time_sec)) {
		ours_newer = (-1);
	    } else if (np->slsp_node_life
	      > (lifetime + time_sec + SLSP_LIFE_MAXDIFF)) {
		ours_newer = 1;
	    }
	}
	if (!ours_newer) {
	    if (SLSP_SEQNUM_OLDER(np->slsp_node_seqnum, seqnum)) {
		ours_newer = (-1);
	    } else if (np->slsp_node_seqnum != seqnum) {
		ours_newer = 1;
	    }
	}

	if (ours_newer < 0) {
	    /*
	     * Our's is older, ask him for his unless this is
	     * a local packet in which case we reoriginate.
	     */
	    if (SLSP_IS_LOCAL_LSP(np)) {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_NORMAL,
			 0,
			 ("slsp_db_seq: sequence numbers from %A ID %A is local and better, jumping his sequence",
			  nbr->slsp_nbr_addr,
			  &addr));
		sends++;
		slsp_db_reoriginate(np, seqnum);
	    } else {
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_NORMAL,
			 0,
			 ("slsp_db_seq: sequence numbers from %A ID %A better than ours, requesting it",
			  nbr->slsp_nbr_addr,
			  &addr));
		requests++;
		SLSP_DB_ADD_ACK(nbr,
				SLSP_SEQ_ENT_TYPE_ROUTER,
				&naddr,
				SLSP_SEQNUM_MIN,
				0,
				0);
	    }
	    continue;
	}

	if (!ours_newer) {
	    /*
	     * Sequence number match.  If his lifetime is zero and
	     * so is ours, or if the cksums match, remove his transmit
	     * bit from the LSP if set.  We're in agreement.
	     */
	    if ((lifetime == 0
	      && (np->slsp_node_type == SLSP_LSP_TIMEDOUT
		|| np->slsp_node_life <= (time_sec + 10)))
	      || cksum == np->slsp_node_cksum) {
		acks++;
		trace_tf(nbr->slsp_nbr_trace_options,
			 TR_NORMAL,
			 0,
			 ("slsp_db_seq: sequence numbers from %A ID %A matches ours",
			  nbr->slsp_nbr_addr,
			  &addr));
		if (np->slsp_node_xmt_nbits) {
		    SLSP_BIT_CLR(&(np->slsp_node_xmt_bits), nbr);
		    if (np->slsp_node_xmt_nbits == 0) {
			SLSP_XMT_REMOVE(np);
			if (np->slsp_node_type == SLSP_LSP_TIMEDOUT) {
			    SLSP_LIFE_REMOVE(np);
			    if (np->slsp_node_refcount) {
				np->slsp_node_type = SLSP_LSP_PLACEHOLDER;
				np->slsp_node_cksum = 0;
			 	np->slsp_node_life = (time_t) 0;
				np->slsp_node_life_timeout = (time_t) 0;
			    } else {
				slsp_node_remove(np);
				if (np->slsp_node_addr) {
				    sockfree(np->slsp_node_addr);
				}
				SLSP_DB_NODE_FREE(np);
			    }
			}
		    }
		}
		continue;
	    }
	}

	/*
	 * About the only thing left to us is to send him ours.  Do so now.
	 */
	sends++;
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_NORMAL,
		 0,
		 ("slsp_db_seq: sequence numbers from %A ID %A worse/mismatched with ours, sending ours",
		  nbr->slsp_nbr_addr,
		  &addr));
	slsp_db_sendto(nbr, np);
    }

    /*
     * Done with that.  If this was a full sequence numbers packet it
     * remains to walk the remainder of the full list processing the
     * guys who weren't included in the packet.  Send each of these
     * to him.  If there were no requests in there and we're sending
     * a full sequence numbers packet, stop it.  We're synchronized.
     */
    if (stype) {
	register slsp_db_node *np_next;

	np_next = SLSP_FULL_NEXT(&list, &list);
	while ((np = np_next)) {
	    np_next = SLSP_FULL_NEXT(&list, np);
	    sends++;
	    slsp_db_sendto(nbr, np);
	    SLSP_FULL_REMOVE(np);
	    SLSP_FULL_PREPEND(inp, np);
	}

	if (!requests && nbr->slsp_nbr_fsq) {
	    SLSP_FSQ_REMOVE(nbr->slsp_nbr_fsq);
	    SLSP_DB_FSQ_FREE(nbr->slsp_nbr_fsq);
	    nbr->slsp_nbr_fsq = (slsp_full_seq_q *) 0;
	}
    }

    trace_tf(nbr->slsp_nbr_trace_options,
	     TR_NORMAL,
	     0,
	     ("slsp_db_seq: sequence numbers packet from %A ID %A acks %d sends %d requests %d",
	      nbr->slsp_nbr_addr,
	      &addr,
	      acks,
	      sends,
	      requests));
    return;

dumppkt:
    if (nbr->slsp_nbr_trace_options) {
	slsp_pktdump(nbr->slsp_nbr_trace_options,
		     "SLSP ERR ",
		     pkt,
		     pktlen);
    }
}


/*
 * slsp_db_lsp_useless - receive and ACK the useless ext LSPs that
 *			 rcp_routed sends for some unknown reason.
 */
void
slsp_db_lsp_useless __PF3(nbr, slsp_neighbour *,
			  pkt, byte *,
			  pktlen, size_t)
{
    register byte *cp = pkt;
    register size_t length;
    int lifetime;
    sockaddr_un addr;
    slsp_seqnum_t seqnum;
    slsp_cksum_t cksum;
    int rttype;

    /*
     * Sanity check the beginning of the LSP
     */
    if (pktlen < SLSP_EXT_FIXED_LEN) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp_useless: truncated ext LSP from %A: length %u (< %u)",
		  nbr->slsp_nbr_addr,
		  pktlen,
		  SLSP_EXT_FIXED_LEN));
	goto dumppkt;
    }

    if (SLSP_GET_LEADER_LENGTH(cp) != SLSP_EXT_FIXED_LEN) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp_useless: ext LSP from %A: fixed length screwed (%u != %u)",
		  nbr->slsp_nbr_addr,
		  SLSP_GET_LEADER_LENGTH(cp),
		  SLSP_EXT_FIXED_LEN));
	goto dumppkt;
    }

    if (!SLSP_CHK_EXT(cp)) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp_useless: ext LSP from %A failed fixed fields check",
		  nbr->slsp_nbr_addr));
	goto dumppkt;
    }

    /*
     * Okay.  Fetch the guts from the packet header.
     */
    sockclear_in(&addr);
    SLSP_GET_LSP(length, lifetime, &addr, rttype, seqnum, cksum, cp);

    /*
     * Check to make sure the total length doesn't exceed what we have,
     * and is greater than or equal to the fixed length of the packet.
     * Make sure the lifetime is sensible.
     */
    if (length > pktlen) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp_useless: ext LSP from %A ID %A: LSP length %u greater than packet length %u",
		  nbr->slsp_nbr_addr,
		  &addr,
		  length,
		  pktlen));
	goto dumppkt;
    }

    if (length < SLSP_EXT_FIXED_LEN) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp_useless: ext LSP from %A ID %A: LSP length %u less than minimum length %u (packet %u)",
		  nbr->slsp_nbr_addr,
		  &addr,
		  length,
		  SLSP_EXT_FIXED_LEN,
		  pktlen));
	goto dumppkt;
    }

    if (lifetime > SLSP_LIFE_MAX) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp_useless: ext LSP from %A has lifetime %u greater than protocol maximum %u",
		  nbr->slsp_nbr_addr,
		  lifetime,
		  SLSP_LIFE_MAX));
	goto dumppkt;
    }

    /*
     * Check the checksum using the length found in the packet.  Zero the
     * lifetime to do this right.
     */
    SLSP_PUT_EXT_LIFETIME(0, pkt);
    if (!SLSP_CHK_EXT_CKSUM(length, pkt)) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp_useless: ext LSP from %A ID %A: checksum failed",
		  nbr->slsp_nbr_addr,
		  &addr));
	goto dumppkt;
    }

    /*
     * If the length is other than the fixed length, emit a warning
     * that rcp_routed has done something unexpected.
     */
    if (length != SLSP_EXT_FIXED_LEN) {
	trace_tf(nbr->slsp_nbr_trace_options,
		 TR_ALL,
		 LOG_WARNING,
		 ("slsp_db_lsp_useless: ext LSP from %A ID %A has %u bytes of payload (don't ask me what)!",
		  nbr->slsp_nbr_addr,
		  &addr,
		  (length - SLSP_EXT_FIXED_LEN)));
    }

    /*
     * Just ack the stupid thing, it doesn't matter anyway.
     */
    SLSP_DB_ADD_ACK(nbr,
		    SLSP_SEQ_ENT_TYPE_EXT,
		    &addr,
		    seqnum,
		    lifetime,
		    cksum);
    return;

dumppkt:
    if (nbr->slsp_nbr_trace_options) {
	slsp_pktdump(nbr->slsp_nbr_trace_options,
		     "SLSP ERR ",
		     pkt,
		     pktlen);
    }
}


/*
 * slsp_db_terminate - shut down this instance's SLSP database
 */
void
slsp_db_terminate __PF1(inp, slsp_instance *)
{
    /* TBD */
}


/*
 * slsp_db_dump - dump the database for this instance
 */
void
slsp_db_dump __PF2(fd, FILE *,
		   inp, slsp_instance *)
{
    /* TBD */
}

/*
 * ------------------------------------------------------------------------
 * 
 * 	GateD, Release 3.5
 * 
 * 	Copyright (c) 1990,1991,1992,1993,1994 by Cornell University
 * 	    All rights reserved.
 * 
 * 	THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY
 * 	EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
 * 	LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * 	AND FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * 	Royalty-free licenses to redistribute GateD Release
 * 	3 in whole or in part may be obtained by writing to:
 * 
 * 	    GateDaemon Project
 * 	    Information Technologies/Network Resources
 * 	    200 CCC
 * 	    Cornell University
 * 	    Ithaca, NY  14853-2601  USA
 * 
 * 	GateD is based on Kirton's EGP, UC Berkeley's routing
 * 	daemon	 (routed), and DCN's HELLO routing Protocol.
 * 	Development of GateD has been supported in part by the
 * 	National Science Foundation.
 * 
 * 	Please forward bug fixes, enhancements and questions to the
 * 	gated mailing list: gated-people@gated.cornell.edu.
 * 
 * ------------------------------------------------------------------------
 * 
 *       Portions of this software may fall under the following
 *       copyrights:
 * 
 * 	Copyright (c) 1988 Regents of the University of California.
 * 	All rights reserved.
 * 
 * 	Redistribution and use in source and binary forms are
 * 	permitted provided that the above copyright notice and
 * 	this paragraph are duplicated in all such forms and that
 * 	any documentation, advertising materials, and other
 * 	materials related to such distribution and use
 * 	acknowledge that the software was developed by the
 * 	University of California, Berkeley.  The name of the
 * 	University may not be used to endorse or promote
 * 	products derived from this software without specific
 * 	prior written permission.  THIS SOFTWARE IS PROVIDED
 * 	``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
 * 	INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * 	MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */
