ftp.nice.ch/pub/next/unix/network/system/gated.2.1pl2.NI.bs.tar.gz#/gated-2.1/src/egp.c

This is egp.c in view mode; [Download] [Up]

/*
 *  $Header: /disk/d/src/devel/gated/dist/src/RCS/egp.c,v 2.1 92/02/24 14:12:27 jch Exp $
 */

/*%Copyright%*/
/************************************************************************
*									*
*	GateD, Release 2						*
*									*
*	Copyright (c) 1990,1991,1992 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		*
*	2 in whole or in part may be obtained by writing to:		*
*									*
*	    GateDaemon Project						*
*	    Information Technologies/Network Resources			*
*	    143 Caldwell Hall						*
*	    Cornell University						*
*	    Ithaca, NY 14853-2602					*
*									*
*	GateD is based on Kirton's EGP, UC Berkeley's routing		*
*	daemon	 (routed), and DCN's HELLO routing Protocol.		*
*	Development of Release 2 has been supported by the		*
*	National Science Foundation.					*
*									*
*	Please forward bug fixes, enhancements and questions to the	*
*	gated mailing list: gated-people@gated.cornell.edu.		*
*									*
*	Authors:							*
*									*
*		Jeffrey C Honig <jch@gated.cornell.edu>			*
*		Scott W Brim <swb@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.		*
*									*
************************************************************************/


#include "include.h"
#include "egp.h"
#include "snmp.h"

#ifdef	PROTO_EGP

static void egp_event_up();
static void egp_event_down();

/*
 *	Format an EGP network update packet
 */
static void
egp_trace_NR(nr, nr_length)
struct egpnr *nr;
int nr_length;
{
    int gateways, distances, networks;
    int distance, t_gateways;
    int shared_net_class, net_class;
    char class;
    u_char *nr_ptr, *nr_end;
    struct sockaddr_in gateway, network;

    sockclear_in(&gateway);

    shared_net_class = gd_inet_class((u_char *) & nr->en_net);
    memcpy((char *) &gateway.sin_addr, (char *) &nr->en_net, shared_net_class);
    nr_ptr = (u_char *) nr + sizeof(struct egpnr);
    nr_end = (u_char *) nr + nr_length;
    gateway.sin_addr = nr->en_net;	/* struct copy */
    trace(TR_EGP | TR_NOSTAMP, 0, "\tnet %A (%c) - %d interior gateways, %d exterior gateways",
	  &gateway,
	  'A' - 1 + shared_net_class,
	  nr->en_igw,
	  nr->en_egw);

    t_gateways = nr->en_igw + nr->en_egw;
    for (gateways = 0; gateways < t_gateways; gateways++) {
	memcpy((char *) &gateway.sin_addr + shared_net_class, (char *) nr_ptr, 4 - shared_net_class);
	nr_ptr += 4 - shared_net_class;
	distances = (u_char) * nr_ptr;
	nr_ptr++;
	trace(TR_EGP | TR_NOSTAMP, 0, "\t\t%s gateway %A, %d distances",
	      gateways < nr->en_igw ? "interior" : "exterior",
	      &gateway,
	      distances);
	for (; distances; distances--) {
	    distance = (u_char) * nr_ptr;
	    nr_ptr++;
	    networks = (u_char) * nr_ptr;
	    nr_ptr++;
	    trace(TR_EGP | TR_NOSTAMP, 0, "\t\t\tdistance %d, %d networks",
		  distance, networks);
	    for (; networks; networks--) {
		sockclear_in(&network);
		if ((net_class = gd_inet_class(nr_ptr)) == 0) {
		    net_class = CLAC;
		    class = '?';
		} else {
		    class = 'A' - 1 + net_class;
		}
		memcpy((char *) &network.sin_addr, (char *) nr_ptr, net_class);
		nr_ptr += net_class;
		trace(TR_EGP | TR_NOSTAMP, 0, "\t\t\t\t(%c) %A",
		      class,
		      &network);
		if (nr_ptr > nr_end) {
		    trace(TR_EGP | TR_NOSTAMP, 0, "\tpremature end of packet\n");
		    return;
		}
	    }
	}
    }
    trace(TR_EGP, 0, "end of packet");
    return;
}


/*
 * Trace EGP packet
 */
static void
egp_trace(ngp, comment, send_flag, egp, length)
struct egpngh *ngp;
char *comment;
int send_flag;
struct egppkt *egp;
int length;
{
    struct egppkt *ep;
    int reason;
    char packet_status;
    const char *type = (char *) 0;
    const char *status = (char *) 0;
    const char *code = (char *) 0;
    static const char *no_codes[1] =
    {"0"};
    static struct {
	const char *et_type;
	int et_ncodes;
	const char **et_codes;
	int et_nstatus;
	const char **et_status;
    } egp_types[9] = {
	"Invalid", -1, (const char **) 0, -1, (const char **) 0,	/* 0 - Error */
	"Update", 0, no_codes, 3, egp_nr_status,	/* 1 - Nets Reachable */
	"Poll", 0, no_codes, 3, egp_nr_status,	/* 2 - Poll */
	"Acquire", 5, egp_acq_codes, 7, egp_acq_status,	/* 3 - Neighbor Aquisition */
	"Invalid", -1, (const char **) 0, -1, (const char **) 0,	/* 4 - Error */
	"Neighbor", 2, egp_reach_codes, 3, egp_nr_status,	/* 5 - Neighbor Reachability */
	"Invalid", -1, (const char **) 0, -1, (const char **) 0,	/* 6 - Error */
	"Invalid", -1, (const char **) 0, -1, (const char **) 0,	/* 7 - Error */
	"ERROR", -1, (const char **) 0, 3, egp_nr_status	/* 8 - Error packet */
    };

    trace(TR_EGP, 0, "%s %A -> %A length %d",
	  comment,
	  send_flag ? &ngp->ng_interface->int_addr.in : &ngp->ng_addr,
	  send_flag ? &ngp->ng_addr : &ngp->ng_interface->int_addr.in,
	  length);

    if (egp->egp_type <= EGPERR) {
	type = egp_types[egp->egp_type].et_type;
	if ((short) egp->egp_code <= egp_types[egp->egp_type].et_ncodes) {
	    code = egp_types[egp->egp_type].et_codes[egp->egp_code];
	} else {
	    if (egp->egp_code == 0) {
		code = "";
	    } else {
		code = "Invalid";
	    }
	}
	packet_status = egp->egp_status % UNSOLICITED;
	if (packet_status <= egp_types[egp->egp_type].et_nstatus) {
	    status = egp_types[egp->egp_type].et_status[packet_status];
	} else {
	    status = "Invalid";
	}
    } else {
	type = "Invalid";
    }
    tracef("%s vers %d, type %s(%d), code %s(%d), status %s(%d)%s, AS %d, id %d",
	   comment,
	   egp->egp_ver,
	   type,
	   egp->egp_type,
	   code,
	   egp->egp_code,
	   status,
	   egp->egp_status,
	   egp->egp_status & UNSOLICITED ? " Unsolicited" : "",
	   ntohs(egp->egp_system),
	   ntohs(egp->egp_id));
    if (length >= sizeof(struct egppkt)) {
	switch (egp->egp_type) {
	    case EGPACQ:
		if (length == sizeof(struct egpacq)) {
		    trace(TR_EGP, 0, ", hello %d, poll %d",
			  ntohs(((struct egpacq *) egp)->ea_hint),
			  ntohs(((struct egpacq *) egp)->ea_pint));
		}
		break;
	    case EGPPOLL:
		if (length >= sizeof(struct egppoll)) {
		    struct sockaddr_in addr;

		    sockclear_in(&addr);
		    addr.sin_addr = ((struct egppoll *) egp)->ep_net;

		    trace(TR_EGP, 0, ", src net %A",
			  &addr);
		}
		break;
	    case EGPNR:
		if (length >= sizeof(struct egpnr)) {
		    struct sockaddr_in addr;

		    sockclear_in(&addr);
		    addr.sin_addr = ((struct egpnr *) egp)->en_net;
		    trace(TR_EGP, 0, ", #int %d, #ext %d, src net %A",
			  ((struct egpnr *) egp)->en_igw,
			  ((struct egpnr *) egp)->en_egw,
			  &addr);
		} else if (length >= (sizeof(struct egpnr) - sizeof(struct in_addr))) {
		    trace(TR_EGP, 0, ", #int %d, #ext %d",
			  ((struct egpnr *) egp)->en_igw,
			  ((struct egpnr *) egp)->en_egw);
		}
		if (length > sizeof(struct egpnr) && (trace_flags & TR_UPDATE)) {
		    egp_trace_NR((struct egpnr *) egp, length);
		}
		break;
	    case EGPERR:
		reason = ntohs(((struct egperr *) egp)->ee_rsn);
		if (reason > EMAXERR) {
		    trace(TR_EGP, 0, ", error %d (invalid)", reason);
		} else {
		    trace(TR_EGP, 0, ", error: %s(%d)", egp_reasons[reason], reason);
		}
		ep = (struct egppkt *) ((struct egperr *) egp)->ee_egphd;
		if (length >= sizeof(struct egperr)) {
		    char e_comment[MAXHOSTNAMELENGTH];

		    (void) strcpy(e_comment, comment);
		    (void) strcat(e_comment, " ERROR");
		    egp_trace(ngp, e_comment, send_flag, ep, sizeof(((struct egperr *) egp)->ee_egphd));
		}
		break;
	    case EGPHELLO:
	    default:
		trace(TR_EGP, 0, NULL);
	}
    }
    trace(TR_EGP, 0, NULL);
    return;
}


static void
egp_msg_event(ngp, string)
struct egpngh *ngp;
char *string;
{

    trace(TR_EGP, 0, "egp_msg_event: neighbor %s version %d state %s event %s",
	  ngp->ng_name, ngp->ng_V, trace_state(egp_states, ngp->ng_state), string);
}


static void
egp_msg_state(ngp, state)
struct egpngh *ngp;
int state;
{

    trace(TR_EGP, 0, "egp_msg_state: neighbor %s version %d state %s transition to %s",
	  ngp->ng_name, ngp->ng_V, trace_state(egp_states, ngp->ng_state), trace_state(egp_states, state));
}


static void
egp_msg_confused(ngp, event)
struct egpngh *ngp;
char *event;
{
    trace(TR_EGP, 0, "egp_msg_confused: neighbor %s version %d event %s should not occur in state %s",
	  ngp->ng_name, ngp->ng_V, event, trace_state(egp_states, ngp->ng_state));
}


static void
egp_msg_timer(tip)
timer *tip;
{
    struct egpngh *ngp;

    ngp = (struct egpngh *) tip->timer_task->task_data;

    tracef("egp_msg_timer: neighbor %s version %d state %s timer %s ",
	   ngp->ng_name, ngp->ng_V, trace_state(egp_states, ngp->ng_state), tip->timer_name);
    if (tip->timer_interval) {
	trace(TR_EGP, 0, "reset to %#T at %T ", tip->timer_interval, tip->timer_next_time);
    } else {
	trace(TR_EGP, 0, "stopped");
    }
}


/*
 *	Set new version and print a message
 */
static void
egp_set_version(ngp, egp_version)
struct egpngh *ngp;
u_char egp_version;
{
    trace(TR_EGP, LOG_NOTICE, "egp_set_version: neighbor %s version %d state %s set version %d",
	  ngp->ng_name, ngp->ng_V, trace_state(egp_states, ngp->ng_state), egp_version);
    ngp->ng_V = egp_version;
}


/*
 *	Routines to deal with maxacquire limits
 */
static int
egp_group_acquired(ngp)
struct egpngh *ngp;
{
    int acquired = 0;
    struct egpngh *tngp;

    for (tngp = ngp->ng_gr_head; tngp; tngp = tngp->ng_next) {
	if (tngp->ng_gr_index != ngp->ng_gr_index) {
	    break;
	}
	if (tngp->ng_state == NGS_UP) {
	    acquired++;
	}
    }

    return (acquired);
}


static void
egp_group_checkmax(ngp)
struct egpngh *ngp;
{
    u_short acquired = 0;
    u_short acquire;
    struct egpngh *tngp;

    acquire = ngp->ng_gr_head->ng_gr_acquire;

    for (tngp = ngp->ng_gr_head; tngp; tngp = tngp->ng_next) {
	if (tngp->ng_gr_index != ngp->ng_gr_index) {
	    break;
	}
#ifdef	DEBUG
	trace(TR_INT, LOG_WARNING, "egp_group_checkmax: neighbor %s state %s acquired %d acquire %d",
	      tngp->ng_name, trace_state(egp_states, tngp->ng_state), acquired, acquire);
#endif				/* DEBUG */
	switch (tngp->ng_state) {
	    case NGS_IDLE:
	    case NGS_CEASE:
		break;
	    case NGS_UP:
	    case NGS_ACQUISITION:
	    case DOWN:
		if (acquired >= acquire) {
		    egp_event_stop(tngp, GODOWN);
		}
		if (tngp->ng_state == NGS_UP) {
		    acquired++;
		}
		break;
	}
    }
}


/*
 *  egp_check_as() is called when we loose reachability to
 *  a neighbor.  It scans the neighbor list to determine if there
 *  are any other active neighbors to this AS.  It should probably
 *  delete any EGP learned routes from the AS of the neighbor.
 */
static int
egp_check_as(down_ngp)
struct egpngh *down_ngp;
{
    struct egpngh *ngp;

    EGP_LIST(ngp) {
	if ((ngp != down_ngp) &&
	    (ngp->ng_asin == down_ngp->ng_asin) &&
	    ((ngp->ng_state == NGS_UP) || (ngp->ng_state == NGS_DOWN))) {
	    return 0;
	}
    } EGP_LISTEND;
    trace(TR_EXT, LOG_WARNING, "egp_check_as: lost all neighbors to AS %d",
	  down_ngp->ng_asin);
    return 1;
}


/*
 * egp_check_neighborLoss() handles the loss of a neighbor.  It deletes any
 * routes in the routing table pointing at this gateway, calls egp_check_as()
 * to determine if we lost all neighbors for this AS.
 */
static void
egp_check_neighborLoss(ngp)
struct egpngh *ngp;
{
    int changes = 0;

    if ((ngp->ng_state != NGS_UP) && (ngp->ng_state != NGS_DOWN)) {
	return;
    }
#ifdef	AGENT_SNMP
    snmp_trap_egpNeighborLoss(ngp);
#endif				/* AGENT_SNMP */

    changes += egp_check_as(ngp);	/* check for other direct neighbors in this AS */
    changes += rt_gwunreach(ngp->ng_task, &ngp->ng_gw);	/* delete routes for down gateway */
    if (ngp->ng_flags & NGF_GENDEFAULT) {
	changes += rt_default_delete();
	ngp->ng_flags &= ~NGF_GENDEFAULT;
    }
    if (changes) {
	trace(TR_RT, 0, "egp_check_neighborLoss: above changes due to loss of neighbor %s", ngp->ng_name);
    }
}


/*
 *	Routines to change state
 */
static void
egp_state_idle(ngp)
struct egpngh *ngp;
{

    IF_EGPPROTO egp_msg_state(ngp, NGS_IDLE);

    egp_check_neighborLoss(ngp);

    if (ngp->ng_state == NGS_UP) {
	ngp->ng_stats.statedowns++;
    }
    ngp->ng_state = NGS_IDLE;

    /* If this task is being deleted, issue a deletion event */
    if (ngp->ng_flags & NGF_DELETE) {
	egp_event_delete(ngp);
    }
}


static void
egp_state_acquisition(ngp)
struct egpngh *ngp;
{

    IF_EGPPROTO egp_msg_state(ngp, NGS_ACQUISITION);

    egp_check_neighborLoss(ngp);

    if (ngp->ng_state == NGS_UP) {
	ngp->ng_stats.statedowns++;
    }
    ngp->ng_state = NGS_ACQUISITION;
    ngp->ng_status = 0;
}


/*
 * egp_state_down() Set down state.
 */
static void
egp_state_down(ngp)
struct egpngh *ngp;
{

    IF_EGPPROTO egp_msg_state(ngp, NGS_DOWN);

    if (ngp->ng_state != NGS_UP) {
	trace(TR_EXT, LOG_WARNING, "egp_state_down: acquired neighbor %s AS %d in %s",
	      ngp->ng_name,
	      ngp->ng_asin,
	      egp_acq_status[ngp->ng_M]);
    }
    if (ngp->ng_state == NGS_UP) {
	ngp->ng_stats.statedowns++;
    }
    ngp->ng_state = NGS_DOWN;

}


static void
egp_state_up(ngp)
struct egpngh *ngp;
{

    IF_EGPPROTO egp_msg_state(ngp, NGS_UP);

    ngp->ng_state = NGS_UP;

    egp_group_checkmax(ngp);

    ngp->ng_stats.stateups++;
}


/*
 * egp_state_cease() initiates the sending of an egp neighbor cease.
 */
static void
egp_state_cease(ngp)
struct egpngh *ngp;
{

    egp_check_neighborLoss(ngp);

    IF_EGPPROTO egp_msg_state(ngp, NGS_CEASE);

    if (ngp->ng_state == NGS_UP) {
	ngp->ng_stats.statedowns++;
    }
    ngp->ng_state = NGS_CEASE;

    return;
}


/*
 *	Routines to send packets
 */

/*
 * egp_send() sends an egp packet.
 */
static void
egp_send(ngp, egp, length)
struct egpngh *ngp;
struct egppkt *egp;			/* pointer to start of egp packet */
int length;				/* length in octets of egp packet */
{
    int error = FALSE;
    struct iovec iovec;

    /* Set up iovec */
    iovec.iov_base = (caddr_t) egp;
    iovec.iov_len = length;

    /* Set AS number in outgoing packet */
    egp->egp_system = htons((unsigned short) (ngp ? ngp->ng_asout : my_system));

    /* Set version in outgoing packet */
    egp->egp_ver = ngp ? ngp->ng_V : EGPVER;

    /* Calculate packet checksum */
    egp->egp_chksum = 0;
    egp->egp_chksum = gd_inet_cksum(&iovec, 1, length);

    if (trace_flags & TR_EGP) {
	egp_trace(ngp, "EGP SENT", TRUE, egp, length);
    }
    if (task_send_packet(ngp->ng_task, (caddr_t) egp, length, 0, (sockaddr_un *) 0) < 0) {
	error = TRUE;
    }
    egp_stats.outmsgs++;
    ngp->ng_stats.outmsgs++;
    if (error) {
	egp_stats.outerrors++;
	ngp->ng_stats.outerrors++;
    }
}


/*
 * egp_send_acquire() sends an acquisition or cease packet.
 */
static void
egp_send_acquire(ngp, code, status, id)
struct egpngh *ngp;
u_int code, status;
u_int id;
{
    struct egpacq acqpkt;
    int length;

    acqpkt.ea_pkt.egp_type = EGPACQ;
    acqpkt.ea_pkt.egp_code = code;
    acqpkt.ea_pkt.egp_status = status;
    acqpkt.ea_pkt.egp_id = htons(id);
    acqpkt.ea_hint = htons(ngp->ng_P1);
    acqpkt.ea_pint = htons(ngp->ng_P2);

    if (code == NAREQ || code == NACONF) {
	length = sizeof(acqpkt);
    } else {
	/* omit hello & poll int */
	length = sizeof(acqpkt.ea_pkt);
    }

    egp_send(ngp, (struct egppkt *) & acqpkt, length);
}


/*
 * egp_send_hello() sends a hello or I-H-U packet.
 */
static void
egp_send_hello(ngp, code, id)
struct egpngh *ngp;
u_char code;
u_int id;
{
    struct egppkt hellopkt;

    hellopkt.egp_type = EGPHELLO;
    hellopkt.egp_code = code;
    hellopkt.egp_status = (ngp->ng_state == NGS_UP) ? UP : DOWN;
    hellopkt.egp_id = htons(id);

    if (code == neighHello) {
	/* Remember the ID of this Hello */
	ngp->ng_S_lasthello = id;
    }
    
    egp_send(ngp, (struct egppkt *) & hellopkt,
	     sizeof(hellopkt));
}


/*
 * egp_send_poll() sends an NR poll packet.
 */
static void
egp_send_poll(ngp)
struct egpngh *ngp;
{
    struct egppoll pollpkt;

    pollpkt.ep_pkt.egp_type = EGPPOLL;
    pollpkt.ep_pkt.egp_code = 0;
    pollpkt.ep_pkt.egp_status = (ngp->ng_state == NGS_UP) ? UP : DOWN;
    pollpkt.ep_pkt.egp_id = htons(ngp->ng_S);
    pollpkt.ep_unused = 0;
    pollpkt.ep_net = ngp->ng_saddr.sin_addr;	/* struct copy */

    egp_send(ngp, (struct egppkt *) & pollpkt, sizeof(pollpkt));
}


/*
 * egp_send_error() sends an error packet.
 */
static void
egp_send_error(ngp, egp, length, error, msg)
struct egpngh *ngp;			/* ponter to legit. neighbor table, else zero */
struct egppkt *egp;			/* erroneous egp packet */
int length;				/* length erroneous packet */
int error;
char *msg;
{
    struct egperr errpkt;

    errpkt.ee_pkt.egp_type = EGPERR;
    errpkt.ee_pkt.egp_code = (error == EUVERSION) ? EGPVMASK : 0;
    if (ngp && ((ngp->ng_state == NGS_UP) || (ngp->ng_state == NGS_DOWN))) {
	errpkt.ee_pkt.egp_status = (ngp->ng_state == NGS_UP) ? UP : DOWN;
    } else {
	errpkt.ee_pkt.egp_status = 0;
    }
    errpkt.ee_pkt.egp_id = htons(egprid_h);	/* recvd seq.# */
    errpkt.ee_rsn = htons(error);
    /*
     * copy header of erroneous egp packet
     */
    memset((char *) errpkt.ee_egphd, (char) 0, sizeof(errpkt.ee_egphd));
    if (length > sizeof(errpkt.ee_egphd)) {
	length = sizeof(errpkt.ee_egphd);
    }
    if (length) {
	memcpy((char *) errpkt.ee_egphd, (char *) egp, length);
    } else {
	errpkt.ee_pkt.egp_status |= UNSOLICITED;
    }

    trace(TR_EXT, LOG_WARNING, "egp_send_error: error packet to neighbor %s: %s",
	  ngp->ng_name,
	  msg);

    if ((trace_flags & TR_EXT) && !(trace_flags & TR_EGP)) {
	egp_trace(ngp, "egp_send_error: send error pkt ", TRUE, (struct egppkt *) & errpkt, sizeof(errpkt));
    }
    ngp->ng_stats.outerrmsgs++;
    egp_send(ngp, (struct egppkt *) & errpkt, sizeof(errpkt));
}


/*
 * egp_send_update() sends an NR message packet.
 *
 * It fills in the header information, calls if_rtcheck() to update the
 * interface status information and egp_rt_send() to fill in the reachable
 * networks.
 */

static void
egp_send_update(ngp, unsol)
struct egpngh *ngp;
int unsol;				/* TRUE => set unsolicited bit */
{
    int maxsize, length;
    struct egpnr *nrp;
    struct in_addr egpsnraddr;

    /*
     * allocate message buffer
     */
    maxsize = sizeof(struct egpnr) + NRMAXNETUNIT * (n_interfaces + rt_net_routes);
    nrp = (struct egpnr *) malloc((unsigned) maxsize);
    if (nrp == NULL) {
	trace(TR_ALL, LOG_ERR, "egp_send_update: malloc: %m");
	return;
    }
    /* prepare static part of NR message header */
    nrp->en_pkt.egp_type = EGPNR;
    nrp->en_pkt.egp_code = 0;
    nrp->en_pkt.egp_status = (ngp->ng_state == NGS_UP) ? UP : DOWN;
    if (unsol) {
	nrp->en_pkt.egp_status |= UNSOLICITED;
    }
    nrp->en_pkt.egp_id = htons(ngp->ng_R);
#ifdef	notdef
    nrp->en_egw = 0;			/* no exterior gateways */
#endif				/* notdef */
    /*
     * copy shared net address
     */
    egpsnraddr = gd_inet_makeaddr(gd_inet_wholenetof(ngp->ng_paddr.sin_addr), 0, FALSE);
    nrp->en_net = egpsnraddr;
    length = egp_rt_send(nrp, ngp);

    if (length != ERROR) {
	if (length > egp_pktsize) {
	    trace(TR_ALL, LOG_WARNING, "egp_send_update: neighbor %s AS %d NR message size (%d) larger than EGPMAXPACKETSIZE (%d)",
		  ngp->ng_name,
		  ngp->ng_asin,
		  length,
		  egp_pktsize);
	}
	egp_send(ngp, (struct egppkt *) nrp, length);
    } else {
	trace(TR_ALL, LOG_WARNING, "egp_send_update: NR message not sent");
    }
    free((char *) nrp);
    return;
}


/*
 *	Front end for task timer routines
 */
static void
egp_set_timer(tip, value)
timer *tip;
time_t value;
{

    timer_interval(tip, value);
    IF_EGPPROTO egp_msg_timer(tip);
}


static void
egp_reset_timer(tip, value)
timer *tip;
time_t value;
{

    timer_set(tip, value);
    IF_EGPPROTO egp_msg_timer(tip);
}


/*
 *	Routines to process reachability
 */

/*
 *  egp_check_reachability() checks the reachability status of our neighbors
 */
static void
egp_check_reachability(ngp)
struct egpngh *ngp;
{
    int change = 0;

    IF_EGPPROTO trace(TR_EGP, 0, "egp_check_reachability: neighbor %s version %d state %s [%04B] %d / %d / %d",
	 ngp->ng_name, ngp->ng_V, trace_state(egp_states, ngp->ng_state),
		       ngp->ng_responses, ngp->ng_j, egp_reachability[ngp->ng_responses], ngp->ng_k);

    switch (ngp->ng_state) {
	case NGS_IDLE:
	case NGS_ACQUISITION:
	case NGS_CEASE:
	    egp_msg_confused(ngp, "ReachabilityCheck");
	    break;
	case NGS_DOWN:
	    if (egp_reachability[ngp->ng_responses] >= ngp->ng_j) {
		egp_event_up(ngp);
		change++;
	    }
	    break;
	case NGS_UP:
	    if (egp_reachability[ngp->ng_responses] <= ngp->ng_k) {
		egp_event_down(ngp);
		change++;
	    }
	    break;
    }
    if (change) {
	trace(TR_EXT, LOG_WARNING, "egp_check_reachability: neighbor %s AS %d state %s received %d of %d %s",
	      ngp->ng_name,
	      ngp->ng_asin,
	      trace_state(egp_states, ngp->ng_state),
	      egp_reachability[ngp->ng_responses],
	      REACH_RATIO,
	      (ngp->ng_M == ACTIVE) ? "responses" : "requests");
	IF_EGPPROTO trace(TR_EGP, 0, "egp_check_reachability: neighbor %s version %d state %s [%04B] %d / %d / %d",
	 ngp->ng_name, ngp->ng_V, trace_state(egp_states, ngp->ng_state),
			   ngp->ng_responses, ngp->ng_j, egp_reachability[ngp->ng_responses], ngp->ng_k);
    }
}


static void
egp_event_reachability(ngp)
struct egpngh *ngp;
{

    IF_EGPPROTO egp_msg_event(ngp, "reachability");

    ngp->ng_responses |= 1;
    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_P4);
    egp_check_reachability(ngp);
}


static void
egp_shift_reachability(ngp)
struct egpngh *ngp;
{
    ngp->ng_responses = (ngp->ng_responses << 1) & ((1 << REACH_RATIO) - 1);
    egp_check_reachability(ngp);
}


/*
 *	Check for a status change and issue a reachability event
 */
static int
egp_check_status(ngp, egp_status, status)
struct egpngh *ngp;
u_char egp_status;
u_char status;
{

    switch (egp_status) {
	case UP:
	case DOWN:
	    if (ngp->ng_M == status) {
		/* M == ACTIVE && status == ACTIVE */
		/* M == PASSIVE && status == PASSIVE */
		egp_event_reachability(ngp);
	    }
	    break;
	default:
	    return (EBADHEAD);
    }
    return (NOERROR);
}


/*
 *	Routines for checking polling rate
 */
/*ARGSUSED*/
static void
egp_rate_init(ngp, rp, last)
struct egpngh *ngp;
struct egp_rate *rp;
time_t last;
{
    int i;

    for (i = 0; i < RATE_WINDOW; i++) {
	rp->rate_window[i] = rp->rate_min;
    }
    rp->rate_last = last;
}


static int
egp_rate_check(ngp, rp)
struct egpngh *ngp;
struct egp_rate *rp;
{
    int i;
    int excessive;
    time_t interval;

    interval = rp->rate_last ? time_sec - rp->rate_last : rp->rate_min;

    excessive = interval < rp->rate_min ? 1 : 0;

    for (i = RATE_WINDOW - 1; i; i--) {
	if ((rp->rate_window[i] = rp->rate_window[i - 1]) < rp->rate_min) {
	    excessive++;
	}
    }

    IF_EGPPROTO trace(TR_EGP, 0, "egp_rate_check: neighbor %s min %#T last %T excessive %d",
		       ngp->ng_name,
		       rp->rate_min,
		       rp->rate_last,
		       excessive);
    IF_EGPPROTO tracef("egp_rate_check: neighbor %s window ", ngp->ng_name);

    rp->rate_window[0] = interval;

    rp->rate_last = time_sec;

    for (i = 0; i < RATE_WINDOW; i++) {
	IF_EGPPROTO tracef("%#T ", rp->rate_window[i]);
    }

    IF_EGPPROTO trace(TR_EGP, 0, NULL);

    if (excessive < RATE_MAX) {
	return (0);
    } else {
	egp_rate_init(ngp, rp, rp->rate_last);
	return (1);
    }
}


/*
 * egp_set_intervals() sets EGP hello and poll intervals and times.
 * Returns 1 if either poll or hello intervals too big, 0 otherwise.
 */
static int
egp_set_intervals(ngp, egppkt)
struct egpngh *ngp;
struct egppkt *egppkt;
{
    struct egpacq *egpa = (struct egpacq *) egppkt;
    u_short helloint, pollint, ratio;

    /*
     * check parameters within bounds
     */
    helloint = ntohs(egpa->ea_hint);
    pollint = ntohs(egpa->ea_pint);
    if (helloint > MAXHELLOINT || pollint > MAXPOLLINT) {
	trace(TR_EXT, LOG_WARNING, "egp_set_intervals: Hello interval = %d or poll interval = %d too big from %s, code %d",
	      helloint,
	      pollint,
	      ngp->ng_name,
	      egpa->ea_pkt.egp_code);
	return (1);
    }
    if ((helloint != ngp->ng_P1) || (pollint != ngp->ng_P2)) {
	trace(TR_EXT, 0, "egp_set_intervals: neighbor %s specified hello/poll intervals %d/%d, we specified %d/%d",
	      ngp->ng_name,
	      helloint,
	      pollint,
	      ngp->ng_P1,
	      ngp->ng_P2);
    }
    if (helloint < ngp->ng_P1) {
	helloint = ngp->ng_P1;
    }
    if (pollint < ngp->ng_P2) {
	pollint = ngp->ng_P2;
    }
    ratio = (pollint - 1) / helloint + 1;	/* keep ratio pollint:helloint */
    helloint += HELLOMARGIN;
    pollint = ratio * helloint;
    trace(TR_EXT, 0, "egp_set_intervals: neighbor %s version %d state %s using intervals %d/%d",
	  ngp->ng_name,
	  ngp->ng_V,
	  trace_state(egp_states, ngp->ng_state),
	  helloint,
	  pollint);
    ngp->ng_T1 = helloint;
    ngp->ng_T3 = ngp->ng_T1 * REACH_RATIO;
    ngp->ng_T2 = helloint * ratio;
    return (0);
}


/*
 * egp_init_variables() go into neighbor state, initialize most variables.
 */

/* ARGSUSED */
static int
egp_init_variables(ngp, egp)
struct egpngh *ngp;
struct egppkt *egp;
{
    if (egp_set_intervals(ngp, egp)) {
	return (1);
    }
    ngp->ng_responses = 0;
    ngp->ng_status = 0;
    ngp->ng_noupdate = 0;
    ngp->ng_R_lastpoll = -1;	/* Invalid ID for last poll */
    ngp->ng_flags &= ~(NGF_SENT_POLL | NGF_SENT_REPOLL | NGF_SENT_UNSOL | NGF_RECV_UNSOL | NGF_RECV_REPOLL | NGF_PROC_POLL);

    egp_rate_init(ngp, &ngp->ng_hello_rate, (time_t) 0);
    egp_rate_init(ngp, &ngp->ng_poll_rate, (time_t) 0);

    if (!(ngp->ng_options & NGO_ASIN)) {
	ngp->ng_asin = htons(egp->egp_system);
	ngp->ng_accept = control_exterior_locate(egp_accept_list, ngp->ng_asin);
	ngp->ng_propagate = control_exterior_locate(egp_propagate_list, ngp->ng_asin);
    }
    if (egp->egp_status) {
	ngp->ng_M = egp->egp_status == ACTIVE ? PASSIVE : ACTIVE;
    } else {
	ngp->ng_M = ngp->ng_asin < ngp->ng_asout ? PASSIVE : ACTIVE;
    }

    if (ngp->ng_M == ACTIVE) {
	ngp->ng_j = 3;
	ngp->ng_k = 1;
    } else {
	ngp->ng_j = 1;
	ngp->ng_k = 0;
    }

    ngp->ng_rtage = (ngp->ng_T2 * EGP_N_POLLAGE) > RT_T_EXPIRE ? ngp->ng_T2 * EGP_N_POLLAGE : RT_T_EXPIRE;

    return (0);
}


/* Time to send a poll */
static void
egp_do_poll(ngp, sync_t1)
struct egpngh *ngp;
int sync_t1;
{
    timer *tip = ngp->ng_task->task_timer[EGP_TIMER_t2];
    
    if (ngp->ng_flags & NGF_SENT_REPOLL) {
	ngp->ng_flags &= ~(NGF_SENT_POLL | NGF_SENT_REPOLL);
    }
    if (ngp->ng_flags & NGF_SENT_POLL) {
	ngp->ng_flags |= NGF_SENT_REPOLL;
    } else {
	ngp->ng_S++;
	ngp->ng_flags |= NGF_SENT_POLL;
	ngp->ng_flags &= ~(NGF_RECV_UNSOL);
	if (++ngp->ng_noupdate > MAXNOUPDATE) {
	    char buf[BUFSIZ];

	    sprintf(buf, "no Update received for %d successive new poll id's",
		    ngp->ng_noupdate);
	    egp_send_error(ngp, (struct egppkt *) 0, 0, ENORESPONSE, buf);
	    ngp->ng_noupdate = 0;
	    return;
	}
    }
    egp_send_poll(ngp);

    if (sync_t1) {
	/* Sync t1 to t2 */
	egp_reset_timer(tip,
			ngp->ng_T2 - (time_sec - ngp->ng_task->task_timer[EGP_TIMER_t1]->timer_last_time));
    } else if (tip->timer_interval != ngp->ng_T2) {
	/* Set the correct interval */
	egp_set_timer(tip, ngp->ng_T2);
    }
}


/*
 *	Routines to process events
 */
static void
egp_event_up(ngp)
struct egpngh *ngp;
{
    struct sockaddr_in source_net;

    IF_EGPPROTO egp_msg_event(ngp, "Up");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	case NGS_ACQUISITION:
	case NGS_CEASE:
	case NGS_UP:
	    egp_msg_confused(ngp, "Up");
	    break;
	case NGS_DOWN:
	    egp_state_up(ngp);

	    /* Send a POLL */
	    egp_do_poll(ngp, TRUE);

	    /* Just for good luck, send an unsolicitied update if we can make */
	    /* an educated guess about which net he is interested in */
	    if (!(ngp->ng_flags & (NGF_SENT_UNSOL | NGF_PROC_POLL))) {
		ngp->ng_paddr = ngp->ng_saddr;	/* struct copy */
		sockclear_in(&source_net);
		if (ngp->ng_paddr.sin_addr.s_addr) {
		    /* Polled net is set, use that net address */
		    source_net = ngp->ng_paddr;	/* struct copy */
		} else {
		    /* If polled net not set, use shared net if we are both on it */
		    if (gd_inet_wholenetof(ngp->ng_addr.sin_addr) == gd_inet_wholenetof(ngp->ng_interface->int_addr.in.sin_addr)) {
			source_net.sin_addr.s_addr = gd_inet_wholenetof(ngp->ng_addr.sin_addr);
		    }
		}
		/* If we figured out a net, and have a route to it, send an unsolicited update */
		if (source_net.sin_addr.s_addr) {
		    if (rt_locate(RTS_INTERIOR, (sockaddr_un *) & source_net, RTPROTO_DIRECT)) {
			egp_send_update(ngp, 1);
			ngp->ng_flags |= NGF_SENT_UNSOL;
		    }
		}
	    }
	    break;
    }
}


static void
egp_event_down(ngp)
struct egpngh *ngp;
{

    IF_EGPPROTO egp_msg_event(ngp, "Down");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	case NGS_ACQUISITION:
	case NGS_CEASE:
	case NGS_DOWN:
	    egp_msg_confused(ngp, "Down");
	    break;
	case NGS_UP:
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t2]);
	    egp_state_down(ngp);
	    break;
    }
}


/*ARGSUSED*/
static void
egp_event_request(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    u_short helloint;

    IF_EGPPROTO egp_msg_event(ngp, "Request");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	case NGS_ACQUISITION:
	case NGS_DOWN:
	case NGS_UP:
	    if ((ngp->ng_options & NGO_ASIN) && (ngp->ng_asin != htons(egp->egp_system))) {
		trace(TR_EXT, LOG_ERR, "egp_event_request: neighbor %s version %d state %s specified AS %d, we expected %d",
		      ngp->ng_name,
		      ngp->ng_V,
		      trace_state(egp_states, ngp->ng_state),
		      htons(egp->egp_system),
		      ngp->ng_asin);
		egp_send_acquire(ngp, NAREFUS, ADMINPROHIB, egprid_h);
		egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t1]);
		egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t2]);
		egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_START_LONG);
		egp_state_idle(ngp);
		break;
	    }
	    if (egp_init_variables(ngp, egp)) {
		/* XXX - May want to declare a stop? */
		egp_send_acquire(ngp, NAREFUS, PARAMPROB, egprid_h);
		break;
	    }
	    ngp->ng_R = egprid_h;
	    egp_send_acquire(ngp, NACONF, (u_int) ngp->ng_M, egprid_h);
	    if (ngp->ng_M == ACTIVE) {
		egp_send_hello(ngp, neighHello, ngp->ng_S);
	    }
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t1], ngp->ng_T1);
#ifdef	notdef
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_P5);
#else				/* notdef */
	    helloint = ntohs(((struct egpacq *) egp)->ea_hint);
	    if (helloint < ngp->ng_P1) {
		helloint = ngp->ng_P1;
	    }
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) (2 * REACH_RATIO * helloint));
#endif				/* notdef */
	    egp_state_down(ngp);
	    break;
	case NGS_CEASE:
	    ngp->ng_status = GODOWN;
	    egp_send_acquire(ngp, NACEASE, ngp->ng_status, ngp->ng_S);
	    egp_state_cease(ngp);
    }
}


static void
egp_event_confirm(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    u_short helloint;

    IF_EGPPROTO egp_msg_event(ngp, "Confirm");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    egp_send_acquire(ngp, NACEASE, PROTOVIOL, ngp->ng_S);
	    break;
	case NGS_ACQUISITION:
	    if ((ngp->ng_options & NGO_ASIN) && (ngp->ng_asin != htons(egp->egp_system))) {
		trace(TR_EXT, LOG_ERR, "egp_event_confirm: neighbor %s version %d state %s specified AS %d, we expected %d",
		      ngp->ng_name,
		      ngp->ng_V,
		      trace_state(egp_states, ngp->ng_state),
		      htons(egp->egp_system),
		      ngp->ng_asin);
		egp_send_acquire(ngp, NAREFUS, ADMINPROHIB, egprid_h);
		egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t1]);
		egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t2]);
		egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_START_LONG);
		egp_state_idle(ngp);
		break;
	    }
	    if (egp_init_variables(ngp, egp)) {
		/* XXX - May need more thought */
		egp_event_stop(ngp, PARAMPROB);
		break;
	    }
	    if (egp_check_status(ngp, UP, ACTIVE) != NOERROR) {
		egp_send_error(ngp, egp, egplen, EBADHEAD, "invalid Status field in Confirm");
		egp_stats.inerrors++;
		egp_stats.inmsgs--;
		ngp->ng_stats.inerrors++;
		ngp->ng_stats.inmsgs--;
		break;
	    }
	    ngp->ng_R = egprid_h;
	    if (ngp->ng_M == ACTIVE) {
		egp_send_hello(ngp, neighHello, ngp->ng_S);
	    }
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t1], ngp->ng_T1);
#ifdef	notdef
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_P5);
#else				/* notdef */
	    helloint = ntohs(((struct egpacq *) egp)->ea_hint);
	    if (helloint < ngp->ng_P1) {
		helloint = ngp->ng_P1;
	    }
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) (2 * REACH_RATIO * helloint));
#endif				/* notdef */
	    egp_state_down(ngp);
	    break;
	case NGS_DOWN:
	case NGS_UP:
	case NGS_CEASE:
	    egp_msg_confused(ngp, "Confirm");
	    break;
    }
}


static void
egp_event_refuse(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    time_t restart_delay = 0;

    IF_EGPPROTO egp_msg_event(ngp, "Refuse");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    egp_send_acquire(ngp, NACEASE, PROTOVIOL, ngp->ng_S);
	    break;
	case NGS_ACQUISITION:
	    trace(TR_EGP, LOG_WARNING, "egp_event_refuse: neighbor %s AS %d state %s Cease Refuse %s",
		  ngp->ng_name,
		  ngp->ng_asin,
		  trace_state(egp_states, ngp->ng_state),
		  egp_acq_status[egp->egp_status]);
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t1]);
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t2]);
	    switch (egp->egp_status) {
		case UNSPEC:
		case ACTIVE:
		case PASSIVE:
		case NORESOURCE:
		case GODOWN:
		    restart_delay = EGP_START_SHORT;
		    break;
		case ADMINPROHIB:
		case PARAMPROB:
		case PROTOVIOL:
		    restart_delay = EGP_START_LONG;
		    break;
		default:
		    egp_send_error(ngp, egp, egplen, EBADHEAD, "invalid Status field in Refuse");
		    egp_stats.inerrors++;
		    egp_stats.inmsgs--;
		    ngp->ng_stats.inerrors++;
		    ngp->ng_stats.inmsgs--;
		    break;
	    }
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], restart_delay);
	    egp_state_idle(ngp);
	    break;
	case NGS_DOWN:
	case NGS_UP:
	case NGS_CEASE:
	    egp_msg_confused(ngp, "Refuse");
	    break;
    }
}


static void
egp_event_cease(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    time_t restart_delay = 0;

    IF_EGPPROTO egp_msg_event(ngp, "Cease");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    egp_send_acquire(ngp, NACACK, egp->egp_status, egprid_h);
	    break;
	case NGS_ACQUISITION:
	case NGS_DOWN:
	case NGS_UP:
	    trace(TR_EGP, LOG_WARNING, "egp_event_cease: neighbor %s AS %d state %s Cease reason %s",
		  ngp->ng_name,
		  ngp->ng_asin,
		  trace_state(egp_states, ngp->ng_state),
		  egp_acq_status[egp->egp_status]);
	case NGS_CEASE:
	    egp_send_acquire(ngp, NACACK, egp->egp_status, egprid_h);
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t1]);
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t2]);
	    switch (egp->egp_status) {
		case UNSPEC:
		case ACTIVE:
		case PASSIVE:
		case NORESOURCE:
		case GODOWN:
		    restart_delay = EGP_START_SHORT;
		    break;
		case ADMINPROHIB:
		case PARAMPROB:
		case PROTOVIOL:
		    restart_delay = EGP_START_LONG;
		    break;
		default:
		    egp_send_error(ngp, egp, egplen, EBADHEAD, "invalid Status field in Cease");
		    egp_stats.inerrors++;
		    egp_stats.inmsgs--;
		    ngp->ng_stats.inerrors++;
		    ngp->ng_stats.inmsgs--;
		    break;
	    }
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], restart_delay);
	    egp_state_idle(ngp);
	    break;
    }
}


static void
egp_event_ceaseack(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    time_t restart_delay = 0;

    IF_EGPPROTO egp_msg_event(ngp, "Cease-ack");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    break;
	case NGS_ACQUISITION:
	case NGS_DOWN:
	case NGS_UP:
	    break;
	case NGS_CEASE:
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t1]);
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t2]);
	    switch (ngp->ng_status) {
		case UNSPEC:
		case ACTIVE:
		case PASSIVE:
		case NORESOURCE:
		case GODOWN:
		    restart_delay = EGP_START_SHORT;
		    break;
		case ADMINPROHIB:
		case PARAMPROB:
		case PROTOVIOL:
		    restart_delay = EGP_START_LONG;
		    break;
		default:
		    egp_send_error(ngp, egp, egplen, EBADHEAD, "invalid Status field in Cease-ack");
		    egp_stats.inerrors++;
		    egp_stats.inmsgs--;
		    ngp->ng_stats.inerrors++;
		    ngp->ng_stats.inmsgs--;
		    break;
	    }
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], restart_delay);
	    egp_state_idle(ngp);
	    break;
    }
}


static void
egp_event_hello(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    int error = NOERROR;
    const char *msg = NULL;

    IF_EGPPROTO egp_msg_event(ngp, "Hello");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    egp_send_acquire(ngp, NACEASE, PROTOVIOL, ngp->ng_S);
	    break;
	case NGS_ACQUISITION:
	case NGS_CEASE:
	    break;
	case NGS_DOWN:
	case NGS_UP:
	    if ((error = egp_check_status(ngp, egp->egp_status, PASSIVE)) != NOERROR) {
		msg = "invalid Status field in Hello";
		egp_stats.inerrors++;
		egp_stats.inmsgs--;
		ngp->ng_stats.inerrors++;
		ngp->ng_stats.inmsgs--;
		break;
	    } else {
		if (egp_rate_check(ngp, &ngp->ng_hello_rate)) {
		    error = EXSPOLL;
		    msg = "excessive HELLO rate";
		    break;
		}
		egp_send_hello(ngp, neighHeardU, egprid_h);
	    }
	    break;
    }
    if (error != NOERROR) {
	egp_send_error(ngp, egp, egplen, error, msg);
    }
}


static void
egp_event_heardu(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{

    IF_EGPPROTO egp_msg_event(ngp, "I-H-U");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    egp_send_acquire(ngp, NACEASE, PROTOVIOL, ngp->ng_S);
	    break;
	case NGS_ACQUISITION:
	case NGS_CEASE:
	    break;
	case NGS_DOWN:
	case NGS_UP:
	    if (egp_check_status(ngp, egp->egp_status, ACTIVE) != NOERROR) {
		egp_send_error(ngp, egp, egplen, EBADHEAD, "invalid Status field in I-H-U");
		egp_stats.inerrors++;
		egp_stats.inmsgs--;
		ngp->ng_stats.inerrors++;
		ngp->ng_stats.inmsgs--;
	    }
	    break;
    }
}


static void
egp_event_poll(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    int error = NOERROR;
    const char *msg = NULL;
    struct sockaddr_in source_net;

    IF_EGPPROTO egp_msg_event(ngp, "Poll");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    egp_send_acquire(ngp, NACEASE, PROTOVIOL, ngp->ng_S);
	    break;
	case NGS_ACQUISITION:
	case NGS_CEASE:
	    break;
	case NGS_DOWN:
	case NGS_UP:
	    ngp->ng_flags |= NGF_PROC_POLL;
	    if ((error = egp_check_status(ngp, egp->egp_status, PASSIVE)) != NOERROR) {
		ngp->ng_flags &= ~NGF_PROC_POLL;
		msg = "invalid Status field in Poll";
		break;
	    }
	    ngp->ng_flags &= ~NGF_PROC_POLL;
	    if (egp->egp_code != 0) {
		error = EBADHEAD;
		msg = "invalid Code field in Poll";
		break;
	    }
	    ngp->ng_R = egprid_h;
	    if (egprid_h == ngp->ng_R_lastpoll) {
		if (ngp->ng_flags & (NGF_RECV_REPOLL | NGF_SENT_UNSOL)) {
		    error = EXSPOLL;
		    msg = "too many Polls received";
		    break;
		}
		ngp->ng_flags |= NGF_RECV_REPOLL | NGF_SENT_UNSOL;
	    } else {
		ngp->ng_R_lastpoll = egprid_h;
		ngp->ng_flags &= ~(NGF_RECV_REPOLL | NGF_SENT_UNSOL);
	    }
	    if (egp_rate_check(ngp, &ngp->ng_poll_rate)) {
		error = EXSPOLL;
		msg = "excessive Polling rate";
		break;
	    }
	    if (ngp->ng_state == NGS_DOWN) {
		/* Ignore Polls in Down state */
		break;
	    }
	    ngp->ng_paddr.sin_addr = ((struct egppoll *) egp)->ep_net;
	    source_net = ngp->ng_paddr;	/* struct copy */
	    if (!rt_locate(RTS_INTERIOR, (sockaddr_un *) & source_net, RTPROTO_DIRECT)) {
		error = ENOREACH;
		msg = "no interface on net of Poll";
		break;
	    }
	    egp_send_update(ngp, 0);
	    break;
    }
    if (error != NOERROR) {
	egp_send_error(ngp, egp, egplen, error, msg);
	egp_stats.inerrors++;
	egp_stats.inmsgs--;
	ngp->ng_stats.inerrors++;
	ngp->ng_stats.inmsgs--;
    }
}


static void
egp_event_update(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    int error = NOERROR;
    const char *msg = NULL;


    IF_EGPPROTO egp_msg_event(ngp, "Update");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    egp_send_acquire(ngp, NACEASE, PROTOVIOL, ngp->ng_S);
	    break;
	case NGS_ACQUISITION:
	case NGS_DOWN:
	case NGS_CEASE:
	    break;
	case NGS_UP:
	    if (egp->egp_code != 0) {
		error = EBADHEAD;
		msg = "invalid Code field in Update";
		break;
	    }
	    if ((error = egp_check_status(ngp, egp->egp_status & ~UNSOLICITED, ACTIVE)) != NOERROR) {
		msg = "invalid Status field in Update";
		break;
	    }
	    if (gd_inet_wholenetof(((struct egpnr *) egp)->en_net) != gd_inet_wholenetof(ngp->ng_saddr.sin_addr)) {
		error = EBADHEAD;
		msg = "Update Response/Indication IP Net Address field does not match command";
		break;
	    }
	    if (egprid_h != ngp->ng_S) {/* wrong seq. # */
		/* Ignore packets with bad sequence number */
		break;
	    }
	    if (egp->egp_status & UNSOLICITED) {
		if (ngp->ng_flags & NGF_RECV_UNSOL) {
		    error = EUNSPEC;
		    msg = "too many unsolicited Update Indications";
		    break;
		}
		ngp->ng_flags |= NGF_RECV_UNSOL;
	    } else {
		if (!(ngp->ng_flags & NGF_SENT_POLL)) {
		    error = EUNSPEC;
		    msg = "too many Update Indications";
		    break;
		}
	    }
	    ngp->ng_flags &= ~(NGF_SENT_POLL | NGF_SENT_REPOLL);
	    if ((error = egp_rt_recv(ngp, egp, egplen)) != NOERROR) {
		switch (error) {
		    case EBADDATA:
			msg = "invalid Update message format";
			break;
		    case EUNSPEC:
			msg = "unable to find interface for this neighbor";
			break;
		    default:
			msg = "internal error parsing Update ";
		}
		break;
	    } else {
		ngp->ng_noupdate = 0;
	    }
	    if (egp->egp_status & UNSOLICITED) {
		/* XXX - What should we do here? */
	    }
#ifdef	notdef
	    egp_set_timer(ngp->ng_task->task_timer[EGP_TIMER_t2], ngp->ng_T2);	/* t2 is reset relative to last start */
#endif	/* notdef */
	    break;
    }
    if (error != NOERROR) {
	egp_send_error(ngp, egp, egplen, error, msg);
	egp_stats.inerrors++;
	egp_stats.inmsgs--;
	ngp->ng_stats.inerrors++;
	ngp->ng_stats.inmsgs--;
    }
}


void
egp_event_start(tp)
task *tp;
{
    struct egpngh *ngp;

    ngp = (struct egpngh *) tp->task_data;

    IF_EGPPROTO egp_msg_event(ngp, "Start");

    switch (ngp->ng_state) {
	case NGS_ACQUISITION:
	case NGS_DOWN:
	case NGS_UP:
	    trace(TR_EGP, LOG_WARNING, "egp_event_start: neighbor %s AS %d state %s Start",
		  ngp->ng_name,
		  ngp->ng_asin,
		  trace_state(egp_states, ngp->ng_state));
	case NGS_IDLE:
	    egp_send_acquire(ngp, NAREQ, UNSPEC, ngp->ng_S);
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t1], (time_t) EGP_P3);
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_P5);
	    egp_state_acquisition(ngp);
	    break;
	case NGS_CEASE:
	    break;
    }
}


/*
 * egp_event_delete() delete the current task
 */
void
egp_event_delete(ngp)
struct egpngh *ngp;
{
    struct egpngh *ngp2;

    IF_EGPPROTO egp_msg_event(ngp, "Delete");

    if (ngp->ng_task) {
	task_delete(ngp->ng_task);
    }
    /* Delete this neighbor from the list of neighbors */
    egp_neighbors--;
    if (egp_neighbor_head == ngp) {
	egp_neighbor_head = ngp->ng_next;
    } else {
	EGP_LIST(ngp2) {
	    if (ngp2->ng_next == ngp) {
		ngp2->ng_next = ngp->ng_next;
		break;
	    }
	} EGP_LISTEND;
    }
#if	defined(AGENT_SNMP)
    egp_sort_neighbors();
#endif				/* defined(AGENT_SNMP) */

    /* Now see if we were superceeded and cause him to wake up */
    EGP_LIST(ngp2) {
	if ((ngp2->ng_flags & NGF_WAIT) && equal(&ngp->ng_addr, &ngp2->ng_addr)) {
	    ngp2->ng_flags &= ~NGF_WAIT;
	    egp_event_t3(ngp2->ng_task->task_timer[EGP_TIMER_t3], (time_t) 0);
	    break;
	}
    } EGP_LISTEND;

    /* And finally free the control block */
    (void) free((caddr_t) ngp);
}


/*
 * egp_event_stop() sends Ceases to all neighbors when going down (when SIGTERM
 * received).
 *
 */

void
egp_event_stop(ngp, status)
struct egpngh *ngp;
u_int status;
{

    IF_EGPPROTO egp_msg_event(ngp, "Stop");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    if (ngp->ng_flags & NGF_DELETE) {
		egp_event_delete(ngp);
	    }
	    break;
	case NGS_ACQUISITION:
	case NGS_CEASE:
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t1]);
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t2]);
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_START_SHORT);
	    egp_state_idle(ngp);
	    break;
	case NGS_DOWN:
	case NGS_UP:
	    trace(TR_EGP, LOG_WARNING, "egp_event_stop: neighbor %s AS %d state %s Stop reason %s",
		  ngp->ng_name,
		  ngp->ng_asin,
		  trace_state(egp_states, ngp->ng_state),
		  egp_acq_status[GODOWN]);
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t1], (time_t) EGP_P3);
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t2]);
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_P5);
	    ngp->ng_status = status;
	    egp_send_acquire(ngp, NACEASE, ngp->ng_status, ngp->ng_S);
	    egp_state_cease(ngp);
	    break;
    }
}


/*ARGSUSED*/
void
 egp_event_t3
 (tip, interval)
timer *tip;
time_t interval;
{
    struct egpngh *ngp;

    ngp = (struct egpngh *) tip->timer_task->task_data;

    IF_EGPPROTO egp_msg_event(ngp, "t3");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    if (egp_group_acquired(ngp) < ngp->ng_gr_head->ng_gr_acquire) {
		egp_event_start(tip->timer_task);
	    } else {
		egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_START_RETRY);
	    }
	    break;
	case NGS_ACQUISITION:
#ifdef	EGPVERDEFAULT
	    if (ngp->ng_V != EGPVERDEFAULT) {
		egp_set_version(ngp, EGPVERDEFAULT);
		egp_event_start(tip->timer_task);
		break;
	    }
#endif				/* EGPVERDEFAULT */
	case NGS_CEASE:
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t1]);
	    egp_clear_timer(ngp->ng_task->task_timer[EGP_TIMER_t2]);
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_START_SHORT);
	    egp_state_idle(ngp);
	    break;
	case NGS_DOWN:
	case NGS_UP:
	    trace(TR_EGP, LOG_WARNING, "egp_event_t3: neighbor %s AS %d state %s Abort",
		  ngp->ng_name,
		  ngp->ng_asin,
		  trace_state(egp_states, ngp->ng_state));
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t1], (time_t) EGP_P3);
	    egp_reset_timer(ngp->ng_task->task_timer[EGP_TIMER_t3], (time_t) EGP_P5);
	    ngp->ng_status = GODOWN;
	    egp_send_acquire(ngp, NACEASE, ngp->ng_status, ngp->ng_S);
	    egp_state_cease(ngp);
	    break;
    }
}


/*ARGSUSED*/
void
egp_event_t1(tip, interval)
timer *tip;
time_t interval;
{
    struct egpngh *ngp;

    ngp = (struct egpngh *) tip->timer_task->task_data;

    IF_EGPPROTO egp_msg_event(ngp, "t1");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	    egp_msg_confused(ngp, "t1");
	    break;
	case NGS_ACQUISITION:
	    egp_send_acquire(ngp, NAREQ, UNSPEC, ngp->ng_S);
	    egp_set_timer(ngp->ng_task->task_timer[EGP_TIMER_t1], (time_t) EGP_P3);
	    break;
	case NGS_DOWN:
	case NGS_UP:
	    egp_shift_reachability(ngp);
	    if (ngp->ng_M == ACTIVE) {
		egp_send_hello(ngp, neighHello, ngp->ng_S);
	    }

	    /* Check to see if a repoll is due */
	    if ((ngp->ng_flags & NGF_SENT_POLL) && !(ngp->ng_flags & NGF_SENT_REPOLL) &&
		tip->timer_next_time - ngp->ng_task->task_timer[EGP_TIMER_t2]->timer_last_time >= ngp->ng_T1) {
		/* Time for a repoll */
		egp_do_poll(ngp, FALSE);
	    }

	    /* Make sure our interval is correct (should not be necessary) */
	    egp_set_timer(ngp->ng_task->task_timer[EGP_TIMER_t1], ngp->ng_T1);
	    break;
	case NGS_CEASE:
	    egp_send_acquire(ngp, NACEASE, ngp->ng_status, ngp->ng_S);
	    egp_set_timer(ngp->ng_task->task_timer[EGP_TIMER_t1], (time_t) EGP_P3);
	    break;
    }
}


/*ARGSUSED*/
void
egp_event_t2(tip, interval)
timer *tip;
time_t interval;
{
    struct egpngh *ngp = (struct egpngh *) tip->timer_task->task_data;

    IF_EGPPROTO egp_msg_event(ngp, "t2");

    switch (ngp->ng_state) {
	case NGS_IDLE:
	case NGS_ACQUISITION:
	case NGS_DOWN:
	case NGS_CEASE:
	    egp_msg_confused(ngp, "t2");
	    break;
	case NGS_UP:
	    egp_do_poll(ngp, FALSE);
	    break;
    }
}


/*
 * egp_recv_acquire() handles received Neighbor Acquisition messages: Request, Confirm,
 * Refuse, Cease and Cease-ack.
 *
 */
static void
egp_recv_acquire(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    int error = NOERROR;
    const char *msg = NULL;


    switch (egp->egp_code) {
	case NAREQ:			/* Neighbor acquisition request */
	    if (egplen != sizeof(struct egpacq)) {
		error = EBADHEAD;
		msg = "bad message length";
		break;
	    }
	    egp_event_request(ngp, egp, egplen);
	    break;
	case NACONF:			/* Neighbor acq. confirm */
	    if (egplen != sizeof(struct egpacq)) {
		error = EBADHEAD;
		msg = "bad message length";
		break;
	    }
	    if (egprid_h != ngp->ng_S) {
		/* Ignore packets with invalid sequence number */
		break;
	    }
	    egp_event_confirm(ngp, egp, egplen);
	    break;
	case NAREFUS:			/* Neighbor acq. refuse */
	    if (egplen != sizeof(struct egppkt) && egplen != sizeof(struct egpacq)) {
		error = EBADHEAD;
		msg = "bad message length";
		break;
	    }
	    if (egprid_h != ngp->ng_S) {
		/* Ignore packets with invalid sequence number */
		break;
	    }
	    egp_event_refuse(ngp, egp, egplen);
	    break;
	case NACEASE:			/* Neighbor acq. cease */
	    if (egplen != sizeof(struct egppkt) && egplen != sizeof(struct egpacq)) {
		error = EBADHEAD;
		msg = "bad message length";
		break;
	    }
	    egp_event_cease(ngp, egp, egplen);
	    break;
	case NACACK:			/* Neighbor acq. cease ack */
	    if (egplen != sizeof(struct egppkt) && egplen != sizeof(struct egpacq)) {
		error = EBADHEAD;
		msg = "bad message length";
		break;
	    }
	    if (egprid_h != ngp->ng_S) {
		/* Ignore packets with invalid sequence number */
		break;
	    }
	    egp_event_ceaseack(ngp, egp, egplen);
	    break;
	default:
	    error = EBADHEAD;
	    msg = "invalid Code field";
	    break;
    }
    if (error != NOERROR) {
	egp_send_error(ngp, egp, egplen, error, msg);
	egp_stats.inerrors++;
	egp_stats.inmsgs--;
	ngp->ng_stats.inerrors++;
	ngp->ng_stats.inmsgs--;
    }
    return;
}


/*
 * egp_recv_neighbor() processes received hello packet
 */
static void
egp_recv_neighbor(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{

    switch (egp->egp_code) {
	case neighHello:
	    ngp->ng_R = egprid_h;
	    egp_event_hello(ngp, egp, egplen);
	    break;
	case neighHeardU:
	    if (egprid_h != ngp->ng_S_lasthello) {
		/* Ignore packets with bad sequence numbers */
		break;
	    }
	    egp_event_heardu(ngp, egp, egplen);
	    break;
	default:
	    egp_send_error(ngp, egp, egplen, EBADHEAD, "invalid Code field");
	    egp_stats.inerrors++;
	    egp_stats.inmsgs--;
	    ngp->ng_stats.inerrors++;
	    ngp->ng_stats.inmsgs--;
	    break;
    }
    return;
}


/*ARGSUSED*/
static void
egp_recv_error(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    const char *err_msg;
    int reason;
    struct egperr *ee = (struct egperr *) egp;

    reason = htons(ee->ee_rsn);

    if (reason > EUVERSION) {
	err_msg = "(invalid reason)";
    } else {
	err_msg = egp_reasons[reason];
    }

    ngp->ng_stats.inerrmsgs++;

    trace(TR_EXT, LOG_WARNING, "egp_recv_error: neighbor %s AS %d state %s error %s",
	  ngp->ng_name,
	  ngp->ng_asin,
	  trace_state(egp_states, ngp->ng_state),
	  err_msg);

    switch (reason) {
	case EUNSPEC:
	case EBADHEAD:
	case EBADDATA:
	case EXSPOLL:
	case ENORESPONSE:
	    break;
	case EUVERSION:
	    switch (ngp->ng_state) {
		case NGS_IDLE:
		case NGS_DOWN:
		case NGS_UP:
		case NGS_CEASE:
		    break;
		case NGS_ACQUISITION:
		    if (egp->egp_ver != ngp->ng_V) {
			egp_set_version(ngp, egp->egp_ver);
		    }
		    break;
	    }
	    break;
	case ENOREACH:
	    switch (ngp->ng_state) {
		case NGS_IDLE:
		case NGS_ACQUISITION:
		case NGS_CEASE:
		case NGS_DOWN:
		    break;
		case NGS_UP:
		    egp_event_stop(ngp, GODOWN);
		    break;
	    }
	    break;
    }
}


static int
egp_check_packet(ngp)
struct egpngh *ngp;
{
    struct egppkt *egp = (struct egppkt *) recv_iovec[RECV_IOVEC_DATA].iov_base;
    int egplen;
    struct sockaddr_in addr;
    if_entry *ifp;

    sockclear_in(&addr);
    addr.sin_addr = recv_ip.ip_src;	/* struct copy */

    if (recv_ip.ip_off & ~IP_DF) {
	trace(TR_EXT, LOG_ERR, "egp_check_packet: recv fragmanted pkt from %A",
	      &addr);
	return (0);
    }
    egplen = recv_ip.ip_len;
    if (trace_flags & TR_EGP) {
	egp_trace(ngp, "EGP RECV", FALSE, egp, egplen);
    }
    /* Locate interface used to receive this packet.  If the interface	*/
    /* matches the one we think we should be using, then update the	*/
    /* interface timer.  Otherwise, don't sweat it, this peer might not	*/
    /* share a network with us.						*/

    ifp = if_withdst((sockaddr_un *) & recv_addr);
    if (ifp == ngp->ng_interface) {
	if_rtupdate(ifp);
    }
    if (gd_inet_cksum(&recv_iovec[RECV_IOVEC_DATA], 1, egplen) != (u_short) 0) {
	trace(TR_EXT, LOG_WARNING, "egp_check_packet: bad EGP checksum from %A",
	      &addr);
	return (0);
    }
    if (egplen < sizeof(struct egppkt)) {
	trace(TR_EXT, LOG_WARNING, "egp_check_packet: bad pkt length %d from %A",
	      egplen,
	      &addr);
	return (0);
    }
    return (egplen);
}


static int
egp_check_version(ngp, egp, egplen)
struct egpngh *ngp;
struct egppkt *egp;
int egplen;
{
    int error = NOERROR;
    const char *msg = NULL;

    if (!(EGPVMASK & (1 << (egp->egp_ver - 2)))) {
	if (egp->egp_type != EGPERR) {
	    error = EUVERSION;
	    msg = "unsupported version";
	}
    } else if (ngp && (egp->egp_ver != ngp->ng_V)) {
	switch (egp->egp_type) {
	    case EGPACQ:
		egp_set_version(ngp, egp->egp_ver);
		break;
	    case EGPHELLO:
	    case EGPNR:
	    case EGPPOLL:
		error = EBADHEAD;
		msg = "invalid Version field";
		break;
	    case EGPERR:
		/* Fall through and let egp_recv's switch switch versions */
		break;
	}
    }
    if (error != NOERROR) {
	egp_send_error(ngp, egp, egplen, error, msg);
	return (1);
    } else {
	return (0);
    }
}


/*
 *	Process and incoming EGP packet from a known neighbor
 */
/*ARGSUSED*/
void
egp_recv(tp)
task *tp;
{
    int count;
    struct egpngh *ngp;
    struct egppkt *egp = (struct egppkt *) recv_iovec[RECV_IOVEC_DATA].iov_base;
    int egplen;

    if (task_receive_packet(tp, &count)) {
	return;
    }
    ngp = (struct egpngh *) tp->task_data;

    egplen = egp_check_packet(ngp);
    if (!egplen) {
	egp_stats.inerrors++;
	ngp->ng_stats.inerrors++;
	return;
    }
    egprid_h = ntohs(egp->egp_id);	/* save sequence number in host byte order */

    if (egp_check_version(ngp, egp, egplen)) {
	egp_stats.inerrors++;
	ngp->ng_stats.inerrors++;
	return;
    }
    egp_stats.inmsgs++;
    ngp->ng_stats.inmsgs++;

    switch (egp->egp_type) {
	case EGPACQ:
	    egp_recv_acquire(ngp, egp, egplen);
	    break;
	case EGPHELLO:
	    egp_recv_neighbor(ngp, egp, egplen);
	    break;
	case EGPNR:
	    egp_event_update(ngp, egp, egplen);
	    break;
	case EGPPOLL:
	    egp_event_poll(ngp, egp, egplen);
	    break;
	case EGPERR:
	    egp_recv_error(ngp, egp, egplen);
	    break;
	default:
	    egp_send_error(ngp, egp, egplen, EBADHEAD, "invalid Type field");
	    egp_stats.inerrors++;
	    egp_stats.inmsgs--;
	    ngp->ng_stats.inerrors++;
	    ngp->ng_stats.inmsgs--;
	    return;
    }
}


/*
 *	Compare the old neighbor's configuration with the new configuration.
 *	Some options may be changed on the fly, some require the neighbor to be restarted.
 */
int
egp_neighbor_changed(ngpo, ngpn)
struct egpngh *ngpo, *ngpn;
{
    int changed = FALSE;
    flag_t changed_options = ngpo->ng_options ^ ngpn->ng_options;
    flag_t new_options = ngpn->ng_options;

    /* XXX - What about maxacquire? */

    if (changed_options & (NGO_ASIN | NGO_ASOUT | NGO_INTERFACE | NGO_GATEWAY | NGO_PREFERENCE | NGO_P1 | NGO_P2)) {
	changed = TRUE;
    }
    if ((new_options & NGO_ASIN) && (ngpn->ng_asin != ngpo->ng_asin)) {
	changed = TRUE;
    }
    if ((new_options & NGO_ASOUT) && (ngpn->ng_asout != ngpo->ng_asout)) {
	changed = TRUE;
    }
    if ((new_options & NGO_INTERFACE) && (ngpn->ng_interface != ngpo->ng_interface)) {
	changed = TRUE;
    }
    if ((new_options & NGO_GATEWAY) && !equal(&ngpn->ng_gateway, &ngpo->ng_gateway)) {
	changed = TRUE;
    }
    if ((new_options & NGO_PREFERENCE) && (ngpn->ng_preference != ngpo->ng_preference)) {
	changed = TRUE;
    }
    if ((new_options & NGO_P1) && (ngpn->ng_P1 != ngpo->ng_P1)) {
	changed = TRUE;
    }
    if ((new_options & NGO_P2) && (ngpn->ng_P2 != ngpo->ng_P2)) {
	changed = TRUE;
    }
    if (!changed) {
	/* Nothing has changed that has required a restart.  Let's deal with things */
	/* that can be changed on the fly */

	/* Default propagation and generation options can be changed by just changing the flags */
#define	FLAGS	(NGO_NOGENDEFAULT|NGO_DEFAULTIN|NGO_DEFAULTOUT|NGO_METRICOUT|NGO_SADDR|NGO_VERSION)
	ngpo->ng_options = (ngpo->ng_options & ~(FLAGS)) | (ngpn->ng_options & (FLAGS));
	changed_options &= ~(FLAGS);
#undef	FLAGS

	ngpo->ng_metricout = ngpn->ng_metricout;

	if (ngpn->ng_options & NGO_SADDR) {
	    ngpo->ng_saddr = ngpn->ng_saddr;	/* struct copy */
	}
	if (ngpn->ng_options & NGO_VERSION) {
	    ngpo->ng_version = ngpn->ng_version;
	}
    }
    return (changed);
}


static void
egp_dump_rate(fd, rp, name)
FILE *fd;
struct egp_rate *rp;
char *name;
{
    int i;

    (void) fprintf(fd, "\t\t%s: rate_min: %#T rate_last: %T\n",
		   name,
		   rp->rate_min,
		   rp->rate_last);
    (void) fprintf(fd, "\t\t\twindow:");

    for (i = 0; i < RATE_WINDOW; i++) {
	(void) fprintf(fd, " %#T", rp->rate_window[i]);
    }
    (void) fprintf(fd, "\n");
}


/*
 *	Dump EGP status to dump file
 */
void
egp_dump(fd)
FILE *fd;
{
    struct egpngh *ngp;
    struct egpngh *gr_ngp;

    /*
     *	EGP neighbor status
     */
    if (doing_egp) {
	(void) fprintf(fd, "EGP status:\n");
	(void) fprintf(fd, "\tdefaultegpmetric: %d\tpreference: %d\n",
		       egp_default_metric,
		       egp_preference);
	(void) fprintf(fd, "\tPackets In: %u\t\t\tErrors In: %u\n",
		       egp_stats.inmsgs,
		       egp_stats.inerrors);
	(void) fprintf(fd, "\tPackets Out: %u\t\t\tErrors Out: %u\n",
		       egp_stats.outmsgs,
		       egp_stats.outerrors);
	(void) fprintf(fd, "\t\t\t\t\tTotal Errors: %u\n\n",
		       egp_stats.outerrors + egp_stats.inerrors);
	if (egp_accept_list) {
	    control_exterior_dump(fd, 1, control_accept_dump, egp_accept_list);
	}
	if (egp_propagate_list) {
	    control_exterior_dump(fd, 1, control_propagate_dump, egp_propagate_list);
	}
	gr_ngp = (struct egpngh *) 0;
	EGP_LIST(ngp) {
	    if (gr_ngp != ngp->ng_gr_head) {
		gr_ngp = ngp->ng_gr_head;
		(void) fprintf(fd, "\n\tGroup: %d\tMembers: %d\tAcquire: %d\tAcquired: %d",
			       gr_ngp->ng_gr_index,
			       gr_ngp->ng_gr_number,
			       gr_ngp->ng_gr_acquire,
			       egp_group_acquired(ngp));
		if ((gr_ngp->ng_options & NGO_PREFERENCE)) {
		    (void) fprintf(fd, "\n\t\tPreference: %u",
				   gr_ngp->ng_preference);
		}
		(void) fprintf(fd, "\n\t\t\tASout: ");
		if ((gr_ngp->ng_options & NGO_ASOUT)) {
		    (void) fprintf(fd, "%u",
				   gr_ngp->ng_asout);
		} else {
		    (void) fprintf(fd, "N/A");
		}
		(void) fprintf(fd, "\tASin: ");
		if ((gr_ngp->ng_options & NGO_ASIN) || gr_ngp->ng_asin) {
		    (void) fprintf(fd, "%u",
				   gr_ngp->ng_asin);
		} else {
		    (void) fprintf(fd, "N/A");
		}
		(void) fprintf(fd, "\n");
	    }
	    (void) fprintf(fd, "\n\t%s\tV: %d", ngp->ng_name, ngp->ng_V);
	    (void) fprintf(fd, "\tInterface: %A\n",
			   &ngp->ng_interface->int_addr);
	    (void) fprintf(fd, "\t\tReachability: [%04B] %d\tj: %d\tk: %d\n",
			   ngp->ng_responses,
			   egp_reachability[ngp->ng_responses],
			   ngp->ng_j,
			   ngp->ng_k);
	    (void) fprintf(fd, "\t\tT1: %T\tT2: %T\n", ngp->ng_T1, ngp->ng_T2);
	    (void) fprintf(fd, "\t\tt1: %T\tt2: %T\tt3: %T\n",
		 ngp->ng_task->task_timer[EGP_TIMER_t1]->timer_next_time,
		 ngp->ng_task->task_timer[EGP_TIMER_t2]->timer_next_time,
		ngp->ng_task->task_timer[EGP_TIMER_t3]->timer_next_time);
	    (void) fprintf(fd, "\t\tP1: %#T\tP2: %#T\tP3: %#T\tP4: %#T\tP5: %#T\n",
			   ngp->ng_P1,
			   ngp->ng_P2,
			   EGP_P3,
			   EGP_P4,
			   EGP_P5);
	    (void) fprintf(fd, "\t\tState: <%s>\tMode: %s\n", trace_state(egp_states, ngp->ng_state), egp_acq_status[ngp->ng_M]);
	    (void) fprintf(fd, "\t\tFlags: %#x <%s>\n", ngp->ng_flags, trace_bits(egp_flags, ngp->ng_flags));
	    (void) fprintf(fd, "\t\tOptions: %#x <%s>\n", ngp->ng_options, trace_bits(egp_options, ngp->ng_options));
	    (void) fprintf(fd, "\t\tLast poll received: %A",
			   &ngp->ng_paddr);
	    (void) fprintf(fd, "\tNet to poll: %A\n",
			   &ngp->ng_saddr);
	    (void) fprintf(fd, "\t\tMaximum Route Age: %#T\n", ngp->ng_rtage);
	    (void) fprintf(fd, "\t\tMetricOut: ");
	    if ((ngp->ng_options & NGO_METRICOUT)) {
		(void) fprintf(fd, "%d", ngp->ng_metricout);
	    } else {
		(void) fprintf(fd, "N/A");
	    }
	    (void) fprintf(fd, "\n\t\tDefaultMetric: ");
	    if ((ngp->ng_options & NGO_DEFAULTOUT)) {
		(void) fprintf(fd, "%d", ngp->ng_defaultmetric);
	    } else {
		(void) fprintf(fd, "N/A");
	    }
	    (void) fprintf(fd, "\tGateway: ");
	    if ((ngp->ng_options & NGO_GATEWAY)) {
		(void) fprintf(fd, "%A",
			       &ngp->ng_gateway);
	    } else {
		(void) fprintf(fd, "N/A");
	    }
	    (void) fprintf(fd, "\n");
	    egp_dump_rate(fd, &ngp->ng_poll_rate, "Poll");
	    egp_dump_rate(fd, &ngp->ng_hello_rate, "Hello");
	    (void) fprintf(fd, "\t\tPackets In: %u\t\t\tErrors In: %u\n",
			   ngp->ng_stats.inmsgs,
			   ngp->ng_stats.inerrors);
	    (void) fprintf(fd, "\t\tPackets Out: %d\t\t\tErrors Out: %d\n",
			   ngp->ng_stats.outmsgs,
			   ngp->ng_stats.outerrors);
	} EGP_LISTEND;
	(void) fprintf(fd, "\n");
    }
}


#endif				/* PROTO_EGP */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.