/* ospf_neighbor.ex.c: Operations on OSPF neighbors. */

/***** Header files *****/
#include <opnet.h>

/* OPNET Model Support (OMS) headers */
#include "oms_pr.h"

/* IP specific headers */
#include "ip3_addr.h"
#include "ip3_rte.h"

/* Dijkstra package header */
#include "djk.h"

/* OSPF specific headers */
#include "ospf_defs.h"
#include "ospf_const.h"
#include "ospf_lsa.h"
#include "ospf_neighbor.h"
#include "ospf_interface.h"
#include "ospf_area.h"
#include "ospf_msg.h"

/***** Globals *****/
extern IpT_Address			IpI_Default_Addr;
extern Stathandle			OspfI_Global_Load_Stat;
extern double				OspfI_Global_Bits_Accum;

/* Table indicating procedure to execute for a particular state/event combination. */
OspfT_Neighbor_State_Event_Proc
OspfI_Neighbor_Trans_Table [OSPFC_NBR_NUM_STATES] [OSPFC_NBR_NUM_EVENTS] = 
{
	/* Down state */
	{
		ospf_neighbor_undef_handler,	/* Hello Received */
		ospf_neighbor_undef_handler,	/* Start */
		ospf_neighbor_undef_handler,	/* Two Way Received */
		ospf_neighbor_undef_handler,	/* Neg Done */
		ospf_neighbor_undef_handler,	/* Exchg Done */
		ospf_neighbor_undef_handler,	/* Bad LS Req */
		ospf_neighbor_undef_handler,	/* Load Done */
		ospf_neighbor_undef_handler,	/* Adj OK */
		ospf_neighbor_undef_handler,	/* Seq Num Mismatch */
		ospf_neighbor_undef_handler,	/* One Way Received */
		ospf_neighbor_undef_handler,	/* Kill Nbr */
		ospf_neighbor_undef_handler,	/* Inactive Timer */
		ospf_neighbor_undef_handler		/* LL Down */
	},

	/* Attempt state */
	{
		ospf_neighbor_process_invoke,	/* Hello Received */
		ospf_neighbor_undef_handler,	/* Start */
		ospf_neighbor_undef_handler,	/* Two Way Received */
		ospf_neighbor_undef_handler,	/* Neg Done */
		ospf_neighbor_undef_handler,	/* Exchg Done */
		ospf_neighbor_undef_handler,	/* Bad LS Req */
		ospf_neighbor_undef_handler,	/* Load Done */
		ospf_neighbor_undef_handler,	/* Adj OK */
		ospf_neighbor_undef_handler,	/* Seq Num Mismatch */
		ospf_neighbor_undef_handler,	/* One Way Received */
		ospf_neighbor_destroy,			/* Kill Nbr */
		ospf_neighbor_destroy,			/* Inactive Timer */
		ospf_neighbor_destroy			/* LL Down */
	},

	/* Init state */
	{
		ospf_neighbor_inactive_reset,	/* Hello Received */
		ospf_neighbor_undef_handler,	/* Start */
		ospf_neighbor_process_invoke,	/* Two Way Received */
		ospf_neighbor_undef_handler,	/* Neg Done */
		ospf_neighbor_undef_handler,	/* Exchg Done */
		ospf_neighbor_undef_handler,	/* Bad LS Req */
		ospf_neighbor_undef_handler,	/* Load Done */
		ospf_neighbor_undef_handler,	/* Adj OK */
		ospf_neighbor_undef_handler,	/* Seq Num Mismatch */
		ospf_neighbor_event_suppress,	/* One Way Received */
		ospf_neighbor_destroy,			/* Kill Nbr */
		ospf_neighbor_destroy,			/* Inactive Timer */
		ospf_neighbor_destroy			/* LL Down */
	},

	/* Two-Way state */	
	{
		ospf_neighbor_inactive_reset,	/* Hello Received */
		ospf_neighbor_undef_handler,	/* Start */
		ospf_neighbor_event_suppress,	/* Two Way Received */
		ospf_neighbor_undef_handler,	/* Neg Done */
		ospf_neighbor_undef_handler,	/* Exchg Done */
		ospf_neighbor_undef_handler,	/* Bad LS Req */
		ospf_neighbor_undef_handler,	/* Load Done */
		ospf_neighbor_process_invoke,	/* Adj OK */
		ospf_neighbor_undef_handler,	/* Seq Num Mismatch */
		ospf_neighbor_process_invoke,	/* One Way Received */
		ospf_neighbor_destroy,			/* Kill Nbr */
		ospf_neighbor_destroy,			/* Inactive Timer */
		ospf_neighbor_destroy			/* LL Down */
	},

	/* ExStart state */	
	{
		ospf_neighbor_inactive_reset,	/* Hello Received */
		ospf_neighbor_undef_handler,	/* Start */
		ospf_neighbor_event_suppress,	/* Two Way Received */
		ospf_neighbor_process_invoke,	/* Neg Done */
		ospf_neighbor_undef_handler,	/* Exchg Done */
		ospf_neighbor_undef_handler,	/* Bad LS Req */
		ospf_neighbor_undef_handler,	/* Load Done */
		ospf_neighbor_adj_reform,		/* Adj OK */
		ospf_neighbor_undef_handler,	/* Seq Num Mismatch */
		ospf_neighbor_process_invoke,	/* One Way Received */
		ospf_neighbor_destroy,			/* Kill Nbr */
		ospf_neighbor_destroy,			/* Inactive Timer */
		ospf_neighbor_destroy			/* LL Down */
	},

	/* Exchange state */	
	{
		ospf_neighbor_inactive_reset,	/* Hello Received */
		ospf_neighbor_undef_handler,	/* Start */
		ospf_neighbor_event_suppress,	/* Two Way Received */
		ospf_neighbor_undef_handler,	/* Neg Done */
		ospf_neighbor_process_invoke,	/* Exchg Done */
		ospf_neighbor_process_invoke,	/* Bad LS Req */
		ospf_neighbor_undef_handler,	/* Load Done */
		ospf_neighbor_adj_reform,		/* Adj OK */
		ospf_neighbor_process_invoke,	/* Seq Num Mismatch */
		ospf_neighbor_process_invoke,	/* One Way Received */
		ospf_neighbor_destroy,			/* Kill Nbr */
		ospf_neighbor_destroy,			/* Inactive Timer */
		ospf_neighbor_destroy			/* LL Down */
	},

	/* Loading state */	
	{
		ospf_neighbor_inactive_reset,	/* Hello Received */
		ospf_neighbor_undef_handler,	/* Start */
		ospf_neighbor_event_suppress,	/* Two Way Received */
		ospf_neighbor_undef_handler,	/* Neg Done */
		ospf_neighbor_undef_handler,	/* Exchg Done */
		ospf_neighbor_process_invoke,	/* Bad LS Req */
		ospf_neighbor_process_invoke,	/* Load Done */
		ospf_neighbor_adj_reform,		/* Adj OK */
		ospf_neighbor_process_invoke,	/* Seq Num Mismatch */
		ospf_neighbor_process_invoke,	/* One Way Received */
		ospf_neighbor_destroy,			/* Kill Nbr */
		ospf_neighbor_destroy,			/* Inactive Timer */
		ospf_neighbor_destroy			/* LL Down */
	},

	/* Full state */	
	{
		ospf_neighbor_inactive_reset,	/* Hello Received */
		ospf_neighbor_undef_handler,	/* Start */
		ospf_neighbor_event_suppress,	/* Two Way Received */
		ospf_neighbor_undef_handler,	/* Neg Done */
		ospf_neighbor_undef_handler,	/* Exchg Done */
		ospf_neighbor_process_invoke,	/* Bad LS Req */
		ospf_neighbor_undef_handler,	/* Load Done */
		ospf_neighbor_adj_reform,		/* Adj OK */
		ospf_neighbor_process_invoke,	/* Seq Num Mismatch */
		ospf_neighbor_process_invoke,	/* One Way Received */
		ospf_neighbor_destroy,			/* Kill Nbr */
		ospf_neighbor_destroy,			/* Inactive Timer */
		ospf_neighbor_destroy			/* LL Down */
	}
};

/***** Procedure Definitions *****/

OspfT_Neighbor *
ospf_neighbor_alloc (void)
	{
	OspfT_Neighbor *		neighbor_ptr;

	/** Allocate and initialize a neighbor data structure. **/
	FIN (ospf_neighbor_alloc (void));

	/* Allocate neighbor. */
	neighbor_ptr = (OspfT_Neighbor *) op_prg_mem_alloc (sizeof (OspfT_Neighbor));

	/* Initialize neighbor members. */
	neighbor_ptr->ip_address = IpI_Default_Addr;
	neighbor_ptr->options = 0;
	neighbor_ptr->last_message_ptr = OPC_NIL;
	neighbor_ptr->pending_req_ptr = OPC_NIL;

	FRET (neighbor_ptr);
	}

OspfT_Neighbor_Invoke_Info *
ospf_neighbor_invoke_info_create (void)
	{
	OspfT_Neighbor_Invoke_Info *		neighbor_invoke_ptr;

	/** Allocate and initialize a data structure used to pass state from **/
	/** the interface process to the neighbor process.                   **/
	FIN (ospf_neighbor_invoke_info_create (void));

	/* Allocate data structure. */
	neighbor_invoke_ptr = (OspfT_Neighbor_Invoke_Info *) op_prg_mem_alloc 
		(sizeof (OspfT_Neighbor_Invoke_Info));
	
	/* Initialize elements of data structure. */
	neighbor_invoke_ptr->neighbor_ptr = OPC_NIL;
	neighbor_invoke_ptr->neighbor_event = OspfC_Neighbor_Event_Start;
	neighbor_invoke_ptr->neighbor_priority = 1;

	FRET (neighbor_invoke_ptr);
	}

OspfT_Neighbor *
ospf_neighbor_create (IpT_Address neighbor_addr, OspfT_Neighbor_Event neighbor_event,
	Objid module_objid, int router_priority, OspfT_Interface *parent_interface_ptr)
	{
	OspfT_Neighbor *				neighbor_ptr;
	OspfT_Neighbor_Invoke_Info *	neighbor_invoke_ptr;

	/** Create a neighbor (specified by the IP address).  This procedure **/
	/** is also responsible for invoking the neighbor process; causing   **/
	/** initialization of the neighbor.                                  **/
	FIN (ospf_neighbor_create (neighbor_addr, neighbor_event, module_id, parent_interface_ptr));

	/* Create the neighbor. */
	neighbor_ptr = ospf_neighbor_alloc ();

	/* Create a neighbor process. */
	neighbor_ptr->proc_handle = op_pro_create ("ospf_neighbor", OPC_NIL);

	/* Register this process model in the process registry. */
	neighbor_ptr->proc_reg_handle = (OmsT_Pr_Handle) oms_pr_process_register
		(op_topo_parent (module_objid), module_objid,
			neighbor_ptr->proc_handle, "ospf_neighbor");

	/* Specify the interface this parent belongs to. */
	neighbor_ptr->parent_interface_ptr = parent_interface_ptr;

	/* Create state structure that will be passed to the neighbor process. */
	neighbor_invoke_ptr = ospf_neighbor_invoke_info_create ();

	/* Set the elements of the data structure. */	
	neighbor_invoke_ptr->neighbor_ptr = neighbor_ptr;
	neighbor_invoke_ptr->neighbor_event = neighbor_event;
	neighbor_invoke_ptr->neighbor_addr = neighbor_addr;
	neighbor_invoke_ptr->neighbor_priority = router_priority;

	/* Invoke the neighbor process so that it can initialize itself. */
	op_pro_invoke (neighbor_ptr->proc_handle, neighbor_invoke_ptr);

	FRET (neighbor_ptr);
	}

void
ospf_neighbor_destroy (OspfT_Neighbor *neighbor_ptr, OspfT_Neighbor_Event neighbor_event)
	{
	List *						neighbor_list_ptr;
	int							num_neighbors, neighbor_index;
	OspfT_Neighbor *			cur_neighbor_ptr;
	OspfT_Interface *			interface_ptr;

	/** Deallocates a neighbor, and calls op_pro_destroy () on the neighbor process. **/
	FIN (ospf_neighbor_destroy (neighbor_ptr, neighbor_event));

	/* Cancel any pending timers. */

	/* Don't cancel the inactive timer if that's what caused this procedure to be called. */
	if (op_ev_valid (neighbor_ptr->inactive_timer) && (neighbor_event != OspfC_Neighbor_Event_Inactive_Timer))
		op_ev_cancel (neighbor_ptr->inactive_timer);

	if (op_ev_valid (neighbor_ptr->rxmt_timer))
		op_ev_cancel (neighbor_ptr->rxmt_timer);

	if (op_ev_valid (neighbor_ptr->lsa_rxmt_timer))
		op_ev_cancel (neighbor_ptr->lsa_rxmt_timer);

	/* Destroy the retransmission message. */
	op_pk_destroy (neighbor_ptr->last_message_ptr);

	/* Destroy the process. */
	op_pro_destroy (neighbor_ptr->proc_handle);

	/* Remove oneself from the interface's neighbor list. */
	interface_ptr = neighbor_ptr->parent_interface_ptr;
	neighbor_list_ptr = ospf_interface_neighbor_list_get (interface_ptr);

	/* Loop through the neighbor list, and when a matching pointer is */
	/* found, use op_prg_list_remove () to remove the element.        */
	num_neighbors = op_prg_list_size (neighbor_list_ptr);
	for (neighbor_index = 0; neighbor_index < num_neighbors; neighbor_index++)
		{
		cur_neighbor_ptr = op_prg_list_access (neighbor_list_ptr, neighbor_index);
		if (neighbor_ptr == cur_neighbor_ptr)
			{
			op_prg_list_remove (neighbor_list_ptr, neighbor_index);
			break;
			}
		}

	ip_address_destroy (neighbor_ptr->ip_address);
	op_prg_mem_free (neighbor_ptr);

	/* The neighbor state has changed, so regenerate a new router links advertisement. */
	ospf_area_router_links_originate (interface_ptr->parent_area_ptr);

	/* If the router is the DR, then also originate a new network links advertisement. */
	if (ip_address_equal (ospf_interface_dr_get (interface_ptr),
		interface_ptr->parent_area_ptr->parent_router_ptr->router_id))
		ospf_interface_network_links_originate (interface_ptr);

	FOUT;
	}

void
ospf_neighbor_static_list_create (List *neighbor_list_ptr, Objid neighbor_list_objid, 
	Objid interface_objid, OspfT_Interface *parent_interface_ptr)
	{
	char				ip_addr_str [IPC_ADDR_STR_LEN];
	IpT_Address			ip_addr;
	Objid				neighbor_objid;
	int					num_neighbors, neighbor_index;
	OspfT_Neighbor *	neighbor_ptr;

	/** Add neighbors to the neighbor list based on static configuration by the user. **/
	/** This procedure should only be called if the interface is attached to a        **/
	/** multi-access non-broadcast network.  The object id passed to this procedure   **/
	/** represents a compound attribute that contains the list of neighbors.          **/
	FIN (ospf_neighbor_static_list_create (neighbor_list_ptr, neighbor_list_objid, parent_interface_ptr));
	
	/* Determine the number of configured neighbors. */
	num_neighbors = op_topo_child_count (neighbor_list_objid, OPC_OBJTYPE_GENERIC);

	/* Loop through each neighbor address and create a corresponding neighbor object. */
	for (neighbor_index = 0; neighbor_index < num_neighbors; neighbor_index++)
		{
		/* Get current neighbor object. */
		neighbor_objid = op_topo_child (neighbor_list_objid, OPC_OBJTYPE_GENERIC, neighbor_index);

		/* Get the IP address of the neighbor. */
		op_ima_obj_attr_get (neighbor_objid, "Neighbor Address", ip_addr_str);
		ip_addr = ip_address_create (ip_addr_str);
		
		/* Create a neighbor that is to be invoked with a 'Start' event. */
		neighbor_ptr = ospf_neighbor_create (ip_addr, OspfC_Neighbor_Event_Start, interface_objid,
			1, parent_interface_ptr);

		/* Add this neighbor to the neighbor list. */
		op_prg_list_insert (neighbor_list_ptr, neighbor_ptr, OPC_LISTPOS_TAIL);
		}

	FOUT;
	}

void
ospf_neighbor_list_summary_print (List *neighbor_list_ptr)
	{
	int						num_neighbors, neighbor_index;
	int						ls_trans_size, db_summ_size, ls_req_size;
	OspfT_Neighbor_State	neighbor_state;
	char					neighbor_state_str [32];
	char					ip_addr_str [IPC_ADDR_STR_LEN], router_id_str [IPC_ADDR_STR_LEN];
	OspfT_Neighbor *		neighbor_ptr;
	char					summary_line [512];

	/** Print out summary report of neighbors on an interface.  This report is **/
	/** is similar to the report described in section D.2.3 in RFC 1247.       **/
	FIN (ospf_neighbor_list_summary_print (neighbor_list_ptr));

	/* Print table header. */
	op_prg_odb_print_minor ("", OPC_NIL);
	op_prg_odb_print_minor ("Nbr IP Addr\tRouter ID\tState    \tLS Rxmt Size\tDbase Sum Size\tLS Req Size\tProcess ID",
		OPC_NIL);
	op_prg_odb_print_minor ("-----------\t---------\t-----    \t------------\t--------------\t-----------\t----------",
		OPC_NIL);

	/* Loop through each neighbor and print a summary line for each. */
	num_neighbors = op_prg_list_size (neighbor_list_ptr);

	for (neighbor_index = 0; neighbor_index < num_neighbors; neighbor_index++)
		{
		/* Get current neighbor. */
		neighbor_ptr = (OspfT_Neighbor *) op_prg_list_access (neighbor_list_ptr, 
			neighbor_index);

		/* Get neighbor IP address. */
		ip_address_print (ip_addr_str, neighbor_ptr->ip_address);

		/* Get neighbor router ID. */
		ip_address_print (router_id_str, ospf_neighbor_router_id_get (neighbor_ptr));

		/* Get the operational state. */
		neighbor_state = ospf_neighbor_state_get (neighbor_ptr, neighbor_state_str);

		/* Get size of the neighbor lists. */
		ls_trans_size = op_prg_list_size (ospf_neighbor_ls_transmit_list_get (neighbor_ptr));
		db_summ_size = op_prg_list_size (ospf_neighbor_dbase_summary_list_get (neighbor_ptr));
		ls_req_size = op_prg_list_size (ospf_neighbor_ls_request_list_get (neighbor_ptr));

		/* Construct summary string. */
		sprintf (summary_line, "%-15s\t%-15s\t%s\t\t%d\t\t%d\t\t%d\t\t%d", ip_addr_str, 
			router_id_str, neighbor_state_str, ls_trans_size, db_summ_size, ls_req_size,
			op_pro_id (neighbor_ptr->proc_handle));

		/* Print the summary string. */
		op_prg_odb_print_minor (summary_line, OPC_NIL);
		}

	FOUT;
	}

void
ospf_neighbor_message_send (OspfT_Neighbor *neighbor_ptr, Packet *message_ptr, Boolean rxmt_message)
	{
	static Ici *			ip_encap_ici_ptr = OPC_NIL;
	OspfT_Router *			parent_router_ptr;
	int						message_type;
	char					type_str [64];
	char					nbr_addr_str [IPC_ADDR_STR_LEN];
	char					intf_addr_str [IPC_ADDR_STR_LEN];
	char					trace_str [128];
	double					pk_size;

	/** Send a database description message to a neighbor.  Use the neighbor's **/
	/** IP address as the destination for this message.                        **/
	FIN (ospf_neighbor_neighbor_message_send (neighbor_ptr, message_ptr, rxmt_message));

	/* Create the IP encap ICI to be associated with this packet. */
	if (ip_encap_ici_ptr == OPC_NIL)
		ip_encap_ici_ptr = op_ici_create ("ip3_encap_req");

	/* Set the destination. */
	op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", neighbor_ptr->ip_address);

	/* Install the ICI. */
	op_ici_install (ip_encap_ici_ptr);

	/* Get the parent router. */
	parent_router_ptr = ospf_interface_parent_router_get (neighbor_ptr->parent_interface_ptr);

	if (rxmt_message)
		{
		/* Before sending the message, set up a re-transmission timer.  If */
		/* this timer expires, then the message is resent.  This timer is  */
		/* reset upon receiving an acknowledgement of the message.         */
		neighbor_ptr->last_message_ptr = op_pk_copy (message_ptr);
		neighbor_ptr->rxmt_timer = op_intrpt_schedule_call (op_sim_time () + 
			neighbor_ptr->parent_interface_ptr->retransmit_interval, 0, 
			ospf_neighbor_message_rxmt, neighbor_ptr);
		}

	if (op_prg_odb_ltrace_active ("ospf_message_send"))
		{
		message_type = ospf_message_type_get (message_ptr, type_str);
		ip_address_print (nbr_addr_str, neighbor_ptr->ip_address);
		ip_address_print (intf_addr_str, neighbor_ptr->parent_interface_ptr->interface_address);
		sprintf (trace_str, "Unicast %s message sent to neighbor %s on interface %s", 
			type_str, nbr_addr_str, intf_addr_str);
		op_prg_odb_print_minor (trace_str, OPC_NIL);
		}

	/* Update the router load statistics. */
	pk_size = (double) op_pk_total_size_get (message_ptr);
	parent_router_ptr->bits_accum += pk_size;
	OspfI_Global_Bits_Accum += pk_size;

	if (op_sim_time () > 0.0)
		{
		op_stat_write (parent_router_ptr->load_stat, parent_router_ptr->bits_accum / op_sim_time ());
		op_stat_write (OspfI_Global_Load_Stat, OspfI_Global_Bits_Accum / op_sim_time ());
		}

	/* Send the message. */
	op_pk_send_forced (message_ptr, parent_router_ptr->ip_outstrm);

	FOUT;
	}

void
ospf_neighbor_message_rxmt (OspfT_Neighbor *neighbor_ptr, int intrpt_code)
	{
	Packet *				tmp_message_ptr;
	static Ici *			ip_encap_ici_ptr = OPC_NIL;
	OspfT_Router *			parent_router_ptr;
	int						message_type;
	char					type_str [64];
	char					nbr_addr_str [IPC_ADDR_STR_LEN];
	char					intf_addr_str [IPC_ADDR_STR_LEN];
	char					trace_str [128];
	double					pk_size;

	/** This procedure is invoked when the retransmission timer for the **/
	/** neighbor has timed out.  Simply resend the message, and reset   **/
	/** the timer.                                                      **/
	FIN (ospf_neighbor_message_rxmt (neighbor_ptr, intrpt_code));

	/* Create the IP encap ICI to be associated with this packet. */
	if (ip_encap_ici_ptr == OPC_NIL)
		ip_encap_ici_ptr = op_ici_create ("ip3_encap_req");

	/* Set the destination. */
	op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", neighbor_ptr->ip_address);

	/* Install the ICI. */
	op_ici_install (ip_encap_ici_ptr);

	/* Get the parent router. */
	parent_router_ptr = ospf_interface_parent_router_get (neighbor_ptr->parent_interface_ptr);

	/* Before sending the message, set up a re-transmission timer.  If */
	/* this timer expires, then the message is resent.  This timer is  */
	/* reset upon receiving an acknowledgement of the message.         */
	tmp_message_ptr = op_pk_copy (neighbor_ptr->last_message_ptr);
	neighbor_ptr->rxmt_timer = op_intrpt_schedule_call (op_sim_time () + 
		neighbor_ptr->parent_interface_ptr->retransmit_interval, 0, 
		ospf_neighbor_message_rxmt, neighbor_ptr);

	if (op_prg_odb_ltrace_active ("ospf_message_send"))
		{
		message_type = ospf_message_type_get (tmp_message_ptr, type_str);
		ip_address_print (nbr_addr_str, neighbor_ptr->ip_address);
		ip_address_print (intf_addr_str, neighbor_ptr->parent_interface_ptr->interface_address);
		sprintf (trace_str, "Unicast %s retransmission sent to neighbor %s on interface %s", 
			type_str, nbr_addr_str, intf_addr_str);
		op_prg_odb_print_minor (trace_str, OPC_NIL);
		}

	/* Update the router load statistics. */
	pk_size = (double) op_pk_total_size_get (neighbor_ptr->last_message_ptr);
	parent_router_ptr->bits_accum += pk_size;
	OspfI_Global_Bits_Accum += pk_size;

	if (op_sim_time () > 0.0)
		{
		op_stat_write (parent_router_ptr->load_stat, parent_router_ptr->bits_accum / op_sim_time ());
		op_stat_write (OspfI_Global_Load_Stat, OspfI_Global_Bits_Accum / op_sim_time ());
		}

	/* Send the message. */
	op_pk_send_forced (neighbor_ptr->last_message_ptr, parent_router_ptr->ip_outstrm);

	neighbor_ptr->last_message_ptr = tmp_message_ptr;

	FOUT;
	}

void
ospf_neighbor_ls_list_clear (List *nbr_list_ptr)
	{
	int				num_ls;

	/** Clear a link state list belonging to a neighbor. **/
	FIN (ospf_neighbor_ls_list_clear (neighbor_ptr));

	/* Simply remove the elements of the list.  The advertisements are not deleted, */
	/* since they reference advertisements in the link state database.              */
	num_ls = op_prg_list_size (nbr_list_ptr);
	while (num_ls > 0)
		{
		op_prg_list_remove (nbr_list_ptr, OPC_LISTPOS_TAIL);
		num_ls--;
		}
	
	FOUT;
	}

int
ospf_neighbor_list_num_states_match (List *nbr_list_ptr, OspfT_Neighbor_State nbr_state)
	{
	int					num_matching_nbrs = 0;
	int					num_nbrs, nbr_index;
	OspfT_Neighbor *	neighbor_ptr;

	/** Return the number of neighbors in the list that match the **/
	/** given state.  Useful for answering questions like: 'How   **/
	/** many neighbors in the list are fully adjacent'.           **/
	FIN (ospf_neighbor_list_num_states_match (nbr_list_ptr, nbr_state));

	num_nbrs = op_prg_list_size (nbr_list_ptr);

	for (nbr_index = 0; nbr_index < num_nbrs; nbr_index++)
		{
		neighbor_ptr = op_prg_list_access (nbr_list_ptr, nbr_index);
		if (ospf_neighbor_state_get (neighbor_ptr, OPC_NIL) == nbr_state)
			num_matching_nbrs++;
		}

	FRET (num_matching_nbrs);
	}

OspfT_Neighbor_State
ospf_neighbor_state_get (OspfT_Neighbor *neighbor_ptr, char *neighbor_state_str)
	{
	OspfT_Neighbor_State *	neighbor_state_ptr;

	/** Return the current operational state (as defined by OSPF) of the neighbor. **/
	/** If neighbor_state_str is non-NIL, then a string representation is copied   **/
	/** into neighbor_state_str.                                                   **/
	FIN (ospf_neighbor_state_get (neighbor_ptr, neighbor_state_str));

	/* Get the state via the process registry. */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"state", OMSC_PR_ADDRESS, &neighbor_state_ptr);

	/* Return string value if specified. */
	if (neighbor_state_str != OPC_NIL)
		ospf_neighbor_state_str_get (*neighbor_state_ptr, neighbor_state_str);

	FRET (*neighbor_state_ptr);
	}

void
ospf_neighbor_state_str_get (OspfT_Neighbor_State nbr_state, char *neighbor_state_str)
	{
	char					neighbor_state_str_array [OSPFC_NBR_NUM_STATES][32] = 
	    {"Down", "Attempt", "Init",	"2-Way", "ExStart", "Exchg", "Load", "Full"};

	/** Get the string representation of the operational state of the neighbor. **/
	FIN (ospf_neighbor_state_str_get (nbr_state, neighbor_state_str));

	strcpy (neighbor_state_str, neighbor_state_str_array [(int) nbr_state]);

	FOUT;
	}

OspfT_Router_Id
ospf_neighbor_dr_get (OspfT_Neighbor *neighbor_ptr)
	{
	OspfT_Router_Id *			designated_router_id_ptr;

	/** Return the neighbor's idea of the designated router. **/
	FIN (ospf_neighbor_dr_get (neighbor_ptr));

	/* Since the neighbor's idea of the designated router is part of */
	/* the dynamic state of the neighbor, retrieve the designated    */
	/* router ID from the process registry.                          */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"designated router", OMSC_PR_ADDRESS, &designated_router_id_ptr);

	FRET (*designated_router_id_ptr);
	}

void
ospf_neighbor_dr_set (OspfT_Neighbor *neighbor_ptr, OspfT_Router_Id new_dr)
	{
	OspfT_Router_Id *			designated_router_id_ptr;

	/** Set the neighbor's designated router.  This would be set **/
	/** when a hello packet arrives with the neighbor's idea of  **/
	/** the designated router.                                   **/
	FIN (ospf_neighbor_dr_set (neighbor_ptr, new_dr));

	/* Retrieve from process registry. */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"designated router", OMSC_PR_ADDRESS, &designated_router_id_ptr);

	ip_address_destroy (*designated_router_id_ptr);
	*designated_router_id_ptr = ip_address_copy (new_dr);

	FOUT;
	}

OspfT_Router_Id
ospf_neighbor_backup_dr_get (OspfT_Neighbor *neighbor_ptr)
	{
	OspfT_Router_Id *			backup_designated_router_id_ptr;

	/** Return the neighbor's idea of the backup designated router. **/
	FIN (ospf_neighbor_backup_dr_get (neighbor_ptr));

	/* Since the neighbor's idea of the backup designated router is   */
	/* part of the dynamic state of the neighbor, retrieve the backup */
	/* designated router ID from the process registry.                */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"backup designated router", OMSC_PR_ADDRESS, &backup_designated_router_id_ptr);

	FRET (*backup_designated_router_id_ptr);
	}

void
ospf_neighbor_backup_dr_set (OspfT_Neighbor *neighbor_ptr, OspfT_Router_Id new_backup_dr)
	{
	OspfT_Router_Id *			backup_designated_router_id_ptr;

	/** Set the neighbor's backup DR.  This would be set **/
	/** when a hello packet with the neighbor's idea of  **/
	/** the backup designated router.                    **/
	FIN (ospf_neighbor_backup_dr_set (neighbor_ptr, new_backup_dr));

	/* Retrieve from process registry. */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"backup designated router", OMSC_PR_ADDRESS, &backup_designated_router_id_ptr);
	
	ip_address_destroy (*backup_designated_router_id_ptr);
	*backup_designated_router_id_ptr = ip_address_copy (new_backup_dr);

	FOUT;
	}

List *
ospf_neighbor_ls_transmit_list_get (OspfT_Neighbor *neighbor_ptr)
	{
	List **					ls_transmit_list_pptr;

	/** Return the neighbor's link state retransmission list. **/
	FIN (ospf_neighbor_ls_transmit_list_get (neighbor_ptr));

	/* Retrieve link state retransmission list from process registry. */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"link state transmit list", OMSC_PR_ADDRESS, &ls_transmit_list_pptr);

	FRET (*ls_transmit_list_pptr);
	}

List *
ospf_neighbor_dbase_summary_list_get (OspfT_Neighbor *neighbor_ptr)
	{
	List **					dbase_summary_list_pptr;

	/** Retrieve the neighbor's database summary list. **/
	FIN (ospf_neighbor_dbase_summary_list_get (neighbor_ptr));

	/* Retrieve the database summary list from process registry. */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"database summary list", OMSC_PR_ADDRESS, &dbase_summary_list_pptr);

	FRET (*dbase_summary_list_pptr);
	}

List *
ospf_neighbor_ls_request_list_get (OspfT_Neighbor *neighbor_ptr)
	{
	List **					ls_request_list_pptr;

	/** Retrieve the neighbor's link state request list. **/
	FIN (ospf_neighbor_ls_request_list_get (neighbor_ptr));

	/* Retrieve the link state request list from process registry. */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"link state request list", OMSC_PR_ADDRESS, &ls_request_list_pptr);

	FRET (*ls_request_list_pptr);
	}

OspfT_Router_Id
ospf_neighbor_router_id_get (OspfT_Neighbor *neighbor_ptr)
	{
	OspfT_Router_Id *			neighbor_id_ptr;

	/** Retrieve the router ID of the neighbor. **/
	FIN (ospf_neighbor_router_id_get (neighbor_ptr));

	/* The neighbor ID is part of the dynamic state of the neighbor, */
	/* retrieve it through the process registry.                     */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"router ID", OMSC_PR_ADDRESS, &neighbor_id_ptr);

	FRET (*neighbor_id_ptr);
	}

void
ospf_neighbor_router_id_set (OspfT_Neighbor *neighbor_ptr, OspfT_Router_Id new_id)
	{
	OspfT_Router_Id *			neighbor_id_ptr;

	/** Set the router ID of the neighbor.  This occurs when the first **/
	/** hello message from the neighbor arrives.                       **/
	FIN (ospf_neighbor_router_id_set (neighbor_ptr, new_id));

	/* Retrieve the neighbor ID through the process registry, destroy */
	/* the old ID, and set the new ID.                                */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"router ID", OMSC_PR_ADDRESS, &neighbor_id_ptr);

	ip_address_destroy (*neighbor_id_ptr);
	*neighbor_id_ptr = ip_address_copy (new_id);

	FOUT;
	}

void
ospf_neighbor_event_str_get (OspfT_Neighbor_Event neighbor_event, char *event_str)
	{
	char neighbor_event_str_array [OSPFC_NBR_NUM_EVENTS] [128] = 
    	{"Hello Received", "Start", "2-Way Received", "Negotiation Done", "Exchange Done",
		 "Bad LS Request", "Loading Done", "Adj OK?", "Sequence Number Mismatch", "1-Way Received",
		 "Kill Neighbor", "Inactive Timer", "LL Down"};

	/** Return the string representation of an event. **/
	FIN (ospf_neighbor_event_str_get (neighbor_event, event_str));

	strcpy (event_str, neighbor_event_str_array [(int) neighbor_event]);

	FOUT;
	}

int
ospf_neighbor_priority_get (OspfT_Neighbor *neighbor_ptr)
	{
	int *				priority_ptr;

	/** Returns the priority of the neighboring router. **/
	FIN (ospf_neighbor_priority_get (neighbor_ptr));

	/* Since the priority is part of the dynamic state of the neighbor, */
	/* use the process registry to get it.                              */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"router priority", OMSC_PR_ADDRESS, &priority_ptr);

	FRET (*priority_ptr);
	}

void
ospf_neighbor_priority_set (OspfT_Neighbor *neighbor_ptr, int new_prio)
	{
	int *				priority_ptr;

	/** Update the state of the neighbor.  This is caused by a hello **/
	/** message that advertises a new priority for a neighbor.       **/
	FIN (ospf_neighbor_priority_set (neighbor_ptr, new_prio));

	/* Retrieve a pointer to the priority state variable and update it. */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"router priority", OMSC_PR_ADDRESS, &priority_ptr);

	*priority_ptr = new_prio;

	FOUT;
	}

Boolean
ospf_neighbor_master_status_get (OspfT_Neighbor *neighbor_ptr)
	{
	Boolean *			master_status_ptr;

	/** Returns OPC_TRUE if router is considered master with respect **/
	/** to the passed in neighbor, OPC_FALSE otherwise.              **/
	FIN (ospf_neighbor_master_status_get (neighbor_ptr));

	/* Since the master slave status is consider part of the dynamic state */
	/* of the router, retrieve the status from the process registry.       */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"master/slave status", OMSC_PR_ADDRESS, &master_status_ptr);

	FRET (*master_status_ptr);
	}

void
ospf_neighbor_master_status_set (OspfT_Neighbor *neighbor_ptr, Boolean new_status)
	{
	Boolean *			master_status_ptr;

	/** Update the master/slave status of the neighbor.  Occurs during **/
	/** adjacency negotiation.                                         **/
	FIN (ospf_neighbor_master_status_set (neighbor_ptr, new_status));	

	/* Retrieve a pointer to the master/slave status state variable and update it. */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"master/slave status", OMSC_PR_ADDRESS, &master_status_ptr);

	*master_status_ptr = new_status;

	FOUT;
	}

int
ospf_neighbor_seq_num_get (OspfT_Neighbor *neighbor_ptr)
	{
	int *				seq_num_ptr;

	/** Returns the current database description sequence number of the neighbor. **/
	FIN (ospf_neighbor_seq_num_get (neighbor_ptr));

	/* Since the sequence number is part of the dynamic state of the neighbor, */
	/* retrieve it from the process registry.                                  */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"sequence number", OMSC_PR_ADDRESS, &seq_num_ptr);

	FRET (*seq_num_ptr);
	}

void
ospf_neighbor_seq_num_set (OspfT_Neighbor *neighbor_ptr, int new_seq_num)
	{
	int *				seq_num_ptr;

	/** Updates the sequence number of the neighbor.  Occurs during **/
	/** master/slave negotiation if the router becomes the slave.   **/
	FIN (ospf_neighbor_seq_num_set (neighbor_ptr, new_seq_num));

	/* Retrieve a pointer to the sequence number state variable and update it. */
	oms_pr_attr_get (neighbor_ptr->proc_reg_handle,
		"sequence number", OMSC_PR_ADDRESS, &seq_num_ptr);

	*seq_num_ptr = new_seq_num;

	FOUT;
	}

void
ospf_neighbor_invoke (OspfT_Neighbor *neighbor_ptr, OspfT_Neighbor_Event neighbor_event)
	{
	char						nbr_id_str [IPC_ADDR_STR_LEN];
	char						trace_msg [256];
	char						state_str [128], event_str [128];
	OspfT_Interface_State		nbr_state;

	/** Cover function that will pass an event to the neighbor state machine. **/
	FIN (ospf_neighbor_invoke (neighbor_ptr, neighbor_event));

	nbr_state = ospf_neighbor_state_get (neighbor_ptr, state_str);

	if (op_prg_odb_ltrace_active ("ospf_event"))
		{
		ip_address_print (nbr_id_str, ospf_neighbor_router_id_get (neighbor_ptr));
		ospf_neighbor_event_str_get (neighbor_event, event_str);

		sprintf (trace_msg, "On neighbor %s in state %s, invoked event %s", nbr_id_str,
			state_str, event_str);

		op_prg_odb_print_minor (trace_msg, OPC_NIL);
		}

	OspfI_Neighbor_Trans_Table [(int) nbr_state] [(int) neighbor_event] (neighbor_ptr, neighbor_event);

	FOUT;
	}

Boolean
ospf_neighbor_adj_eval (OspfT_Neighbor *neighbor_ptr)
	{
	OspfT_Interface *				intf_ptr;
	OspfT_Router *					router_ptr;
	OspfT_Router_Id					dr, backup_dr;

	/** Returns OPC_TRUE, if this router should form an adjacency with this neighbor. **/
	/** Otherwise, returns OPC_FALSE.  See Section 10.4 in RFC 1583 for criteria.     **/
	FIN (ospf_neighbor_adj_eval (neighbor_ptr));

	/* Create a reference to the interface for which this neighbor belongs. */
	intf_ptr = neighbor_ptr->parent_interface_ptr;

	/* Create a reference to the router that contains the interface. */
	router_ptr = intf_ptr->parent_area_ptr->parent_router_ptr;
	
	/* Always establish for point-to-point links. */
	if (intf_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt)
		FRET (OPC_TRUE);

	/* Get designated router and backup designated router. */
	dr = ospf_interface_dr_get (intf_ptr);
	backup_dr = ospf_interface_backup_dr_get (intf_ptr);

	/* If the router is (Backup) DR, establish adjacency. */
	if (ip_address_equal (router_ptr->router_id, dr) ||
		ip_address_equal (router_ptr->router_id, backup_dr))
		FRET (OPC_TRUE);

	/* If the neighbor is (Backup) DR, establish adjacency. */
	if (ip_address_equal (ospf_neighbor_router_id_get (neighbor_ptr), dr) ||
		ip_address_equal (ospf_neighbor_router_id_get (neighbor_ptr), backup_dr))
		FRET (OPC_TRUE);

	/* None of the criteria are established, return OPC_FALSE. */
	FRET (OPC_FALSE);
	}

/***** State/Event Handlers *****/
void
ospf_neighbor_undef_handler (OspfT_Neighbor *neighbor_ptr, OspfT_Neighbor_Event neighbor_event)
	{
	char					err_msg [256];
	char					state_str [128], event_str [128], nbr_id_str [IPC_ADDR_STR_LEN];
	OspfT_Neighbor_State	nbr_state;

	/** This handles the case where we've encountered an undefined state/event **/
	/** combination.  End the simulation in this case.                         **/
	FIN (ospf_neighbor_undef_handler (neighbor_ptr, neighbor_event));

	nbr_state = ospf_neighbor_state_get (neighbor_ptr, state_str);
	ospf_neighbor_event_str_get (neighbor_event, event_str);
	ip_address_print (nbr_id_str, ospf_neighbor_router_id_get (neighbor_ptr));

	sprintf (err_msg, "Invalid state/event combination: Event %s received in state %s in neighbor %s",
		event_str, state_str, nbr_id_str);

	ospf_fatal_error (err_msg);

	FOUT;
	}

void
ospf_neighbor_process_invoke (OspfT_Neighbor *neighbor_ptr, OspfT_Neighbor_Event neighbor_event)
	{
	OspfT_Neighbor_Invoke_Info *			neighbor_info_ptr;

	/** Handles the case where invoking the process is sufficient. **/
	FIN (ospf_neighbor_process_invoke (neighbor_ptr, neighbor_event));

	/* Set up argument memory associated with the neighbor process invocation. */
	neighbor_info_ptr = ospf_neighbor_invoke_info_create ();
	neighbor_info_ptr->neighbor_event = neighbor_event;

	/* Invoke the process. */
	op_pro_invoke (neighbor_ptr->proc_handle, neighbor_info_ptr);

	FOUT;
	}

void
ospf_neighbor_inactive_reset (OspfT_Neighbor *neighbor_ptr, OspfT_Neighbor_Event neighbor_event)
	{
	double						router_dead_interval;

	/** Resets the inactivity timer for a particular neighbor.  This occurs **/
	/** when the neighbor has sent a hello to the router.                   **/
	FIN (ospf_neighbor_inactive_reset (neighbor_ptr, neighbor_event));

	/* First cancel the current timer. */
	op_ev_cancel (neighbor_ptr->inactive_timer);

	/* Then create a new timer. */
	router_dead_interval = neighbor_ptr->parent_interface_ptr->router_dead_interval;

	/* Set the firing of the timer, RouterDeadInterval seconds from now. */
	neighbor_ptr->inactive_timer = op_intrpt_schedule_call (op_sim_time () + router_dead_interval,
		0, ospf_neighbor_inactive_timer_expire, neighbor_ptr);

	FOUT;
	}

void
ospf_neighbor_adj_reform (OspfT_Neighbor *neighbor_ptr, OspfT_Neighbor_Event neighbor_event)
	{
	/** Evaluates an adjacency, and if adjacency is maintained, do nothing, **/
	/** otherwise invoke the state machine (transition back to 2-Way).      **/	
	FIN (ospf_neighbor_adj_reform (neighbor_ptr, neighbor_event));	

	if (!ospf_neighbor_adj_eval (neighbor_ptr))
		ospf_neighbor_process_invoke (neighbor_ptr, neighbor_event);

	FOUT;
	}

void
ospf_neighbor_inactive_timer_expire (OspfT_Neighbor *neighbor_ptr, int intrpt_code)
	{
	/** Handles the expiration of the inactivity timer.  Handle this by deallocating **/
    /** the neighbor that set the timer in the first place.                          **/
	FIN (ospf_neighbor_inactive_timer_expire (neighbor_ptr, intrpt_code));

	ospf_neighbor_destroy (neighbor_ptr, OspfC_Neighbor_Event_Inactive_Timer);

	FOUT;
	}

void
ospf_neighbor_event_suppress (OspfT_Neighbor *neighbor_ptr, OspfT_Neighbor_Event neighbor_event)
	{
	/** Handles the case where the event/state combination is defined, but **/
	/** no action is to be taken.  In this case, don't bother invoking     **/
	/** the state machine.                                                 **/
	FIN (ospf_neighbor_event_suppress (neighbor_ptr, neighbor_event));

	/* This function is a no-op. */

	FOUT;
	}

void 
ospf_neighbor_lsa_rxmt (OspfT_Neighbor *neighbor_ptr, int intrpt_code) 
	{ 
	List * 				lsa_rxmt_list_ptr; 
	int 				num_lsa, lsa_index; 
	int					max_pk_size; 
	Packet * 			lsu_msg_ptr; 
	OspfT_Lsa *			lsa_ptr;
	OspfT_Lsa_Header *	lsa_header_ptr;
	char				trace_msg [128];
	char				intf_addr_str [IPC_ADDR_STR_LEN];
	char				nbr_addr_str [IPC_ADDR_STR_LEN];

	/** Handler to retransmit whatever is currently on LSA list. **/
	FIN (ospf_neighbor_lsa_rxmt (neighbor_ptr, intrpt_code));

	/* Create a reference to retransmission list. */
	lsa_rxmt_list_ptr = ospf_neighbor_ls_transmit_list_get (neighbor_ptr);
	num_lsa = op_prg_list_size (lsa_rxmt_list_ptr);

	if (num_lsa > 0)
		{
		/* Sort list according to number of transmissions. */
		op_prg_list_sort (lsa_rxmt_list_ptr, ospf_neighbor_lsa_sort);

		/* Loop through LSAs, and create as big a LSU as possible. */
		max_pk_size = neighbor_ptr->parent_interface_ptr->mtu * 8;
		lsu_msg_ptr = ospf_message_lsu_create (neighbor_ptr);

		lsa_index = 0;
		while (((op_pk_total_size_get (lsu_msg_ptr) + OSPFC_MSG_LSAH_SIZE) <= max_pk_size) && 
			(lsa_index < num_lsa))
			{
			lsa_ptr = op_prg_list_access (lsa_rxmt_list_ptr, lsa_index);
			lsa_header_ptr = lsa_ptr->lsa_header_ptr;

			/* Age the LSA. */
			if (lsa_header_ptr->age != OSPFC_MAX_AGE)
				lsa_header_ptr->age += neighbor_ptr->parent_interface_ptr->transmit_delay;
 
			ospf_message_lsa_add (lsu_msg_ptr, lsa_ptr);
			lsa_header_ptr->num_retrans++;
			lsa_index++;

			/* Issue a trace message. */
			if (op_prg_odb_ltrace_active ("ospf_lsa"))
				{
				ip_address_print (intf_addr_str, neighbor_ptr->parent_interface_ptr->interface_address);
				ip_address_print (nbr_addr_str, neighbor_ptr->ip_address);
				sprintf (trace_msg, "Interface %s retransmits LSA (%d) to neighbor %s",
					intf_addr_str, lsa_header_ptr->sequence_num, nbr_addr_str);
				op_prg_odb_print_minor (trace_msg, OPC_NIL);
				}
			}

		ospf_neighbor_message_send (neighbor_ptr, lsu_msg_ptr, OPC_FALSE);
		}

	/* Set the next timed retransmission. */
	neighbor_ptr->lsa_rxmt_timer = op_intrpt_schedule_call (op_sim_time () +
		neighbor_ptr->parent_interface_ptr->retransmit_interval, 0,
		ospf_neighbor_lsa_rxmt, neighbor_ptr);

	FOUT;
	}

int
ospf_neighbor_lsa_sort (OspfT_Lsa *lsa1_ptr, OspfT_Lsa *lsa2_ptr)
	{
	/** Compare LSAs according to number of retransmissions.  Favor **/
	/** LSAs with less transmissions over LSAs with more.           **/
	FIN (ospf_neighbor_lsa_sort (lsa1_ptr, lsa2_ptr));
	
	if (lsa1_ptr->lsa_header_ptr->num_retrans < lsa2_ptr->lsa_header_ptr->num_retrans)
		FRET (1);

	if (lsa1_ptr->lsa_header_ptr->num_retrans > lsa2_ptr->lsa_header_ptr->num_retrans)
		FRET (-1);

	FRET (0);
	}

void
ospf_neighbor_lsa_rxmt_add (OspfT_Neighbor *neighbor_ptr, OspfT_Lsa *lsa_ptr)
	{
	List *					nbr_rxmt_list_ptr;

	/** Add an LSA to the neighbor's retransmission list. **/
	FIN (ospf_neighbor_lsa_rxmt_add (neighbor_ptr, lsa_ptr));

	/* Get the neighbor's retransmission list. */
	nbr_rxmt_list_ptr = ospf_neighbor_ls_transmit_list_get (neighbor_ptr);
	
	/* Add LSA to list. */
	op_prg_list_insert (nbr_rxmt_list_ptr, lsa_ptr, OPC_LISTPOS_TAIL);

	/* Indicate on the LSA that it's to be retransmitted. */
	lsa_ptr->rxmt_count++;

	FOUT;
	}

void
ospf_neighbor_lsa_rxmt_remove (OspfT_Neighbor *nbr_ptr, OspfT_Lsa *lsa_ptr)
	{
	List *			rxmt_list_ptr;
	OspfT_Router *	router_ptr;

	/** Remove an LSA from the retransmission list.  Also indicate on the LSA **/
	/** itself that it no longer is on any retransmission lists.              **/
	FIN (ospf_neighbor_lsa_rxmt_remove (nbr_ptr, lsa_ptr));

	rxmt_list_ptr = ospf_neighbor_ls_transmit_list_get (nbr_ptr);
	ospf_lsa_remove (rxmt_list_ptr, lsa_ptr);

	/* Decrement retransmission reference count. */
	lsa_ptr->rxmt_count--;

	/* Get the parent router. */
	router_ptr = nbr_ptr->parent_interface_ptr->parent_area_ptr->parent_router_ptr;

	/* Does this LSA need to be removed from the database? */
	if ((lsa_ptr->rxmt_count == 0) &&
		(lsa_ptr->lsa_header_ptr->age == OSPFC_MAX_AGE) &&
		(!ospf_neighbor_router_dbase_exchange_check (router_ptr)))
		ospf_lsa_dbase_remove (router_ptr, lsa_ptr);

	FOUT;
	}

Boolean
ospf_neighbor_router_dbase_exchange_check (OspfT_Router *router_ptr)
	{
	List *					area_list_ptr;
	OspfT_Area *			area_ptr;
	OspfT_Interface *		intf_ptr;
	OspfT_Neighbor *		nbr_ptr;
	OspfT_Neighbor_State	nbr_state;
	List *					nbr_list_ptr;
	int						num_areas, num_intfs, num_nbrs;
	int						area_index, intf_index, nbr_index;

	/** Check to see if any of the neighbors on the router are still taking  **/
	/** part in database exchange.  Returns OPC_TRUE if any neighbors on the **/
	/** router are in Exchange or Loading, OPC_FALSE if none are.            **/
	FIN (ospf_neighbor_dbase_exchange_check (router_ptr));

	area_list_ptr = ospf_area_list_get (router_ptr);
	num_areas = op_prg_list_size (area_list_ptr);
	for (area_index = 0; area_index < num_areas; area_index++)
		{
		area_ptr = op_prg_list_access (area_list_ptr, area_index);
		num_intfs = op_prg_list_size (area_ptr->interface_list_ptr);
		for (intf_index = 0; intf_index < num_intfs; intf_index++)
			{
			intf_ptr = op_prg_list_access (area_ptr->interface_list_ptr, intf_index);
			nbr_list_ptr = ospf_interface_neighbor_list_get (intf_ptr);
			num_nbrs = op_prg_list_size (nbr_list_ptr);
			for (nbr_index = 0; nbr_index < num_nbrs; nbr_index++)
				{
				nbr_ptr = op_prg_list_access (nbr_list_ptr, nbr_index);
				nbr_state = ospf_neighbor_state_get (nbr_ptr, OPC_NIL);
				if ((nbr_state == OspfC_Neighbor_State_Exchange) ||
					(nbr_state == OspfC_Neighbor_State_Loading))
					FRET (OPC_TRUE);
				}
			}
		}

	FRET (OPC_FALSE);
	}
