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.