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

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

/*
 *  $Header: /disk/d/src/devel/gated/dist/src/RCS/bgp.c,v 2.1 92/02/24 14:12:23 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 "bgp.h"

#ifdef	PROTO_BGP

void bgp_event_OpenFail();
void bgp_event_Closed();

/*
 *
 *  Routines to log messages about events and state transitions
 *
 */
void
bgp_msg_event(bnp, event)
bgpPeer *bnp;
int event;
{

    trace(TR_BGP, 0, "bgp_msg_event: peer %s state %s event %s",
	  bnp->bgp_name, trace_state(bgpStates, bnp->bgp_state), trace_state(bgpEvents, event));
}


void
bgp_msg_state(bnp, state)
bgpPeer *bnp;
int state;
{

    trace(TR_BGP, 0, "bgp_msg_state: peer %s state %s transition to state %s",
	  bnp->bgp_name, trace_state(bgpStates, bnp->bgp_state), trace_state(bgpStates, state));
}


void
bgp_msg_confused(bnp, event)
bgpPeer *bnp;
int event;
{
    trace(TR_BGP, 0, "bgp_msg_confused: peer %s event %s should not occur in state %s",
	  bnp->bgp_name, trace_state(bgpEvents, event), trace_state(bgpStates, bnp->bgp_state));
}


/*
 *
 *	Routines to transition to new states.  Processing that always
 *	occurs during a state transition is done here.
 *
 */

void
bgp_state_Idle(bnp, interval)
bgpPeer *bnp;
time_t interval;			/* How long before entering Active state */
{
    int changes = 0;

    IF_BGPPROTO bgp_msg_state(bnp, BGPSTATE_IDLE);

    /* Set the Abort timer for automatic Start event */
    if (interval) {
	timer_set(bnp->bgp_task->task_timer[BGPTIMER_HOLDTIME], interval);
    }
    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_ACTIVE:
	    break;
	case BGPSTATE_CONNECT:
	    bgp_connect_finit(bnp);
	    break;
	case BGPSTATE_ESTABLISHED:
	    trace(TR_BGP, LOG_NOTICE, "bgp_state_Idle: lost peer %s AS %d",
		  bnp->bgp_name,
		  bnp->bgp_asin);
	    changes += rt_gwunreach(bnp->bgp_task, &bnp->bgp_gw);
	    if (bnp->bgp_flags & BGPF_GENDEFAULT) {
		changes += rt_default_delete();
		bnp->bgp_flags &= ~BGPF_GENDEFAULT;
	    }
	    if (changes) {
		trace(TR_RT, 0, "bgp_state_Idle: above changes due to loss of peer %s", bnp->bgp_name);
	    }
	case BGPSTATE_OPENSENT:
	case BGPSTATE_OPENCONFIRM:
	    bgp_session_finit(bnp, FALSE);
	    break;
    }

    if (bnp->bgp_packet) {
	/* Free packet buffer */
	(void) free((caddr_t) bnp->bgp_packet);
    }
    bnp->bgp_state = BGPSTATE_IDLE;

}


void
bgp_state_Active(bnp)
bgpPeer *bnp;
{

    IF_BGPPROTO bgp_msg_state(bnp, BGPSTATE_ACTIVE);

    bnp->bgp_state = BGPSTATE_ACTIVE;

    if (bnp->bgp_task->task_socket != -1) {
	bgp_session_finit(bnp, FALSE);
    }
    timer_set(bnp->bgp_task->task_timer[BGPTIMER_HOLDTIME], (time_t) BGP_IDLE_SHORT);

}


void
bgp_state_Connect(bnp)
bgpPeer *bnp;
{

    IF_BGPPROTO bgp_msg_state(bnp, BGPSTATE_CONNECT);

    bnp->bgp_state = BGPSTATE_CONNECT;

    timer_reset(bnp->bgp_task->task_timer[BGPTIMER_HOLDTIME]);
    bgp_connect_init(bnp);
}


void
bgp_state_OpenSent(bnp)
bgpPeer *bnp;
{

    IF_BGPPROTO bgp_msg_state(bnp, BGPSTATE_OPENSENT);

    bnp->bgp_state = BGPSTATE_OPENSENT;

    /* Allocate receive packet */
    bnp->bgp_packet = (bgpPdu *) malloc(BGPMAXPACKETSIZE);
    if (!bnp->bgp_packet) {
	trace(TR_ALL, LOG_ERR, "bgp_state_OpenSent: peer %s malloc receive packet: %m",
	      bnp->bgp_name);
	quit(errno);
    }
    bnp->bgp_length = 0;		/* Setup to receive first packet header */
}


void
bgp_state_OpenConfirm(bnp)
bgpPeer *bnp;
{

    IF_BGPPROTO bgp_msg_state(bnp, BGPSTATE_OPENCONFIRM);

    bnp->bgp_state = BGPSTATE_OPENCONFIRM;
}


void
bgp_state_Established(bnp)
bgpPeer *bnp;
{

    IF_BGPPROTO bgp_msg_state(bnp, BGPSTATE_ESTABLISHED);

    bnp->bgp_state = BGPSTATE_ESTABLISHED;

    /* Reset the abort timer */
    timer_set(bnp->bgp_task->task_timer[BGPTIMER_KEEPALIVE], bnp->bgp_holdtime_out / 3);

    trace(TR_BGP, LOG_WARNING, "bgp_state_Established: established peer %s AS %d",
	  bnp->bgp_name,
	  bnp->bgp_asin);
}


void
bgp_send(bnp, PDU, length)
bgpPeer *bnp;
bgpPdu *PDU;
int length;
{

    length += sizeof(pduHeader);

    PDU->header.marker = htons(bgpMarker);
    PDU->header.length = length;
    PDU->header.length = htons(PDU->header.length);
    PDU->header.version = bgpVersion;
    PDU->header.holdTime = htons(bnp->bgp_holdtime_out);

    bgp_trace((struct sockaddr_in *) & bnp->bgp_interface->int_addr,
	      (struct sockaddr_in *) & bnp->bgp_addr,
	      "Send",
	      PDU,
	      length);

    if (task_send_packet(bnp->bgp_task, (caddr_t) PDU, length, 0, (sockaddr_un *) 0) < 0) {
	switch (errno) {
	    case ENETDOWN:
	    case ENETUNREACH:
	    case EHOSTDOWN:
	    case EHOSTUNREACH:
		break;

	    default:
		switch (bnp->bgp_state) {
		    case BGPSTATE_IDLE:
		    case BGPSTATE_ACTIVE:
			break;

		    case BGPSTATE_CONNECT:
			bgp_event_OpenFail(bnp);
			break;

		    case BGPSTATE_OPENSENT:
		    case BGPSTATE_OPENCONFIRM:
		    case BGPSTATE_ESTABLISHED:
			bgp_event_Closed(bnp);
			break;
		}
	}
    }
}


void
bgp_send_KeepAlive(bnp)
bgpPeer *bnp;
{
    bgpPdu *PDU = (bgpPdu *) bgp_send_buffer;

    PDU->header.type = bgpPduKeepAlive;

    bgp_send(bnp, PDU, 0);
}


void
bgp_send_OpenConfirm(bnp)
bgpPeer *bnp;
{
    bgpPdu *PDU = (bgpPdu *) bgp_send_buffer;

    PDU->header.type = bgpPduOpenConfirm;

    bgp_send(bnp, PDU, 0);
}


void
bgp_send_Open(bnp)
bgpPeer *bnp;
{
    bgpPdu *PDU = (bgpPdu *) bgp_send_buffer;

    PDU->header.type = bgpPduOpen;

    PDU->pdu.open.openAs = htons(bnp->bgp_asout);
    PDU->pdu.open.openLinkType = bnp->bgp_linktype;
    PDU->pdu.open.openAuthCode = htons(0);

    bgp_send(bnp, PDU, sizeof(openPdu) - 1);
}


void
bgp_send_Notify(bnp, code, data)
bgpPeer *bnp;
int code;				/* Error code */
u_char *data;				/* Data in network byte order */
{
    int length;
    bgpPdu *PDU = (bgpPdu *) bgp_send_buffer;

    length = notifyLengths[code];

    PDU->header.type = bgpPduNotify;

    PDU->pdu.notify.notifyCode = code;
    PDU->pdu.notify.notifyCode = htons(PDU->pdu.notify.notifyCode);
    memcpy((char *) PDU->pdu.notify.notifyData, (caddr_t) data, length);

    bgp_send(bnp, PDU, sizeof(PDU->pdu.notify.notifyCode) + length);

}


void
bgp_send_NotifyUpdate(bnp, code, packet, size)
bgpPeer *bnp;
int code;				/* Error code */
bgpPdu *packet;
int size;
{
    int length;
    u_short short_code;
    bgpPdu *PDU = (bgpPdu *) bgp_send_buffer;

    length = sizeof(notifyPdu) - 1 + size - sizeof(pduHeader);
    if ((length + sizeof(pduHeader)) > BGPMAXPACKETSIZE) {
	length = BGPMAXPACKETSIZE;
    }
    PDU->header.type = bgpPduNotify;

    PDU->pdu.notify.notifyCode = htons(BGPERRCD_UPDATE);
    short_code = code;
    short_code = htons(short_code);
    memcpy((caddr_t) PDU->pdu.notify.notifyData, (caddr_t) & short_code, sizeof(PDU->pdu.notify.notifyData));

    memcpy((char *) PDU->pdu.notify.notifyPacket, (caddr_t) packet + sizeof(pduHeader),
	   length - (sizeof(PDU->pdu.notify) - sizeof(PDU->pdu.notify.notifyPacket)));

    bgp_send(bnp, PDU, length);

}



void
bgp_event_Start(bnp)
bgpPeer *bnp;
{

    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_START);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_ACTIVE:
	    bgp_state_Connect(bnp);
	    break;
	case BGPSTATE_CONNECT:
	case BGPSTATE_OPENSENT:
	case BGPSTATE_OPENCONFIRM:
	case BGPSTATE_ESTABLISHED:
	    bgp_msg_confused(bnp, BGPEVENT_START);
#ifdef	notdef
	    /* Allow for operator initiated starts */
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
#endif				/* notdef */
	    break;
    }
}


void
bgp_event_Open(bnp)
bgpPeer *bnp;
{
    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_OPEN);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_OPENSENT:
	case BGPSTATE_OPENCONFIRM:
	case BGPSTATE_ESTABLISHED:
	    bgp_msg_confused(bnp, BGPEVENT_OPEN);
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
	    break;
	case BGPSTATE_ACTIVE:
	case BGPSTATE_CONNECT:
	    bgp_send_Open(bnp);
	    timer_set(bnp->bgp_task->task_timer[BGPTIMER_HOLDTIME], (time_t) BGP_ABORT_OPEN);
	    bgp_state_OpenSent(bnp);
    }

}


void
bgp_event_Closed(bnp)
bgpPeer *bnp;
{
    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_CLOSED);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_ACTIVE:
	case BGPSTATE_CONNECT:
	    bgp_msg_confused(bnp, BGPEVENT_CLOSED);
	    /* Fall Through */
	case BGPSTATE_ESTABLISHED:
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_SHORT);
	    break;
	case BGPSTATE_OPENSENT:
	    /* Deviation from RFC1105 - need to do this to make opens succeed */
	    bgp_state_Active(bnp);
	    break;
	case BGPSTATE_OPENCONFIRM:
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_SHORT);
	    break;
    }
}


void
bgp_event_OpenFail(bnp)
bgpPeer *bnp;
{
    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_OPENFAIL);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_ACTIVE:
	case BGPSTATE_OPENCONFIRM:
	case BGPSTATE_ESTABLISHED:
	    bgp_msg_confused(bnp, BGPEVENT_OPENFAIL);
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
	    break;
	case BGPSTATE_CONNECT:
	    bgp_state_Active(bnp);
	    break;
	case BGPSTATE_OPENSENT:
	    /* Open failed after connect succeded */
	    bgp_state_Active(bnp);
	    break;
    }
}



void
bgp_event_RecvOpen(bnp, PDU, length)
bgpPeer *bnp;
bgpPdu *PDU;
int length;
{
    int error = 0;
    u_char *errptr = NULL;
    static u_char linkTypes[] =
    {
	openLinkInternal,
	openLinkDown,
	openLinkUp,
	openLinkHorizontal,
    };

#define	ptrTypeInternal	&linkTypes[0]	/* Pointer to correct link type for internal connection */

    if (length < (sizeof(pduHeader) + sizeof(openPdu) - sizeof(PDU->pdu.open.openAuthData))) {
	error = BGPERRCD_MSGLEN;
	errptr = (u_char *) & PDU->header.length;
	goto Error;
    }
    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_RECVOPEN);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_ACTIVE:
	case BGPSTATE_CONNECT:
	case BGPSTATE_OPENCONFIRM:
	case BGPSTATE_ESTABLISHED:
	    bgp_msg_confused(bnp, BGPEVENT_RECVOPEN);
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
	    return;
	case BGPSTATE_OPENSENT:
	    /* If AsIn option specified, verify that his AS matches */
	    if (bnp->bgp_options & BGPO_ASIN) {
		if (bnp->bgp_asin != ntohs(PDU->pdu.open.openAs)) {
		    error = BGPERRCD_OPENAS;
		    goto Error;
		}
	    } else {
		bnp->bgp_asin = ntohs(PDU->pdu.open.openAs);
		bnp->bgp_accept = control_exterior_locate(bgp_accept_list, bnp->bgp_asin);
		bnp->bgp_propagate = control_exterior_locate(bgp_propagate_list, bnp->bgp_asin);
	    }

	    /* If his AS matches ours, link type must be Internal, if it does not, it must not */
	    if (bnp->bgp_asin == bnp->bgp_asout) {
		if (PDU->pdu.open.openLinkType != openLinkInternal) {
		    error = BGPERRCD_LINKTYPE;
		    errptr = ptrTypeInternal;
		    goto Error;
		}
	    } else {
		if (PDU->pdu.open.openLinkType == openLinkInternal) {
		    error = BGPERRCD_LINKTYPE;
		    errptr = ptrTypeInternal;
		    goto Error;
		}
		if (PDU->pdu.open.openLinkType != linkTypes[bnp->bgp_linktype]) {
		    error = BGPERRCD_LINKTYPE;
		    errptr = &linkTypes[bnp->bgp_linktype];
		    goto Error;
		}
	    }

	    /* Authorization code goes here */
	    if (PDU->pdu.open.openAuthCode != 0) {
		error = BGPERRCD_AUTHCODE;
		goto Error;
	    }
	    /* Open packet OK, send confirm, change state and set HoldTimer */
	    bgp_send_OpenConfirm(bnp);
	    bgp_state_OpenConfirm(bnp);
	    timer_set(bnp->bgp_task->task_timer[BGPTIMER_HOLDTIME], bnp->bgp_holdtime_in);
    }

  Error:
    if (error) {
	bgp_send_Notify(bnp, error, errptr);
	bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
    }
}


/*ARGSUSED*/
void
bgp_event_RecvConfirm(bnp, PDU, length)
bgpPeer *bnp;
bgpPdu *PDU;
int length;
{

    if (length != sizeof(pduHeader)) {
	bgp_send_Notify(bnp, BGPERRCD_MSGLEN, (u_char *) & PDU->header.length);
	bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
    } else {
	IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_RECVCONFIRM);

	switch (bnp->bgp_state) {
	    case BGPSTATE_IDLE:
	    case BGPSTATE_ACTIVE:
	    case BGPSTATE_CONNECT:
	    case BGPSTATE_OPENSENT:
	    case BGPSTATE_ESTABLISHED:
		bgp_msg_confused(bnp, BGPEVENT_RECVCONFIRM);
		bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
		break;
	    case BGPSTATE_OPENCONFIRM:
		bgp_state_Established(bnp);
		bgp_send_update(bnp, FALSE);	/* Send a complete update */
	}
    }

}


void
bgp_event_RecvKeepAlive(bnp, PDU, length)
bgpPeer *bnp;
bgpPdu *PDU;
int length;
{

    if (length != sizeof(pduHeader)) {
	bgp_send_Notify(bnp, BGPERRCD_MSGLEN, (u_char *) & PDU->header.length);
	bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
    } else {
	IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_RECVKEEPALIVE);

	switch (bnp->bgp_state) {
	    case BGPSTATE_IDLE:
	    case BGPSTATE_ACTIVE:
	    case BGPSTATE_CONNECT:
	    case BGPSTATE_OPENSENT:
	    case BGPSTATE_OPENCONFIRM:
		bgp_msg_confused(bnp, BGPEVENT_RECVKEEPALIVE);
		bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
		break;
	    case BGPSTATE_ESTABLISHED:
		timer_set(bnp->bgp_task->task_timer[BGPTIMER_HOLDTIME], bnp->bgp_holdtime_in);
	}
    }
}


void
bgp_event_RecvUpdate(bnp, PDU, length)
bgpPeer *bnp;
bgpPdu *PDU;
int length;
{
    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_RECVUPDATE);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_ACTIVE:
	case BGPSTATE_CONNECT:
	case BGPSTATE_OPENSENT:
	case BGPSTATE_OPENCONFIRM:
	    bgp_msg_confused(bnp, BGPEVENT_RECVUPDATE);
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
	    break;
	case BGPSTATE_ESTABLISHED:
	    bgp_recv_Update(bnp, PDU, length);
	    timer_set(bnp->bgp_task->task_timer[BGPTIMER_HOLDTIME], bnp->bgp_holdtime_in);
    }
}


/*ARGSUSED*/
void
bgp_event_RecvNotify(bnp, PDU, length)
bgpPeer *bnp;
bgpPdu *PDU;
int length;
{
    u_short error_code;
    const char *err_msg = "Invalid error code";

#ifdef	notdef
    /* XXX - This calculation was not working correctly */
    if (length < (sizeof(pduHeader) + sizeof(notifyPdu) - sizeof(PDU->pdu.notify.notifyPacket))) {
	error = BGPERRCD_MSGLEN;
	errptr = (u_char *) & PDU->header.length;
	goto Error;
    }
#endif				/* notdef */

    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_RECVNOTIFY);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_ACTIVE:
	case BGPSTATE_CONNECT:
	    bgp_msg_confused(bnp, BGPEVENT_RECVNOTIFY);
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
	    break;
	case BGPSTATE_OPENSENT:
	case BGPSTATE_OPENCONFIRM:
	case BGPSTATE_ESTABLISHED:
	    error_code = ntohs(PDU->pdu.notify.notifyCode);
	    if (error_code != BGPERRCD_UPDATE) {
		if (error_code && (error_code <= BGPERRCD_MAX)) {
		    err_msg = trace_state(bgpErrors, error_code);
		}
		trace(TR_EXT, LOG_WARNING, "bgp_event_RecvNotify: peer %s error %s(%d)",
		      bnp->bgp_name,
		      err_msg,
		      error_code);
		if (error_code == BGPERRCD_CEASE) {
		    bgp_state_Idle(bnp, (time_t) BGP_IDLE_SHORT);
		} else {
		    bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
		}
	    } else {
		memcpy((caddr_t) & error_code, (caddr_t) PDU->pdu.notify.notifyData, sizeof(error_code));
		error_code = ntohs(error_code);
		if (error_code && (error_code <= BGPUPDERR_MAX)) {
		    err_msg = trace_state(bgpUpdateErrors, error_code);
		}
		trace(TR_EXT, LOG_WARNING, "bgp_event_RecvNotify: peer %s update error %s(%d)",
		      bnp->bgp_name,
		      err_msg,
		      error_code);
		timer_set(bnp->bgp_task->task_timer[BGPTIMER_HOLDTIME], bnp->bgp_holdtime_in);
	    }
    }
}


/*ARGSUSED*/
void
bgp_event_Holdtime(tip, interval)
timer *tip;
time_t interval;
{
    bgpPeer *bnp = (bgpPeer *) tip->timer_task->task_data;

    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_HOLDTIME);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_ACTIVE:
	    /* Automatically issue a start event when HoldTimer fires */
	    /* in Idle and Active states */
	    bgp_event_Start(bnp);
	    break;
	case BGPSTATE_CONNECT:
	    bgp_msg_confused(bnp, BGPEVENT_HOLDTIME);
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
	    break;
	case BGPSTATE_OPENSENT:
	case BGPSTATE_OPENCONFIRM:
	case BGPSTATE_ESTABLISHED:
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_SHORT);
    }
}


/*ARGSUSED*/
void
bgp_event_KeepAlive(tip, interval)
timer *tip;
time_t interval;
{
    bgpPeer *bnp = (bgpPeer *) tip->timer_task->task_data;

    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_KEEPALIVE);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_ACTIVE:
	case BGPSTATE_CONNECT:
	case BGPSTATE_OPENSENT:
	case BGPSTATE_OPENCONFIRM:
	    bgp_msg_confused(bnp, BGPEVENT_KEEPALIVE);
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
	    break;
	case BGPSTATE_ESTABLISHED:
	    bgp_send_KeepAlive(bnp);
    }
}


void
bgp_event_Cease(bnp)
bgpPeer *bnp;
{
    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_CEASE);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	case BGPSTATE_ACTIVE:
	case BGPSTATE_CONNECT:
	case BGPSTATE_OPENSENT:
	case BGPSTATE_OPENCONFIRM:
	    bgp_msg_confused(bnp, BGPEVENT_CEASE);
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
	    break;
	case BGPSTATE_ESTABLISHED:
	    bgp_send_Notify(bnp, BGPERRCD_CEASE, (u_char *) 0);	/* XXX - is the spec right about this? */
	    bgp_state_Idle(bnp, (time_t) BGP_IDLE_SHORT);
    }
}


void
bgp_event_Stop(bnp)
bgpPeer *bnp;
{

    IF_BGPPROTO bgp_msg_event(bnp, BGPEVENT_STOP);

    switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	    break;
	case BGPSTATE_OPENSENT:
	case BGPSTATE_OPENCONFIRM:
	case BGPSTATE_ESTABLISHED:
	    /* If connected, send an error before shutting down */
	    bgp_send_Notify(bnp, BGPERRCD_CEASE, (u_char *) 0);
	    /* Fall Through */
	case BGPSTATE_ACTIVE:
	case BGPSTATE_CONNECT:
	    bgp_state_Idle(bnp, (time_t) 0);
	    break;
    }
}


/*
 *  Process a complete received packet
 */
void
bgp_in(tp)
task *tp;
{
    int length;
    int error = 0;
    u_char *errptr = NULL;
    u_short holdTime;
    bgpPeer *bnp = (bgpPeer *) tp->task_data;
    bgpPdu *PDU;

    PDU = bnp->bgp_packet;
    length = bnp->bgp_length;

    bgp_trace((struct sockaddr_in *) & bnp->bgp_addr,
	      (struct sockaddr_in *) & bnp->bgp_interface->int_addr,
	      "Recv",
	      PDU,
	      length);

    holdTime = ntohs(PDU->header.holdTime);
    /* XXX - Should check validity of Hold Time */
    if (bnp->bgp_holdtime_in != holdTime) {
	bnp->bgp_holdtime_in = holdTime;
	IF_BGPPROTO trace(TR_BGP, 0, "bgp_in: peer %s holdtime set to %#T",
			   bnp->bgp_name,
			   bnp->bgp_holdtime_in);
    }
    switch (PDU->header.type) {
	case bgpPduOpen:
	    bgp_event_RecvOpen(bnp, PDU, length);
	    break;
	case bgpPduUpdate:
	    if (length < (sizeof(pduHeader) + updatePduMinSize)) {
		error = BGPERRCD_MSGLEN;
		errptr = (u_char *) & PDU->header.length;
		goto Error;
	    }
	    bgp_event_RecvUpdate(bnp, PDU, length);
	    break;
	case bgpPduNotify:
	    bgp_event_RecvNotify(bnp, PDU, length);
	    break;
	case bgpPduKeepAlive:
	    bgp_event_RecvKeepAlive(bnp, PDU, length);
	    break;
	case bgpPduOpenConfirm:
	    bgp_event_RecvConfirm(bnp, PDU, length);
	    break;
    }

  Error:
    if (error) {
	bgp_send_Notify(bnp, error, errptr);
	bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
    }
}


/*
 * Process a successful select for read by reading up to one BGP
 * packet or until the read would block.
 */
void
bgp_read(tp)
task *tp;
{
    int error = 0;
    int count;
    u_char *errptr = NULL;
    bgpPeer *bnp = (bgpPeer *) tp->task_data;
    bgpPdu *PDU = bnp->bgp_packet;

    if (!bnp->bgp_length) {
	/* New packet */
	bnp->bgp_length = sizeof(pduHeader);
	bnp->bgp_length_accumulated = 0;
	bnp->bgp_readpointer = (caddr_t) bnp->bgp_packet;
	bnp->bgp_flags |= BGPF_HEADER;	/* Indicate we are trying to read header */
    }
    while ((count = bgp_recv(tp)) > 0) {

	/* Process data if we have read desired length */
	if (bnp->bgp_length_accumulated == bnp->bgp_length) {

	    /* Complete packet or header has been read */
	    if (bnp->bgp_flags & BGPF_HEADER) {

		/* Complete Header has been read */

		bnp->bgp_length = ntohs(PDU->header.length);
		/* Verify Marker */
		if (ntohs(PDU->header.marker) != bgpMarker) {
		    trace(TR_INT, LOG_WARNING, "bgp_read: peer %s missing marker or out of sync",
			  bnp->bgp_name);
		    error = BGPERRCD_SYNC;
		    goto Error;
		};

		/* Verify packet length */
		if ((bnp->bgp_length < sizeof(pduHeader)) || (bnp->bgp_length > BGPMAXPACKETSIZE)) {
		    trace(TR_INT, LOG_WARNING, "bgp_read: peer %s Invalid length %d",
			  bnp->bgp_name,
			  bnp->bgp_length);
		    error = BGPERRCD_MSGLEN;
		    errptr = (u_char *) & PDU->header.length;
		    goto Error;
		}
		/* Verify version */
		if (PDU->header.version != BGP_VERSION) {
		    trace(TR_EXT, 0, "bgp_read: peer %s invalid version: %d",
			  bnp->bgp_name,
			  PDU->header.version);
		    error = BGPERRCD_VERSION;
		    errptr = &PDU->header.version;
		    goto Error;
		}
		/* Verify that packet type is valid */
		if (!PDU->header.type || (PDU->header.type > bgpPduMax)) {
		    trace(TR_EXT, 0, "bgp_read: peer %s invalid packet type: %d",
			  bnp->bgp_name,
			  PDU->header.type);
		    error = BGPERRCD_MSGTYPE;
		    errptr = (u_char *) & PDU->header.type;
		    goto Error;
		}
		if (bnp->bgp_length > sizeof(pduHeader)) {
		    bnp->bgp_length_accumulated = sizeof(pduHeader);
		    bnp->bgp_readpointer = (caddr_t) PDU + sizeof(pduHeader);
		    bnp->bgp_flags &= ~BGPF_HEADER;
		    continue;		/* Retry loop for rest of packet */
		}
	    }
	    bgp_in(tp);

	    /* Setup for header and break from loop */
	    bnp->bgp_length = 0;
	    bnp->bgp_length_accumulated = 0;
	    break;
	}
    }

    if (count < 0) {
	bgp_event_Closed(bnp);
    }
  Error:
    if (error) {
	bgp_send_Notify(bnp, error, errptr);
	bgp_state_Idle(bnp, (time_t) BGP_IDLE_FATAL);
    }
}


int
bgp_peer_changed(old, new)
bgpPeer *old, *new;
{
    int changed = FALSE;
    flag_t changed_options = old->bgp_options ^ new->bgp_options;
    flag_t new_options = new->bgp_options;

    if (changed_options &
	(BGPO_METRICOUT | BGPO_ASIN | BGPO_ASOUT | BGPO_GATEWAY | BGPO_PREFERENCE | BGPO_INTERFACE | BGPO_LINKTYPE)) {
	changed = TRUE;
    }
    if ((new_options & BGPO_METRICOUT) && (old->bgp_metricout != new->bgp_metricout)) {
	changed = TRUE;
    }
    if ((new_options & BGPO_ASIN) && (old->bgp_asin != new->bgp_asin)) {
	changed = TRUE;
    }
    if ((new_options & BGPO_ASOUT) && (old->bgp_asout != new->bgp_asout)) {
	changed = TRUE;
    }
    if ((new_options & BGPO_GATEWAY) && !equal(&old->bgp_gateway, &new->bgp_gateway)) {
	changed = TRUE;
    }
    if ((new_options & BGPO_PREFERENCE) && (old->bgp_preference != new->bgp_preference)) {
	changed = TRUE;
    }
    if ((new_options & BGPO_INTERFACE) && (old->bgp_interface != new->bgp_interface)) {
	changed = TRUE;
    }
    if (((changed_options & BGPO_LINKTYPE) || (new_options & BGPO_LINKTYPE)) && (old->bgp_linktype != new->bgp_linktype)) {
	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	(BGPO_NOGENDEFAULT|BGPO_LINKTYPE)
	old->bgp_options = (old->bgp_options & ~(FLAGS)) | (new->bgp_options & (FLAGS));
	changed_options &= ~(FLAGS);
#undef	FLAGS
    }
    return (changed);
}


void
bgp_dump(fd)
FILE *fd;
{
    bgpPeer *bnp;

    if (doing_bgp) {
	(void) fprintf(fd, "BGP status:\n");
	(void) fprintf(fd, "\tdefaultbgpmetric: %d\tpreference: %d\n",
		       bgp_default_metric,
		       bgp_preference);
	(void) fprintf(fd, "\tAutonomous System: %u\n\n",
		       my_system);
	if (bgp_accept_list) {
	    control_exterior_dump(fd, 1, control_accept_dump, bgp_accept_list);
	}
	if (bgp_propagate_list) {
	    control_exterior_dump(fd, 1, control_propagate_dump, bgp_propagate_list);
	}
	BGP_LIST(bnp) {
	    (void) fprintf(fd, "\t%-15s\n\t\tinterface: %s\tLinkType: %s\n",
			   bnp->bgp_name,
			   bnp->bgp_interface->int_name,
			   trace_state(bgpOpenType, bnp->bgp_linktype));
	    (void) fprintf(fd, "\t\tAsIn: %5u\t\tAsOut: %5u\t\tPreference: %3u\n",
			   bnp->bgp_asin,
			   bnp->bgp_asout,
			   bnp->bgp_preference);
	    (void) fprintf(fd, "\t\tState: <%s>\tFlags:<%s>\n",
			   trace_state(bgpStates, bnp->bgp_state),
			   trace_bits(bgpFlags, bnp->bgp_flags));
	    (void) fprintf(fd, "\t\tOptions: <%s>\n",
			   trace_bits(bgpOptions, bnp->bgp_options));
	    (void) fprintf(fd, "\t\tHoldTime: In: %#T  Out: %#T\n",
			   bnp->bgp_holdtime_in,
			   bnp->bgp_holdtime_out);
	    (void) fprintf(fd, "\t\tAbort at: %T\n",
			   bnp->bgp_task->task_timer[BGPTIMER_HOLDTIME]->timer_next_time);
	    if (bnp->bgp_options & BGPO_GATEWAY) {
		(void) fprintf(fd, "\t\tGateway: %A\n",
			       &bnp->bgp_gateway);
	    }
	    if (bnp->bgp_options & BGPO_METRICOUT) {
		(void) fprintf(fd, "\t\tMetricOut: %d\n",
			       bnp->bgp_metricout);
	    }
	    (void) fprintf(fd, "\n\n");
	} BGP_LISTEND;
    }
    /* Dump the AS paths */
    bgp_as_dump(fd);
}


void
bgp_trace_update(comment, update, length)
char *comment;
u_char *update;
int length;
{
    u_char *cp, *lp;
    u_char asCount;
    u_char asDirection;
    as_t asNumber;
    struct sockaddr_in gateway;
    u_short netCount;
    u_short netMetric;
    struct sockaddr_in netNetwork;

    sockclear_in(&gateway);
    sockclear_in(&netNetwork);

    cp = update;
    lp = cp + length;

    PickUp(cp, gateway.sin_addr);
    PickUp(cp, asCount);
    trace(TR_BGP, 0, "%s Gateway %A asCount %d",
	  comment,
	  &gateway,
	  asCount);
    if (!asCount || ((cp + asCount * (sizeof(asDirection) + sizeof(asNumber))) >= lp)) {
	trace(TR_BGP, 0, "invalid asCount");
	return;
    }
    for (; asCount; asCount--) {
	PickUp(cp, asDirection);
	PickUp(cp, asNumber);
	asNumber = ntohs(asNumber);
	trace(TR_BGP, 0, "%s\tAS %5d  Direction %s(%d)",
	      comment,
	      asNumber,
	      trace_state(bgpAsDirs, asDirection),
	      asDirection);
    }
    if ((cp + sizeof(gateway.sin_addr)) >= lp) {
	trace(TR_BGP, 0, "premature end of packet");
	return;
    }
    PickUp(cp, netCount);
    netCount = ntohs(netCount);
    trace(TR_BGP, 0, "%s Gateway %A  netCount %d",
	  comment,
	  &gateway,
	  netCount);
    if (!netCount || (cp + netCount * (sizeof(netMetric) + sizeof(netNetwork.sin_addr))) != lp) {
	trace(TR_BGP, 0, "invalid net Count");
	return;
    }
    for (; netCount; netCount--) {
	PickUp(cp, netNetwork.sin_addr);
	tracef("%s\tNetwork %-15A  Metric ",
	       comment,
	       &netNetwork);
	PickUp(cp, netMetric);
	netMetric = ntohs(netMetric);
	if (netMetric == bgpMetricInfinity) {
	    tracef("unreachable");
	} else {
	    tracef("%d",
		   netMetric);
	}
	trace(TR_BGP, 0, NULL);
    }
}


void
bgp_trace(src, dst, direction, PDU, length)
struct sockaddr_in *src, *dst;
const char *direction;
bgpPdu *PDU;
int length;
{
    char comment[BUFSIZ];
    const char *pduType;
    u_short notifyCode;
    u_short twobyte;
    const char *msg;

    (void) sprintf(comment, "BGP %s", direction);

    pduType = trace_state(bgpPduType, PDU->header.type);
    if (!*pduType) {
	pduType = "Invalid";
    }
    trace(TR_BGP, 0, "%s %A -> %A  length %d  version %d  type %s(%d)  HoldTime %d",
	  comment,
	  src, dst,
	  htons(PDU->header.length),
	  PDU->header.version,
	  pduType,
	  PDU->header.type,
	  htons(PDU->header.holdTime));

    switch (PDU->header.type) {
	case bgpPduOpen:
	    trace(TR_BGP, 0, "%s  as %d  linkType %s(%d)  authCode %d",
		  comment,
		  htons(PDU->pdu.open.openAs),
		  trace_state(bgpOpenType, PDU->pdu.open.openLinkType),
		  PDU->pdu.open.openLinkType,
		  PDU->pdu.open.openAuthCode);
	    break;
	case bgpPduUpdate:
	    if (trace_flags & TR_UPDATE) {
		bgp_trace_update(comment, (u_char *) PDU + sizeof(pduHeader), length - sizeof(pduHeader));
	    }
	    break;
	case bgpPduNotify:
	    notifyCode = ntohs(PDU->pdu.notify.notifyCode);
	    msg = trace_state(bgpErrors, notifyCode);
	    if (!msg) {
		msg = "Invalid notification code";
	    }
	    tracef("%s errorCode: %s(%d)",
		   comment,
		   msg,
		   notifyCode);
	    switch (notifyCode) {
		case BGPERRCD_LINKTYPE:
		    trace(TR_BGP, 0, "  correct link type should be %s(%d)",
			  trace_state(bgpOpenType, PDU->pdu.notify.notifyData[0]),
			  PDU->pdu.notify.notifyData[0]);
		    break;
		case BGPERRCD_UPDATE:
		    memcpy((caddr_t) & twobyte, (caddr_t) PDU->pdu.notify.notifyData, sizeof(twobyte));
		    twobyte = ntohs(twobyte);
		    trace(TR_BGP, 0, " updateErrorCode: %s(%d)",
			  trace_state(bgpUpdateErrors, twobyte),
			  twobyte);
		    (void) sprintf(comment, "BGP %s Error", direction);
		    bgp_trace_update(comment, (u_char *) PDU->pdu.notify.notifyPacket,
				     length - (sizeof(pduHeader) + sizeof(notifyPdu) - sizeof(PDU->pdu.notify.notifyPacket)));
		    break;
		case BGPERRCD_MSGLEN:
		    memcpy((caddr_t) & twobyte, (caddr_t) PDU->pdu.notify.notifyData, sizeof(twobyte));
		    twobyte = ntohs(twobyte);
		    trace(TR_BGP, 0, " incorrect length: %d",
			  twobyte);
		    break;
		case BGPERRCD_MSGTYPE:
		    trace(TR_BGP, 0, " incorrect type: %d",
			  PDU->pdu.notify.notifyData[0]);
		    break;
		case BGPERRCD_VERSION:
		    trace(TR_BGP, 0, " incorrect version: %d",
			  PDU->pdu.notify.notifyData[0]);
		    break;
		case BGPERRCD_AUTHCODE:
		case BGPERRCD_AUTHFAIL:
		case BGPERRCD_SYNC:
		case BGPERRCD_OPENAS:
		default:
		    trace(TR_BGP, 0, NULL);
		    break;
	    }
	    break;
	case bgpPduKeepAlive:
	    /* fall through */
	case bgpPduOpenConfirm:
	    break;
    }
    trace(TR_BGP, 0, "");
}

#endif				/* PROTO_BGP */

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