/*
 * Public Release 3
 * 
 * $Id: bgp_mib.c,v 1.3.2.2 1998/10/20 19:29:18 swright Exp $
 */

/*
 * BGP4+ alpha && IPv6 ( HITACHI_INET6 )
 * TONEv1 - advapi02 
 */

/*
 * ------------------------------------------------------------------------
 * 
 * Copyright (c) 1996, 1997, 1998 The Regents of the University of Michigan
 * All Rights Reserved
 *  
 * Royalty-free licenses to redistribute GateD Release
 * 3 in whole or in part may be obtained by writing to:
 * 
 * 	Merit GateDaemon Project
 * 	4251 Plymouth Road, Suite C
 * 	Ann Arbor, MI 48105
 *  
 * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF THE
 * UNIVERSITY OF MICHIGAN AND MERIT DO NOT WARRANT THAT THE
 * FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR
 * THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE. The Regents of the
 * University of Michigan and Merit shall not be liable for
 * any special, indirect, incidental or consequential damages with respect
 * to any claim by Licensee or any third party arising from use of the
 * software. GateDaemon was originated and developed through release 3.0
 * by Cornell University and its collaborators.
 * 
 * Please forward bug fixes, enhancements and questions to the
 * gated mailing list: gated-people@gated.merit.edu.
 * 
 * ------------------------------------------------------------------------
 * 
 * Copyright (c) 1990,1991,1992,1993,1994,1995 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.
 * 
 * GateD is based on Kirton's EGP and UC Berkeley's routing
 * daemon	 (routed).
 * Development of GateD has been supported in part by the
 * National Science Foundation.
 * 
 * ------------------------------------------------------------------------
 * 
 * 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.
 */


#define	INCLUDE_ISODE_SNMP
#define	INCLUDE_RT_VAR
#include "include.h"
#include "inet.h"
#include "bgp_proto.h"
#include "bgp.h"
#include "bgp_var.h"
#include "snmp_isode.h"

PROTOTYPE(o_bgp,
	  static int,
	  (OI,
	   struct type_SNMP_VarBind *,
	   int));
PROTOTYPE(o_bgp_peer,
	  static int,
	  (OI,
	   struct type_SNMP_VarBind *,
	   int));
PROTOTYPE(o_bgp_path,
	  static int,
	  (OI,
	   struct type_SNMP_VarBind *,
	   int));
PROTOTYPE(o_bgp4_path,
	  static int,
	  (OI,
	   struct type_SNMP_VarBind *,
	   int));

static struct object_table bgp_objects[] = {
#define	IbgpVersion			0
#define	IbgpLocalAs			1
#define	IbgpIdentifier			2
    OTE(bgpVersion, o_bgp, NULL),
    OTE(bgpLocalAs, o_bgp, NULL),
    OTE(bgpIdentifier, o_bgp, NULL),

#define	IbgpPeerIdentifier		3
#define	IbgpPeerState			4
#define	IbgpPeerAdminStatus		5
#define	IbgpPeerNegotiatedVersion	6
#define	IbgpPeerLocalAddr		7
#define	IbgpPeerLocalPort		8
#define	IbgpPeerRemoteAddr		9
#define	IbgpPeerRemotePort		10
#define	IbgpPeerRemoteAs		11
#define	IbgpPeerInUpdates		12
#define	IbgpPeerOutUpdates    		13
#define	IbgpPeerInTotalMessages		14
#define	IbgpPeerOutTotalMessages	15
#define	IbgpPeerLastError		16
#define	IbgpPeerFsmEstablishedTransitions	17
#define	IbgpPeerFsmEstablishedTime		18
#define	IbgpPeerConnectRetryInterval		19
#define	IbgpPeerHoldTime			20
#define	IbgpPeerKeepAlive			21
#define	IbgpPeerHoldTimeConfigured		22
#define	IbgpPeerKeepAliveConfigured		23
#define	IbgpPeerMinASOriginationInterval	24
#define	IbgpPeerMinRouteAdvertisementInterval	25
#define	IbgpPeerInUpdateElapsedTime		26
    OTE(bgpPeerIdentifier, o_bgp_peer, NULL),
    OTE(bgpPeerState, o_bgp_peer, NULL),
#define	PSTATE_IDLE		1
#define	PSTATE_CONNECT		2
#define	PSTATE_ACTIVE		3
#define	PSTATE_OPENSENT		4
#define	PSTATE_OPENCONFIRM	5
#define	PSTATE_ESTABLISHED	6
    OTE(bgpPeerAdminStatus, o_bgp_peer, NULL),
    OTE(bgpPeerNegotiatedVersion, o_bgp_peer, NULL),
    OTE(bgpPeerLocalAddr, o_bgp_peer, NULL),
    OTE(bgpPeerLocalPort, o_bgp_peer, NULL),
    OTE(bgpPeerRemoteAddr, o_bgp_peer, NULL),
    OTE(bgpPeerRemotePort, o_bgp_peer, NULL),
    OTE(bgpPeerRemoteAs, o_bgp_peer, NULL),
    OTE(bgpPeerInUpdates, o_bgp_peer, NULL),
    OTE(bgpPeerOutUpdates, o_bgp_peer, NULL),
    OTE(bgpPeerInTotalMessages, o_bgp_peer, NULL),
    OTE(bgpPeerOutTotalMessages, o_bgp_peer, NULL),
    OTE(bgpPeerLastError, o_bgp_peer, NULL),
    OTE(bgpPeerFsmEstablishedTransitions, o_bgp_peer, NULL),
    OTE(bgpPeerFsmEstablishedTime, o_bgp_peer, NULL),
    OTE(bgpPeerConnectRetryInterval, o_bgp_peer, NULL),
    OTE(bgpPeerHoldTime, o_bgp_peer, NULL),
    OTE(bgpPeerKeepAlive, o_bgp_peer, NULL),
    OTE(bgpPeerHoldTimeConfigured, o_bgp_peer, NULL),
    OTE(bgpPeerKeepAliveConfigured, o_bgp_peer, NULL),
    OTE(bgpPeerMinASOriginationInterval, o_bgp_peer, NULL),
    OTE(bgpPeerMinRouteAdvertisementInterval, o_bgp_peer, NULL),
    OTE(bgpPeerInUpdateElapsedTime, o_bgp_peer, NULL),

#define	IbgpPathAttrPeer			27
#define	IbgpPathAttrDestNetwork			28
#define	IbgpPathAttrOrigin			29
#define	IbgpPathAttrASPath			30
#define	IbgpPathAttrNextHop			31
#define	IbgpPathAttrInterASMetric		32
    OTE(bgpPathAttrPeer, o_bgp_path, NULL),
    OTE(bgpPathAttrDestNetwork, o_bgp_path, NULL),
    OTE(bgpPathAttrOrigin, o_bgp_path, NULL),
#define	PATH_ORIGIN_IGP		1
#define	PATH_ORIGIN_EGP		2
#define	PATH_ORIGIN_INCOMPLETE	3
    OTE(bgpPathAttrASPath, o_bgp_path, NULL),
    OTE(bgpPathAttrNextHop, o_bgp_path, NULL),
    OTE(bgpPathAttrInterASMetric, o_bgp_path, NULL),

#define	Ibgp4PathAttrPeer			33
#define	Ibgp4PathAttrIpAddrPrefixLen		34
#define	Ibgp4PathAttrIpAddrPrefix		35
#define	Ibgp4PathAttrOrigin			36
#define	Ibgp4PathAttrASPathSegment		37
#define	Ibgp4PathAttrNextHop			38
#define	Ibgp4PathAttrMultiExitDisc		39
#define	Ibgp4PathAttrLocalPref			40
#define	Ibgp4PathAttrAtomicAggregate		41
#define	Ibgp4PathAttrAggregatorAS		42
#define	Ibgp4PathAttrAggregatorAddr		43
#define	Ibgp4PathAttrCalcLocalPref		44
#define	Ibgp4PathAttrBest			45
#define	Ibgp4PathAttrUnknown			46
    OTE(bgp4PathAttrPeer, o_bgp4_path, NULL),
    OTE(bgp4PathAttrIpAddrPrefixLen, o_bgp4_path, NULL),
    OTE(bgp4PathAttrIpAddrPrefix, o_bgp4_path, NULL),
    OTE(bgp4PathAttrOrigin, o_bgp4_path, NULL),
#define	PATH4_ORIGIN_IGP		1
#define	PATH4_ORIGIN_EGP		2
#define	PATH4_ORIGIN_INCOMPLETE		3
    OTE(bgp4PathAttrASPathSegment, o_bgp4_path, NULL),
#define	PATH4_AS_SET			1
#define	PATH4_AS_SEQUENCE		2
    OTE(bgp4PathAttrNextHop, o_bgp4_path, NULL),
    OTE(bgp4PathAttrMultiExitDisc, o_bgp4_path, NULL),
#define	PATH4_NO_MED			-1
    OTE(bgp4PathAttrLocalPref, o_bgp4_path, NULL),
#define	PATH4_NO_LOCAL_PREF		-1
    OTE(bgp4PathAttrAtomicAggregate, o_bgp4_path, NULL),
#define	PATH4_LESS_SPECIFIC_ROUTE_NOT_SELECTED	1
#define	PATH4_LESS_SPECIFIC_ROUTE_SELECTED	2
    OTE(bgp4PathAttrAggregatorAS, o_bgp4_path, NULL),
#define	PATH4_NO_AGGREGATOR_AS		0
    OTE(bgp4PathAttrAggregatorAddr, o_bgp4_path, NULL),
#define	PATH4_NO_AGGREGATOR_ADDR	0
    OTE(bgp4PathAttrCalcLocalPref, o_bgp4_path, NULL),
#define	PATH4_NO_CALC_LOCAL_PREF	-1
    OTE(bgp4PathAttrBest, o_bgp4_path, NULL),
#define	PATH4_NOT_BEST_ROUTE		1
#define	PATH4_BEST_ROUTE		2
    OTE(bgp4PathAttrUnknown, o_bgp4_path, NULL),
#define	PATH4_NO_UNKNOWN_ATTR_SIZE	0

    { NULL }
};

#define	MIB_ENABLED	1
#define	MIB_DISABLED	2

static struct snmp_tree bgp_mib_tree = {
    NULL, NULL,
    "bgp",
    NULLOID,
    readWrite,
    bgp_objects,
    0
};


/*
 *	Sort list maintenance
 */

static bgpPeer *bgp_sort_list = NULL;		/* List of sorted peers, for SNMP */
static bgpPeer *bgp_mib_last_bnp;
static unsigned int *bgp_mib_last;

/*
 * bgp_sort_add - add a peer to the sort list
 */
void
bgp_sort_add __PF1(add_bnp, register bgpPeer *)
{
    register u_long rmt;

    /*
     *  If there is any cached peer, reset the cache since with the
     *  get-next operator any addition may invalidate the cache.
     */
    if (bgp_mib_last) {
        snmp_last_free(&bgp_mib_last);
        bgp_mib_last_bnp = (bgpPeer *) 0;
    }

    /*
     * Load up the local rmt address.  This is an efficiency hack, since
     * most peers will be at different addresses.
     */
    rmt = ntohl(sock2ip(add_bnp->bgp_addr));

    /*
     * See if this goes first in the list
     */
    if (bgp_sort_list == NULL
	|| BGPSORT_LT(add_bnp, rmt, bgp_sort_list)) {
	add_bnp->bgp_sort_next = bgp_sort_list;
	bgp_sort_list = add_bnp;
    } else {
        register bgpPeer *bnpprev, *bnp;

	/*
	 * Not first, run through list
	 */
	for (bnpprev = bgp_sort_list;
	     (bnp = bnpprev->bgp_sort_next) != NULL;
	     bnpprev = bnp) {
	    if (BGPSORT_LT(add_bnp, rmt, bnp)) {
		break;
	    }
	}

	/* Already on sort list */
	assert(add_bnp != bnpprev &&
	       add_bnp != bnp);

	add_bnp->bgp_sort_next = bnp;
	bnpprev->bgp_sort_next = add_bnp;
    }
}


/*
 * bgp_sort_remove - remove a peer from the sort list
 */
void
bgp_sort_remove __PF1(rem_bnp, register bgpPeer *)
{
    /* If this is the cached peer, reset the cache */
    if (rem_bnp == bgp_mib_last_bnp) {
	snmp_last_free(&bgp_mib_last);
	bgp_mib_last_bnp = (bgpPeer *) 0;
    }
    
    /*
     * See if it is first on the list
     */

    /* Sort list empty */
    assert(bgp_sort_list);

    if (bgp_sort_list == rem_bnp) {
	bgp_sort_list = rem_bnp->bgp_sort_next;
    } else {
	register bgpPeer *bnp;

	/*
	 * Not first, must be further in
	 */
	for (bnp = bgp_sort_list;
	     bnp->bgp_sort_next != NULL;
	     bnp = bnp->bgp_sort_next) {
	    if (bnp->bgp_sort_next == rem_bnp) {
		break;
	    }
	}

	/* Not on sort list */
	assert(bnp->bgp_sort_next);

	bnp->bgp_sort_next = rem_bnp->bgp_sort_next;
    }
    rem_bnp->bgp_sort_next = NULL;
}


/* Since we support multiple connections per peer but the MIB does not, */
/* we only acknowledge the first non-test peer with a given address */

#undef	BGP_SORT_LIST
#undef	BGP_SORT_LIST_END

#define	BGP_SORT_LIST(bnp) \
    for (bnp = bgp_sort_list; bnp; bnp = bnp->bgp_sort_next) { \
	if (bnp->bgp_type != BGPG_TEST)
				
#define	BGP_SORT_LIST_END(bnp) \
	while (bnp->bgp_sort_next && \
	    sockaddrcmp_in(bnp->bgp_addr, bnp->bgp_sort_next->bgp_addr)) { \
	    bnp = bnp->bgp_sort_next; \
	} \
    }

static int
o_bgp __PF3(oi, OI,
	    v, register struct type_SNMP_VarBind *,
	    offset, int)
{
    register OID    oid = oi->oi_name;
    register OT	    ot = oi->oi_type;

    switch (offset) {
    case type_SNMP_SMUX__PDUs_get__request:
	if (oid->oid_nelem != ot->ot_name->oid_nelem + 1
	    || oid->oid_elements[oid->oid_nelem - 1]) {
	    return int_SNMP_error__status_noSuchName;
	}
	break;

    case type_SNMP_SMUX__PDUs_get__next__request:
	if (oid->oid_nelem == ot->ot_name->oid_nelem) {
	    OID new;

	    if ((new = oid_extend(oid, 1)) == NULLOID) {
		return int_SNMP_error__status_genErr;
	    }
	    new->oid_elements[new->oid_nelem - 1] = 0;

	    if (v->name) {
		free_SNMP_ObjectName(v->name);
	    }
	    v->name = new;
	} else {
	    return NOTOK;
	}
	break;

    default:
	return int_SNMP_error__status_genErr;
    }

    switch (ot2object(ot)->ot_info) {
    case IbgpVersion:
        {
#define	VSTRING_LENGTH	(((BGP_BEST_VERSION - 1) / NBBY) + 1)
	    register int vers;
	    byte versions[VSTRING_LENGTH];

	    for (vers = 0; vers < sizeof versions; vers++) {
		versions[vers] = (byte) 0;
	    }
	    
	    for (vers = BGP_WORST_VERSION; vers <= BGP_BEST_VERSION; vers++) {
		if (BGP_KNOWN_VERSION(vers)) {
		    /* v is a supported version */
		    register int v1 = vers - 1;

		    versions[v1 / NBBY] |= 0x80 >> (v1 % NBBY);
		}
	    }

	    return o_string(oi, v, versions, VSTRING_LENGTH);
#undef	VSTRING_LENGTH
	}

    case IbgpLocalAs:
	return o_integer(oi, v, inet_autonomous_system);

    case IbgpIdentifier:
	return o_ipaddr(oi,
			v,
			sock2unix(inet_routerid,
				  (int *) 0));
    }

    return int_SNMP_error__status_noSuchName;
}

/**/


static bgpPeer *
o_bgp_get_peer __PF3(ip, register unsigned int *,
		     len, u_int,
		     isnext, int)
{

    if (snmp_last_match(&bgp_mib_last, ip, len, isnext)) {
	return bgp_mib_last_bnp;
    }

    if (len) {
	u_long bnp_addr;
	register bgpPeer *p;

	oid2ipaddr(ip, &bnp_addr);

	GNTOHL(bnp_addr);

	if (isnext) {
	    register bgpPeer *new = (bgpPeer *) 0;
	    register u_long new_addr = 0;

	    BGP_SORT_LIST(p) {
		register u_long cur_addr = ntohl(sock2ip(p->bgp_addr));

		if (cur_addr > bnp_addr &&
		    (!new || cur_addr < new_addr)) {
		    new = p;
		    new_addr = cur_addr;
		}
	    } BGP_SORT_LIST_END(p) ;

	    bgp_mib_last_bnp = new;
	} else {
	    bgp_mib_last_bnp = (bgpPeer *) 0;
	    

	    BGP_SORT_LIST(p) {
		register u_long cur_addr = ntohl(sock2ip(p->bgp_addr));
		
		if (cur_addr == bnp_addr) {
		    bgp_mib_last_bnp = p;
		    break;
		} else if (cur_addr > bnp_addr) {
		    break;
		}
	    } BGP_SORT_LIST_END(p) ;
	}
    } else {
	bgp_mib_last_bnp = bgp_n_peers ? bgp_sort_list : (bgpPeer *) 0;
    }

    return bgp_mib_last_bnp;
}


static int
o_bgp_peer_info __PF4(oi, OI,
		      v, register struct type_SNMP_VarBind *,
		      ifvar, int,
		      vp, void_t)
{
    register bgpPeer *bnp = (bgpPeer *) vp;

    switch (ifvar) {
    case IbgpPeerIdentifier:
	return o_ipaddr(oi,
			v,
			sock2unix(sockbuild_in(0, bnp->bgp_id),
				  (int *) 0));
	
    case IbgpPeerState:
    {
	int state;

	switch (bnp->bgp_state) {
	case BGPSTATE_IDLE:
	    state = PSTATE_IDLE;
	    break;
		
	case BGPSTATE_CONNECT:
	    state = PSTATE_CONNECT;
	    break;
		
	case BGPSTATE_ACTIVE:
	    state = PSTATE_ACTIVE;
	    break;
		
	case BGPSTATE_OPENSENT:
	    state = PSTATE_OPENSENT;
	    break;
		
	case BGPSTATE_OPENCONFIRM:
	    state = PSTATE_OPENCONFIRM;
	    break;
		
	case BGPSTATE_ESTABLISHED:
	    state = PSTATE_ESTABLISHED;
	    break;

	default:
	    state = -1;
	}

	return o_integer(oi, v, state);
    }
	
    case IbgpPeerAdminStatus:
	return o_integer(oi, v, bnp->bgp_state == BGPSTATE_ESTABLISHED ? MIB_DISABLED : MIB_ENABLED);
	
    case IbgpPeerNegotiatedVersion:
	return o_integer(oi, v, bnp->bgp_version);

    case IbgpPeerLocalAddr:
	return o_ipaddr(oi,
			v,
			sock2unix(bnp->bgp_myaddr ? bnp->bgp_myaddr : inet_addr_default,
				  (int *) 0));

    case IbgpPeerLocalPort:
	return o_integer(oi, v, bnp->bgp_myaddr ? ntohs(sock2port(bnp->bgp_myaddr)) : 0);

    case IbgpPeerRemoteAddr:
	return o_ipaddr(oi,
			v,
			sock2unix(bnp->bgp_addr,
				  (int *) 0));

    case IbgpPeerRemotePort:
	return o_integer(oi, v, ntohs(sock2port(bnp->bgp_addr)));

    case IbgpPeerRemoteAs:
	return o_integer(oi, v, bnp->bgp_peer_as);
	
    case IbgpPeerInUpdates:
	return o_integer(oi, v, bnp->bgp_in_updates);
	
    case IbgpPeerOutUpdates:
	return o_integer(oi, v, bnp->bgp_out_updates);
	
    case IbgpPeerInTotalMessages:
	return o_integer(oi, v, bnp->bgp_in_updates + bnp->bgp_in_notupdates);
	
    case IbgpPeerOutTotalMessages:
	return o_integer(oi, v, bnp->bgp_out_updates + bnp->bgp_out_notupdates);
	
    case IbgpPeerLastError:
	return o_string(oi, v, &bnp->bgp_lasterror, sizeof bnp->bgp_lasterror);

    case IbgpPeerFsmEstablishedTransitions:
	return o_integer(oi, v, bnp->bgp_EstablishedTransitions);

    case IbgpPeerFsmEstablishedTime:
	return o_integer(oi, 
			 v, 
			 bnp->bgp_EstablishedTime ? (bgp_time_sec - bnp->bgp_EstablishedTime) : 0);

    case IbgpPeerConnectRetryInterval:
	return o_integer(oi, v, BGPCONN_INTERVAL(bnp));

    case IbgpPeerHoldTime:
	return o_integer(oi, v, bnp->bgp_holdtime);

    case IbgpPeerKeepAlive:
	return o_integer(oi, v, (bnp->bgp_holdtime / 3));

    case IbgpPeerHoldTimeConfigured:
	return o_integer(oi, v, bnp->bgp_holdtime_out);

    case IbgpPeerKeepAliveConfigured:
	return o_integer(oi, v, (bnp->bgp_holdtime_out / 3));

    case IbgpPeerMinASOriginationInterval:
	return o_integer(oi, 
			 v,
			 (bnp->bgp_group->bgpg_type == BGPG_EXTERNAL) ? bnp->bgp_rto_time : 0);

    case IbgpPeerMinRouteAdvertisementInterval:
	return o_integer(oi, 
			 v,
			 (bnp->bgp_group->bgpg_type != BGPG_EXTERNAL) ? bnp->bgp_rto_time : 0);

    case IbgpPeerInUpdateElapsedTime:
	return o_integer(oi, 
			 v, 
			 bnp->bgp_InUpdateTime ? (bgp_time_sec - bnp->bgp_InUpdateTime) : 0);

    }

    return int_SNMP_error__status_noSuchName;
}


static int
o_bgp_peer __PF3(oi, OI,
		 v, register struct type_SNMP_VarBind *,
		 offset, int)
{
    register int    i;
    register unsigned int *ip,
			  *jp;
    register bgpPeer *bnp;
    register OID    oid = oi->oi_name;
    register OT	    ot = oi->oi_type;
    OID		    new;

    /* INDEX { bgpPeerRemoteAddr } */
#define	NDX_SIZE	(sizeof (struct in_addr))

    switch (offset) {
    case type_SNMP_SMUX__PDUs_get__request:
	if (oid->oid_nelem != ot->ot_name->oid_nelem + NDX_SIZE) {
		return int_SNMP_error__status_noSuchName;
	    }
	bnp = o_bgp_get_peer(oid->oid_elements + oid->oid_nelem - NDX_SIZE,
			     sizeof (struct in_addr),
			     0);
	if (!bnp) {
	    return int_SNMP_error__status_noSuchName;
	}
	break;

    case type_SNMP_SMUX__PDUs_get__next__request:
	/* next request with incomplete instance? */
	if ((i = oid->oid_nelem - ot->ot_name->oid_nelem) != 0 && i < NDX_SIZE) {
	    for (jp = (ip = oid->oid_elements + 
		       ot->ot_name->oid_nelem - 1) + i;
		 jp > ip;
		 jp--) {
		if (*jp != 0) {
		    break;
		}
	    }
	    if (jp == ip) {
		oid->oid_nelem = ot->ot_name->oid_nelem;
	    } else {
		if ((new = oid_normalize(oid, NDX_SIZE - i, 256)) == NULLOID) {
			return NOTOK;
		    }
		if (v->name) {
		    free_SNMP_ObjectName(v->name);
		}
		v->name = oid = new;
	    }
	}

	/* next request with no instance? */
	if (oid->oid_nelem == ot->ot_name->oid_nelem) {
	    bnp = o_bgp_get_peer((unsigned int *) 0,
				 0,
				 TRUE);
	    if (!bnp) {
		return NOTOK;
	    }

	    if ((new = oid_extend(oid, NDX_SIZE)) == NULLOID) {
		return int_SNMP_error__status_genErr;
	    }

	    ip = new->oid_elements + new->oid_nelem - NDX_SIZE;
	    STR_OID(ip, &sock2ip(bnp->bgp_addr), sizeof (sock2ip(bnp->bgp_addr)));
		
	    if (v->name) {
		free_SNMP_ObjectName(v->name);
	    }
	    v->name = new;
	} else {
	    int j;

	    bnp = o_bgp_get_peer(ip = oid->oid_elements + ot->ot_name->oid_nelem,
				 (u_int) (j = oid->oid_nelem - ot->ot_name->oid_nelem),
				 TRUE);
	    if (!bnp) {
		return NOTOK;
	    }

	    if ((i = j - NDX_SIZE) < 0) {
		if ((new = oid_extend(oid, -i)) == NULLOID) {
		    return int_SNMP_error__status_genErr;
		}
		if (v->name) {
		    free_SNMP_ObjectName(v->name);
		}
		v->name = oid = new;
	    } else if (i > 0) {
		oid->oid_nelem -= i;
	    }
		
	    ip = oid->oid_elements + ot->ot_name->oid_nelem;
	    STR_OID(ip, &sock2ip(bnp->bgp_addr), sizeof (sock2ip(bnp->bgp_addr)));
	}
	break;

    default:
	return int_SNMP_error__status_genErr;
    }
#undef	NDX_SIZE

    return o_bgp_peer_info(oi, v, ot2object(ot)->ot_info, (void_t) bnp);
}


static rt_entry *
o_bgp_get_match __PF2(rth, register rt_head *,
		      data, void_t)
{
    register bgpPeer *p;
    register u_long this_peer;

    if (data) {
	this_peer = *((u_long *)data);
    } else {
	this_peer = 0;
    }
 
    BGP_SORT_LIST(p) {
	if (!this_peer || ntohl(sock2ip(p->bgp_addr)) > this_peer) {
	    register rt_entry *rt;
	    register gw_entry *gwp = &p->bgp_gw;

	    RT_ALLRT(rt, rth) {
		if (BIT_TEST(rt->rt_state, RTS_DELETE)) {
		    break;
		}
		if (rt->rt_gwp == gwp) {
		    return rt;
		}
	    } RT_ALLRT_END(rt, rth) ;
	}
    } BGP_SORT_LIST_END(p) ;

    return (rt_entry *) 0;
}

static rt_entry *
o_bgp_get_match_exact __PF2(rth, register rt_head *,
			    data, void_t)
{
    register rt_entry *rt;
    register gw_entry *gwp = (gw_entry *)(data);

    RT_ALLRT(rt, rth) {
	if (BIT_TEST(rt->rt_state, RTS_DELETE)) {
	    break;
	}
	if (rt->rt_gwp == gwp) {
	    return rt;
	}
    } RT_ALLRT_END(rt, rth);

    return (rt_entry *) 0;
}


static rt_entry *
o_bgp_get_path __PF3(ip, register unsigned int *,
		     len, u_int,
		     isnext, int)    
{
    static rt_entry *last_rt = (rt_entry *) 0;
    static unsigned int *last = (unsigned int *) 0;
    static int last_quantum = 0;

    if (last_quantum != snmp_quantum) {
	last_quantum = snmp_quantum;

	if (last) {
	    task_mem_free((task *) 0, (caddr_t) last);
	    last = (unsigned int *) 0;
	}
    }
    if (snmp_last_match(&last, ip, len, isnext)) {
	return last_rt;
    }

    if (len) {
	u_long peer;
	sockaddr_un *dest;
	register bgpPeer *p;

	dest = sockbuild_in(0, 0);
	oid2ipaddr(ip, &sock2ip(dest));

	oid2ipaddr(&ip[sizeof (struct in_addr)], &peer);

	if (isnext) {
	    /* Find the next peer in the next valid route */

	    GNTOHL(peer);

	    /* First look up this route and see if there is a next peer */
	    last_rt = rt_table_get(dest,
				   (sockaddr_un *) 0,
				   o_bgp_get_match,
				   (void_t)(&peer));

	    /* That didn't work, find the first peer in the next route */
	    if (!last_rt) {
		last_rt = rt_table_getnext(dest,
					   (sockaddr_un *) 0,
					   AF_INET,
					   o_bgp_get_match,
					   (void_t) 0);
	    }
	} else {
	    /* Find the route with this peer */

	    /* First find the peer */
	    BGP_SORT_LIST(p) {
		if (peer == sock2ip(p->bgp_addr)) {

		    /* Now look up the route */
		    last_rt = rt_table_get(dest,
					   (sockaddr_un *) 0,
					   o_bgp_get_match_exact,
					   (void_t)&(p->bgp_gw));
		    break;
		}
	    } BGP_SORT_LIST_END(p) ;
	}
    } else {
	/* Find the first peer of the first route */

	last_rt = rt_table_getnext((sockaddr_un *) 0,
				   (sockaddr_un *) 0,
				   AF_INET,
				   o_bgp_get_match,
				   (void_t) 0);
    }

    return last_rt;
}


static int
o_bgp_path __PF3(oi, OI,
		 v, register struct type_SNMP_VarBind *,
		 offset, int)
{
    register int    i;
    register unsigned int *ip,
			  *jp;
    register OID    oid = oi->oi_name;
    register OT	    ot = oi->oi_type;
    OID		    new;
    register rt_entry *rt;
    bgpPeer *bnp;

    /* INDEX { bgpPathAttrDestNetwork, bgpPathAttrPeer } */
#define	NDX_SIZE	(sizeof (struct in_addr) + sizeof (struct in_addr))
    
    switch (offset) {
    case type_SNMP_SMUX__PDUs_get__request:
	if (oid->oid_nelem - ot->ot_name->oid_nelem != NDX_SIZE) {
	    return int_SNMP_error__status_noSuchName;
	}
	rt = o_bgp_get_path(oid->oid_elements + oid->oid_nelem - NDX_SIZE,
			    NDX_SIZE,
			    0);
	if (!rt) {
	    return int_SNMP_error__status_noSuchName;
	}
	bnp = (bgpPeer *) rt->rt_gwp->gw_task->task_data;
	break;

    case type_SNMP_SMUX__PDUs_get__next__request:
	/* next request with incomplete instance? */
	if ((i = oid->oid_nelem - ot->ot_name->oid_nelem) != 0 && i < NDX_SIZE) {
	    for (jp = (ip = oid->oid_elements + 
		       ot->ot_name->oid_nelem - 1) + i;
		 jp > ip;
		 jp--) {
		if (*jp != 0) {
		    break;
		}
	    }
	    if (jp == ip) {
		oid->oid_nelem = ot->ot_name->oid_nelem;
	    } else {
		if ((new = oid_normalize(oid, NDX_SIZE - i, 256)) == NULLOID) {
			return NOTOK;
		    }
		if (v->name) {
		    free_SNMP_ObjectName(v->name);
		}
		v->name = oid = new;
	    }
	}

	/* Next request with no instance? */
	if (oid->oid_nelem == ot->ot_name->oid_nelem) {
	    rt = o_bgp_get_path((unsigned int *) 0,
				0,
				TRUE);
	    if (!rt) {
		return NOTOK;
	    }
	    bnp = (bgpPeer *) rt->rt_gwp->gw_task->task_data;
	    
	    if ((new = oid_extend(oid, NDX_SIZE)) == NULLOID) {
		return int_SNMP_error__status_genErr;
	    }

	    ip = new->oid_elements + new->oid_nelem - NDX_SIZE;
	    STR_OID(ip, &sock2ip(rt->rt_dest), sizeof (sock2ip(rt->rt_dest)));
	    STR_OID(ip, &sock2ip(bnp->bgp_addr), sizeof (sock2ip(bnp->bgp_addr)));
		
	    if (v->name) {
		free_SNMP_ObjectName(v->name);
	    }
	    v->name = new;
	} else {
	    int j;

	    rt = o_bgp_get_path(ip = oid->oid_elements + ot->ot_name->oid_nelem,
				(u_int) (j = oid->oid_nelem - ot->ot_name->oid_nelem),
				TRUE);
	    if (!rt) {
		return NOTOK;
	    }
	    bnp = (bgpPeer *) rt->rt_gwp->gw_task->task_data;

	    if ((i = j - NDX_SIZE) < 0) {
		if ((new = oid_extend(oid, -i)) == NULLOID) {
		    return int_SNMP_error__status_genErr;
		}
		if (v->name) {
		    free_SNMP_ObjectName(v->name);
		}
		v->name = oid = new;
	    } else if (i > 0) {
		oid->oid_nelem -= i;
	    }
		
	    ip = oid->oid_elements + ot->ot_name->oid_nelem;
	    STR_OID(ip, &sock2ip(rt->rt_dest), sizeof (sock2ip(rt->rt_dest)));
	    STR_OID(ip, &sock2ip(bnp->bgp_addr), sizeof (sock2ip(bnp->bgp_addr)));
	}
	break;

    default:
	return int_SNMP_error__status_genErr;
    }
#undef	NDX_SIZE

    switch (ot2object(ot)->ot_info) {
    case IbgpPathAttrPeer:
	return o_ipaddr(oi,
			v,
			sock2unix(bnp->bgp_addr,
				  (int *) 0));

    case IbgpPathAttrDestNetwork:
	return o_ipaddr(oi,
			v,
			sock2unix(rt->rt_dest,
				  (int *) 0));
    
    case IbgpPathAttrOrigin:
        {
	    int origin;

	    switch (rt->rt_aspath->path_origin) {
	    case PATH_ORG_IGP:
		origin = PATH_ORIGIN_IGP;
		break;

	    case PATH_ORG_EGP:
		origin = PATH_ORIGIN_EGP;
		break;

	    case PATH_ORG_XX:
		origin = PATH_ORIGIN_INCOMPLETE;
		break;

	    default:
		origin = -1;
	    }
	    
	    return o_integer(oi, v, origin);
	}

    case IbgpPathAttrASPath:
	/* The path is stored in network byte order and that is basically how the MIB wants to see it */
	/* XXX - what about a zero length AS path? */
	return o_string(oi, v, PATH_PTR(rt->rt_aspath), rt->rt_aspath->path_len);

    case IbgpPathAttrNextHop:
	return o_ipaddr(oi,
			v,
			sock2unix(RT_ROUTER(rt),
				  (int *) 0));
	
    case IbgpPathAttrInterASMetric:
	return o_integer(oi, v, rt->rt_metric);
    }

    return int_SNMP_error__status_noSuchName;
}

static rt_entry *
o_bgp4_get_path __PF3(ip, register unsigned int *,
		      len, u_int,
		      isnext, int)    
{
    static rt_entry *last_rt = (rt_entry *) 0;
    static unsigned int *last = (unsigned int *) 0;
    static int last_quantum = 0;

    if (last_quantum != snmp_quantum) {
	last_quantum = snmp_quantum;

	if (last) {
	    task_mem_free((task *) 0, (caddr_t) last);
	    last = (unsigned int *) 0;
	}
    }
    if (snmp_last_match(&last, ip, len, isnext)) {
	return last_rt;
    }

    if (len) {
	u_long peer;
	u_int  prefix_length;
	sockaddr_un *dest, *mask;
	register bgpPeer *p;

	/* Set bgp4PathAttrTable index variables from the ANS.1 string. */
	dest = sockbuild_in(0, 0);
	oid2ipaddr(ip, &sock2ip(dest));

	ip += sizeof (struct in_addr);
	prefix_length = *ip++;
	mask = inet_masks[prefix_length];

	oid2ipaddr(ip, &peer);


	if (isnext) {
	    /* Find the next peer in the next valid route */

	    GNTOHL(peer);

	    /* First look up this route and see if there is a next peer */
	    last_rt = rt_table_get(dest,
				   mask,
				   o_bgp_get_match,
				   (void_t)(&peer));

	    /* That didn't work, find the first peer in the next route */
	    if (!last_rt) {
		last_rt = rt_table_getnext(dest,
					   mask,
					   AF_INET,
					   o_bgp_get_match,
					   (void_t) 0);
	    }
	} else {
	    /* Find the route with this peer */

	    /* First find the peer */
	    BGP_SORT_LIST(p) {
		if (peer == sock2ip(p->bgp_addr)) {

		    /* Now look up the route */
		    last_rt = rt_table_get(dest,
					   mask,
					   o_bgp_get_match_exact,
					   (void_t)&(p->bgp_gw));
		    break;
		}
	    } BGP_SORT_LIST_END(p) ;
	}
    } else {
	/* Find the first peer of the first route */

	last_rt = rt_table_getnext((sockaddr_un *) 0,
				   (sockaddr_un *) 0,
				   AF_INET,
				   o_bgp_get_match,
				   (void_t) 0);
    }

    return last_rt;
}

#define HIGH_ORDER_BIT	0x80000000
#define PREFIX_LENGTH(mask_addr, length) \
        do { \
	   register u_int Xmask = htonl(sock2ip((mask_addr))); \
	   (length) = 0; \
	   while ((Xmask << (length)) & HIGH_ORDER_BIT) (length)++; \
	} while (0)    
#define LONGEST_FORMATTED_BGP4_ASPATH	1024
static char bgp4_ASPathSegment[LONGEST_FORMATTED_BGP4_ASPATH];

/*
 * Format a parsed BGP 4 AS path into a of list path segments with each represented
 * by the triple <type, length, value>. Technique borrowed from aspath_dump_all.
 */
static int
o_bgp4_format_ASPathSegment __PF1(bgp4_aspath, struct _as_path *)
{
    register char *cp = bgp4_ASPathSegment;
    register char *aspath = PATH_PTR(bgp4_aspath);
    register u_short *segment = PATH_SEG_PTR(bgp4_aspath);
    register int number_segments = bgp4_aspath->path_seg_len / PA4_LEN_SEGMENT;
    register int number_ASs;
    register int bgp4_aspath_len;
    while (number_segments-- > 0) {
	number_ASs = PATH_SEG_LEN(*segment);
	if (PATH_ISSET(*segment)) {
	    *cp++ = PATH4_AS_SET;
	} else {
	    *cp++ = PATH4_AS_SEQUENCE;
	}
	*cp++ = (u_char) number_ASs; 
	while (number_ASs-- > 0) {
	    *cp++ = *aspath++;
	    *cp++ = *aspath++;
	}
	segment++;
    }
    bgp4_aspath_len = cp - bgp4_ASPathSegment;
    assert(bgp4_aspath_len <= LONGEST_FORMATTED_BGP4_ASPATH);
    return bgp4_aspath_len;
}

static int
o_bgp4_path __PF3(oi, OI,
		  v, register struct type_SNMP_VarBind *,
		  offset, int)
{
    register int    i;
    register unsigned int *ip,
			  *jp;
    register OID    oid = oi->oi_name;
    register OT	    ot = oi->oi_type;
    OID		    new;
    register rt_entry *rt;
    bgpPeer *bnp;
    register u_int prefix_length = 0;

    /* INDEX { bgp4PathAttrPrefix, bgp4PathAttrPrefixLen, bgp4PathAttrPeer } */
#define	NDX_SIZE	(sizeof (struct in_addr) + 1 + sizeof (struct in_addr))
    
    switch (offset) {
    case type_SNMP_SMUX__PDUs_get__request:
	if (oid->oid_nelem - ot->ot_name->oid_nelem != NDX_SIZE) {
	    return int_SNMP_error__status_noSuchName;
	}
	rt = o_bgp4_get_path(oid->oid_elements + oid->oid_nelem - NDX_SIZE,
			     NDX_SIZE,
			     0);
	if (!rt) {
	    return int_SNMP_error__status_noSuchName;
	}
	bnp = (bgpPeer *) rt->rt_gwp->gw_task->task_data;
	break;

    case type_SNMP_SMUX__PDUs_get__next__request:
	/* next request with incomplete instance? */
	if ((i = oid->oid_nelem - ot->ot_name->oid_nelem) != 0 && i < NDX_SIZE) {
	    for (jp = (ip = oid->oid_elements + 
		       ot->ot_name->oid_nelem - 1) + i;
		 jp > ip;
		 jp--) {
		if (*jp != 0) {
		    break;
		}
	    }
	    if (jp == ip) {
		oid->oid_nelem = ot->ot_name->oid_nelem;
	    } else {
		if ((new = oid_normalize(oid, NDX_SIZE - i, 256)) == NULLOID) {
			return NOTOK;
		    }
		if (v->name) {
		    free_SNMP_ObjectName(v->name);
		}
		v->name = oid = new;
	    }
	}

	/* Next request with no instance? */
	if (oid->oid_nelem == ot->ot_name->oid_nelem) {
	    rt = o_bgp4_get_path((unsigned int *) 0,
				0,
				TRUE);
	    if (!rt) {
		return NOTOK;
	    }
	    bnp = (bgpPeer *) rt->rt_gwp->gw_task->task_data;
	    
	    if ((new = oid_extend(oid, NDX_SIZE)) == NULLOID) {
		return int_SNMP_error__status_genErr;
	    }

	    ip = new->oid_elements + new->oid_nelem - NDX_SIZE;
	    STR_OID(ip, &sock2ip(rt->rt_dest), sizeof (sock2ip(rt->rt_dest)));
	    PREFIX_LENGTH(rt->rt_dest_mask, prefix_length);
	    INT_OID(ip, prefix_length);
	    STR_OID(ip, &sock2ip(bnp->bgp_addr), sizeof (sock2ip(bnp->bgp_addr)));
		
	    if (v->name) {
		free_SNMP_ObjectName(v->name);
	    }
	    v->name = new;
	} else {
	    int j;

	    rt = o_bgp4_get_path(ip = oid->oid_elements + ot->ot_name->oid_nelem,
				(u_int) (j = oid->oid_nelem - ot->ot_name->oid_nelem),
				TRUE);
	    if (!rt) {
		return NOTOK;
	    }
	    bnp = (bgpPeer *) rt->rt_gwp->gw_task->task_data;

	    if ((i = j - NDX_SIZE) < 0) {
		if ((new = oid_extend(oid, -i)) == NULLOID) {
		    return int_SNMP_error__status_genErr;
		}
		if (v->name) {
		    free_SNMP_ObjectName(v->name);
		}
		v->name = oid = new;
	    } else if (i > 0) {
		oid->oid_nelem -= i;
	    }
		
	    ip = oid->oid_elements + ot->ot_name->oid_nelem;
	    STR_OID(ip, &sock2ip(rt->rt_dest), sizeof (sock2ip(rt->rt_dest)));
	    PREFIX_LENGTH(rt->rt_dest_mask, prefix_length);
	    INT_OID(ip, prefix_length);
	    STR_OID(ip, &sock2ip(bnp->bgp_addr), sizeof (sock2ip(bnp->bgp_addr)));
	}
	break;

    default:
	return int_SNMP_error__status_genErr;
    }
#undef	NDX_SIZE

    switch (ot2object(ot)->ot_info) {
    case Ibgp4PathAttrPeer:
	return o_ipaddr(oi,
			v,
			sock2unix(bnp->bgp_addr,
				  (int *) 0));

    case Ibgp4PathAttrIpAddrPrefixLen:
	if (offset == type_SNMP_SMUX__PDUs_get__request) {
	    PREFIX_LENGTH(rt->rt_dest_mask, prefix_length);
	}
	return o_integer(oi,
			 v,
			 prefix_length);

    case Ibgp4PathAttrIpAddrPrefix:
	return o_ipaddr(oi,
			v,
			sock2unix(rt->rt_dest,
				  (int *) 0));
    
    case Ibgp4PathAttrOrigin:
        {
	    int origin;

	    switch (rt->rt_aspath->path_origin) {
	    case PATH_ORG_IGP:
		origin = PATH4_ORIGIN_IGP;
		break;

	    case PATH_ORG_EGP:
		origin = PATH4_ORIGIN_EGP;
		break;

	    case PATH_ORG_XX:
		origin = PATH4_ORIGIN_INCOMPLETE;
		break;

	    default:
		origin = -1;
	    }
	    
	    return o_integer(oi, v, origin);
	}

    case Ibgp4PathAttrASPathSegment:
	/* 
	 * This is inefficient, but doing it otherwise would require saving the 
	 * unparsed AS Path as well as the parsed version. 
	 */
       {
	   u_int aspath_len;
	   aspath_len = o_bgp4_format_ASPathSegment(rt->rt_aspath);
	   return o_string(oi, v, bgp4_ASPathSegment, aspath_len);
        }

    case Ibgp4PathAttrNextHop:
	return o_ipaddr(oi,
			v,
			sock2unix(RT_ROUTER(rt),
				  (int *) 0));
	
    case Ibgp4PathAttrMultiExitDisc:
	/* If none, the route should have been installed with metric of -1. */
	return o_integer(oi, v, rt->rt_metric);

    case Ibgp4PathAttrLocalPref:
	/* If none, the route should have been installed with metric of -1. */
	return o_integer(oi, v, rt->rt_metric2);

    case Ibgp4PathAttrAtomicAggregate:
        {
	    u_int AtomicAggregate;
	    if (BIT_TEST(rt->rt_aspath->path_flags, PATH_FLAG_ATOMIC_AGG)) {
		AtomicAggregate = PATH4_LESS_SPECIFIC_ROUTE_SELECTED;
	    } else {
		AtomicAggregate = PATH4_LESS_SPECIFIC_ROUTE_NOT_SELECTED;
	    }
	    return o_integer(oi, v, AtomicAggregate);
	 }    

    case Ibgp4PathAttrAggregatorAS:
        {
	    as_t as;
	    if (rt->rt_aspath->path_aggr_len != 0) {
		byte *bp = PATH_ATTR_PTR(rt->rt_aspath) + 3;
		PATH_GET_AS(as, bp);
	    } else {
		as = PATH4_NO_AGGREGATOR_AS;
	    }
	    return o_integer(oi, v, as);
	 }   

    case Ibgp4PathAttrAggregatorAddr:
       {
	   static struct sockaddr_in AggregatorAddr = {sizeof(struct sockaddr_in), 
							   AF_INET,
							   0,
							   0};
	
	   if (rt->rt_aspath->path_aggr_len != 0
	       && PATH_ATTR_LEN(6) == rt->rt_aspath->path_aggr_len) {
	       byte *bp = PATH_ATTR_PTR(rt->rt_aspath) + 3 + sizeof(as_t);
	       byte *ap = (byte *) &AggregatorAddr.sin_addr.s_addr;
	       *ap++ = *bp++;
	       *ap++ = *bp++;
	       *ap++ = *bp++;
	       *ap++ = *bp++;	
	   } else {
	       AggregatorAddr.sin_addr.s_addr = PATH4_NO_AGGREGATOR_ADDR;
	   }
	   return o_ipaddr(oi, v, &AggregatorAddr);
       }

    case Ibgp4PathAttrCalcLocalPref:
	return o_integer(oi, 
			 v, 
			 (254 - abs(rt->rt_preference)));

    case Ibgp4PathAttrBest:
        {
	    u_int best_route;
	    if (rt == rt->rt_active) {
		best_route = PATH4_BEST_ROUTE;
	    } else {
		best_route = PATH4_NOT_BEST_ROUTE;
	    }
	    return o_integer(oi, v, best_route);
	}    

    case Ibgp4PathAttrUnknown:
        {
	    register size_t attr_len;
	    
	    attr_len = (size_t)(rt->rt_aspath->path_attr_len - rt->rt_aspath->path_aggr_len);
	    if (attr_len) {
		register byte *attr_cp;
		attr_cp = PATH_ATTR_PTR(rt->rt_aspath) + rt->rt_aspath->path_aggr_len;
		return o_string(oi, v, attr_cp, attr_len);
	    } else {
		return o_string(oi, v, (caddr_t) 0, 0);
	    }
        }
    }
    return int_SNMP_error__status_noSuchName;
}

/**/
/* Traps */

#define	bgpEstablished		1
#define	bgpBackwardTransition	2

#define	TRAP_NUM_VARS	3

static int bgp_trap_varlist[TRAP_NUM_VARS] = {
    IbgpPeerRemoteAddr,
    IbgpPeerLastError,
    IbgpPeerState
};


void
bgp_trap_established __PF1(bnp, bgpPeer *)
{
    struct type_SNMP_VarBindList bindlist[TRAP_NUM_VARS];
    struct type_SNMP_VarBind var[TRAP_NUM_VARS];

    if (snmp_varbinds_build(TRAP_NUM_VARS,
			    bindlist,
			    var,
			    bgp_trap_varlist,
			    &bgp_mib_tree,
			    o_bgp_peer_info,
			    (void_t) bnp) == NOTOK) {
	trace_log_tf(snmp_trace_options,
		     0,
		     LOG_ERR,
		     ("bgp_trap_established: snmp_varbinds_build failed"));
	goto out;
    }

    snmp_trap("bgpEstablished",
	      bgp_mib_tree.r_name,
	      int_SNMP_generic__trap_enterpriseSpecific,
	      bgpEstablished,
	      bindlist);

out:;
    snmp_varbinds_free(bindlist);
}


void
bgp_trap_backward __PF1(bnp, bgpPeer *)
{
    struct type_SNMP_VarBindList bindlist[TRAP_NUM_VARS];
    struct type_SNMP_VarBind var[TRAP_NUM_VARS];

    if (snmp_varbinds_build(TRAP_NUM_VARS,
			    bindlist,
			    var,
			    bgp_trap_varlist,
			    &bgp_mib_tree,
			    o_bgp_peer_info,
			    (void_t) bnp) == NOTOK) {
	trace_log_tf(snmp_trace_options,
		     0,
		     LOG_ERR,
		     ("bgp_trap_backward: snmp_varbinds_build failed"));
	goto out;
    }

    snmp_trap("bgpBackwardTransition",
	      bgp_mib_tree.r_name,
	      int_SNMP_generic__trap_enterpriseSpecific,
	      bgpBackwardTransition,
	      bindlist);

out:;
    snmp_varbinds_free(bindlist);
}

/**/

void
bgp_init_mib __PF1(enabled, int)
{
    if (enabled) {
	snmp_tree_register(&bgp_mib_tree);
    } else {
	snmp_tree_unregister(&bgp_mib_tree);
    }
}
