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.