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

/*
 * ------------------------------------------------------------------------
 * 
 * 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.
 * ------------------------------------------------------------------------
 * 
 * 	Copyright 1991 D.L.S. Associates
 * 
 * 	Permission to use, copy, modify, distribute, and sell this software
 * 	and its documentation for any purpose is hereby granted without
 * 	fee, provided that the above copyright notice appear in all copies
 * 	and that both that copyright notice and this permission notice
 * 	appear in supporting documentation, and that the name of D.L.S. not
 * 	be used in advertising or publicity pertaining to distribution of
 * 	the software without specific, written prior permission.  D.L.S.
 * 	makes no representations about the suitability of this software for
 * 	any purpose.  It is provided "as is" without express or implied
 * 	warranty.
 * 
 * 	D.L.S. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * 	INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 * 	NO EVENT SHALL D.L.S.  BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * 	CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * 	OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * 	NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * 	CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * 	Authors:  Robert Hagens and Dan Schuh
 * 
 * 
 */


#include "include.h"
#include "isis_includes.h"

#ifdef	PROTO_SNMP
#include "isis_mib.h"				/* Isis-Mib */
#endif

void
adjInitFailureEvent __PF5(circuit, CircuitEntry *,
			  adj, AdjacencyEntry *,
			  reason, ReasonCode,
			  id, SystemID6 *,		/* list of IDs of failing system */
			  numIds, int)			/* number in list */
{
	trace_log_tf(isis_trace_options,
		     0,
		     LOG_ERR,
		     ("%s: IS-IS adj init failure: reason %s",
		      circuit->name,
		      reasonCodeToStr(reason)));
        circuit->initFailures++;
#ifdef	PROTO_SNMP
	{
	    IsisMibEntry	*pMibEntry;
	    IsisCircEntry	*pCircEntry;

	    pMibEntry = (IsisMibEntry *)locateEntry(&isisMibTable, 1);
	    pCircEntry = (IsisCircEntry *)locateEntry(&pMibEntry->isisCircTable,
								circuit->localId);
	    if (pCircEntry) {
	        ++pCircEntry->isisCircInitFails;		/* Isis-Mib */
	    }
	    else {
		trace_log_tf(isis_trace_options,
			     0,
			     LOG_ERR,
			     ("adjInitFailureEvent(): cannot locate circ mib entry for localId==%d",
			      circuit->localId));
	    }
	}
#endif	/* PROTO_SNMP */

}

void
adjUpEvent __PF2(circuit, CircuitEntry *,
		 adj, AdjacencyEntry *)
{
	logAdjEvent(circuit, adj, "IS-IS adj up");
	lspAdjUp(circuit, adj);
#ifdef	PROTO_SNMP
	{
    	    IsisMibEntry	*pMibEntry;
    	    IsisCircEntry	*pCircEntry;
	    IsisISAdjEntry	*pISAdjEntry;

    	    pMibEntry = (IsisMibEntry *)locateEntry(&isisMibTable, 1);
    	    pCircEntry = (IsisCircEntry *)locateEntry(&pMibEntry->isisCircTable,
							       circuit->localId);
    	    if (pCircEntry) {
		pISAdjEntry = (IsisISAdjEntry *)locateEntry(&pCircEntry->isisISAdjTable, adj->name);
		if (pISAdjEntry) {
		    freshISAdjEntry(pISAdjEntry, adj);		/* Isis-Mib */
		    freshISAdjAreaAddrTable(pISAdjEntry, adj);
		    pISAdjEntry->isisISAdjState = 2;		/* state <= up */
		} else {
	            trace_log_tf(isis_trace_options,
				 0,
				 LOG_ERR,
				 ("adjUpEvent(): cannot locate entry in IS adj table for localId==%d, adj.name==%d", 
				  circuit->localId,
				  adj->name));
		}
            } else {
	        trace_log_tf(isis_trace_options,
			     0,
			     LOG_ERR,
			     ("adjUpEvent(): cannot locate circ mib entry for localId==%d",
			      circuit->localId));
            }
	}
#endif	/* PROTO_SNMP */
}


void
adjDownEvent(circuit, adj, reason)
CircuitEntry	*circuit;	/* circuit adjacency is on */
AdjacencyEntry	*adj;
ReasonCode	reason;		/* reason for failure */
{
	char s[80];

	if ((adj->state == AdjUp) && (reason == OneWayConnection)) {
		/* we went from up to initializing - neighbor must have restarted */
		const char *t = "IS-IS adj up->initializing (neighbor not hearing me or restarted)"; 
		logAdjEvent(circuit, adj, t);
		lspAdjDown(circuit, adj);
		adj->state = AdjInitializing;
	} else if (adj->state == AdjUp) {
		sprintf(s, "IS-IS adj up->down (%s)", reasonCodeToStr(reason));
		logAdjEvent(circuit, adj, s);
		adj->state = AdjFailed;
		lspAdjDown(circuit, adj);
	} else if (adj->state == AdjInitializing) {
		/* we went from initializing to down */
		sprintf(s, "IS-IS adj initializing->down (%s)", reasonCodeToStr(reason));
		logAdjEvent(circuit, adj, s);
		adj->state = AdjFailed;
	}
#ifdef	PROTO_SNMP
	{
    	    IsisMibEntry	*pMibEntry;
    	    IsisCircEntry	*pCircEntry;
	    IsisISAdjEntry	*pISAdjEntry;

    	    pMibEntry = (IsisMibEntry *)locateEntry(&isisMibTable, 1);
    	    pCircEntry = (IsisCircEntry *)locateEntry(&pMibEntry->isisCircTable, circuit->localId);
    	    if (pCircEntry) {
		pISAdjEntry = (IsisISAdjEntry *)locateEntry(&pCircEntry->isisISAdjTable, adj->name);
		if (pISAdjEntry) {
		    if (adj->state == AdjFailed) {
		        if (queueDelete(&pCircEntry->isisISAdjTable, pISAdjEntry)) {
	                    trace_log_tf(isis_trace_options,
					 0,
					 LOG_ERR,
					 ("adjDownEvent(): cannot delete entry in IS adj table for circIndex==%d, adjIndex==%d", 
					  pISAdjEntry->isisISAdjIndex,
					  pISAdjEntry->isisISAdjIndex)); /* Isis-Mib */
		        }
		    } else {
		    	pISAdjEntry->isisISAdjState = 1;		/* state <= initializing */
		    }
		} else {
	            trace_log_tf(isis_trace_options,
				 0,
				 LOG_ERR,
				 ("adjDownEvent(): cannot locate entry in IS adj table for localId==%d, adj.name==%d", 
				  circuit->localId,
				  adj->name));
		}
            } else {
	        trace_log_tf(isis_trace_options,
			     0,
			     LOG_ERR,
			     ("adjDownEvent(): cannot locate circ mib entry for localId==%d",
			      circuit->localId));
            }
	}
#endif	/* PROTO_SNMP */

	if (adj->state == AdjFailed) {
		removeAdjFromHash(adj);
		/* 
	 	 *	if we are called due to a timer expiration, the adj has already been
	 	 *	removed from the linked list. Otherwise, remove it now.
	 	 */
		if (reason != HoldingTimerExpired)
			DLLRemque(adj);

		/* schedule this adjacency to be deleted after next spf is run */
		addOldAdjacency(adj);
	}
}


void
desigSysChangeEvent __PF3(circuit, CircuitEntry *,
			  level, int,
			  elected, Boolean)		/* true => elected, false => resigned */
{
	trace_log_tf(isis_trace_options,
		     0,
		     LOG_ERR,
		     ("IS-IS DR change: %s: L%d %s",
		      circuit->name,
		      level, 
		      elected ? "elected" : "resigned"));
        if (level == 1) {
                circuit->l1DesignateChanges++;
#ifdef	PROTO_SNMP
		{
		    IsisMibEntry	*pMibEntry;
	    	    IsisCircEntry	*pCircEntry;

		    pMibEntry = (IsisMibEntry *)locateEntry(&isisMibTable, 1);
		    pCircEntry = (IsisCircEntry *)locateEntry(&pMibEntry->isisCircTable,
								circuit->localId);
		    if (pCircEntry) {
		        ++pCircEntry->isisCircLANL1DesISChanges;	/* Isis-Mib */
		    }
		    else {
			trace_log_tf(isis_trace_options,
				     0,
				     LOG_ERR,
				     ("desigSysChangeEvent(): cannot locate circ mib entry for localId==%d",
				      circuit->localId));
		    }
		}
#endif	/* PROTO_SNMP */

	}
        else {
                circuit->l2DesignateChanges++;
#ifdef	PROTO_SNMP
		{
	    	    IsisMibEntry	*pMibEntry;
	    	    IsisCircEntry	*pCircEntry;

		    pMibEntry = (IsisMibEntry *)locateEntry(&isisMibTable, 1);
		    pCircEntry = (IsisCircEntry *)locateEntry(&pMibEntry->isisCircTable,
								circuit->localId);
		    if (pCircEntry) {
		        ++pCircEntry->isisCircLANL2DesISChanges;	/* Isis-Mib */
		    }
		    else {
			trace_log_tf(isis_trace_options,
				     0,
				     LOG_ERR,
				     ("desigSysChangeEvent: cannot locate circ mib entry for localId==%d",
				      circuit->localId));
		    }
		}
#endif	/* PROTO_SNMP */

	}
}


void
logAdjEvent __PF3(circuit, CircuitEntry *,
		  adj, AdjacencyEntry *,
		  msg, const char *)
{
	int i;

	tracef("%s: %s: %s %s",
	       msg,
	       circuit->name,
	       systemTypeToStr(adj->adjacencyType),
	       adj->level2Only ? "(L2 only)" : "");

        circuit->adjacencyEvents++;
#ifdef	PROTO_SNMP
	{
	    IsisMibEntry	*pMibEntry;
	    IsisCircEntry	*pCircEntry;

	    pMibEntry = (IsisMibEntry *)locateEntry(&isisMibTable, 1);
	    pCircEntry = (IsisCircEntry *)locateEntry(&pMibEntry->isisCircTable,
								circuit->localId);
	    if (pCircEntry) {
	        ++pCircEntry->isisCircAdjChanges;	/* Isis-Mib */
	    }
	    else {
		trace_log_tf(isis_trace_options,
			     0,
			     LOG_ERR,
			     ("logAdjEvent(): cannot locate circ mib entry for localId==%d",
			      circuit->localId));
	    }

	}
#endif	/* PROTO_SNMP */

	switch (adj->adjacencyType) {
		case L1IS:
		case L2IS:
#define	HEXPRINT
#ifdef	HEXPRINT
			trace_log_tf(isis_trace_options,
				     0,
				     LOG_ERR,
				     ("%s",
				      IDToStr(adj->neighborSysID, 6)));
#else
			trace_log_tf(isis_trace_options,
				     0,
				     LOG_ERR,
				     ("%s",
				      adj->neighborSysID));
#endif	/* HEXPRINT */
			break;
		case ES:
			if (circuit->circuitType == Broadcast) {
			    trace_log_tf(isis_trace_options,
					 0,
					 LOG_ERR,
					 ("SNPA %s",
					  IDToStr(adj->neighborMAC, 6)));
			} else {
			    tracef("Neighbor%s", (adj->numIds>1) ? "s":" ");
			    for (i=0; i<adj->numIds; i++) {
				tracef("%s%c", IDToStr(&adj->neighborInfo[i*6], 6),
				       i<(adj->numIds-1)?',':' ');
			    }
			    trace_log_tf(isis_trace_options,
					 0,
					 LOG_ERR,
					 (NULL));
			}

			break;
		default:
			assert(0);
	}
}

/*
 *	Called when a circuit is turned on
 *
 *	1) transmit an IIH PDU
 *	2) solicit the end system configuration
 *	3) set a timer to run the election process
 */
void
circuitUpEvent __PF1(c, CircuitEntry *)
{
	c->circuitChanges++;

	if (c->circuitType == Broadcast) {
		switch (systemType) {
			case L1IS:
				circuitL1IIHTimeout(c->timer_l1iih, (time_t) 0);
				break;

			case L2IS:
				if (!c->manualL2Only) {
					circuitL1IIHTimeout(c->timer_l1iih, (time_t) 0);
				}
				circuitL2IIHTimeout(c->timer_l2iih, (time_t) 0);
				break;
			default:
				assert(0);
		}

		setTimer(sysIIHInterval * 2, lanFirstTimeElection, c);
		solicitESConfig(c);
	} else if (c->circuitType == Pt2Pt) {
		circuitP2PIIHTimeout(c->timer_p2piih, (time_t) 0);
	}

	lspCircuitUp(c);
}
