/* Process model C form file: ospf.pr.c */
/* Portions of this file copyright 1992, 1996 by MIL 3, Inc. */



/* OPNET system definitions */
#include <opnet.h>
#include "/u/class/cjin/ospf/ospf.pr.h"
FSM_EXT_DECS



/* Header block */
/***** Includes *****/

/* OMS specific headers */
#include "oms_pr.h"

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

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

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

/***** Globals *****/
OspfT_Area_Id		OspfI_Area_Id_Backbone;
IpT_Address			OspfI_All_Spf_Routers;
IpT_Address			OspfI_All_Dr_Routers;

extern IpT_Address	ALL_IGMP_ROUTERS;
extern IpT_Address	IpI_Default_Addr;
Stathandle			OspfI_Global_Load_Stat;
double				OspfI_Global_Bits_Accum;
Boolean				OspfI_Global_Init = OPC_FALSE;
double				OspfI_Converge_Time;
Boolean				OspfI_Converge_Time_Written;
int					OspfI_Auto_Router_Id = 1;

/***** Transition Macros *****/
#define	MESSAGE_RECEIVED		op_intrpt_type () == OPC_INTRPT_STRM
#define ROUTING_TABLE_CALC		op_intrpt_type () == OPC_INTRPT_SELF
#define END_SIM					op_intrpt_type () == OPC_INTRPT_ENDSIM

/***** Constants *****/
#define OSPFC_ROUTER_ID_AUTO_ASSIGN		"0.0.0.0"

/***** Procdecs *****/
void							ospf_sv_init (OspfT_Router *router_ptr);
List *							ospf_area_list_create (Objid interface_table_objid, OspfT_Router *parent_router_ptr);
void							ospf_root_message_dispatch (Packet *message_ptr, IpT_Address intf_addr);
OspfT_Router *					ospf_router_create (Prohandle proc_handle, OmsT_Pr_Handle proc_reg_handle);
OspfT_Interface *				ospf_router_interface_find (IpT_Address intf_addr);
void							ospf_router_area_config (Objid area_config_objid, List *area_list_ptr);
void							ospf_hello_message_rcvd (Packet *message_ptr, IpT_Address intf_addr);
void							ospf_dbase_desc_message_rcvd (Packet *message_ptr, IpT_Address intf_addr);
void							ospf_neighbor_dbase_sum_free (OspfT_Neighbor *neighbor_ptr, int intrpt_code);
void							ospf_link_state_request_message_rcvd (Packet *message_ptr, IpT_Address intf_addr);
void							ospf_link_state_update_message_rcvd (Packet *message_ptr, IpT_Address intf_addr);
void							ospf_link_state_ack_message_rcvd (Packet *message_ptr, IpT_Address intf_addr);
#if !defined (VOSD_NO_FIN)
#undef	BIN
#undef	BOUT
#define	BIN		_fstack_local_info.last_line_passed = __LINE__ - _block_origin;
#define	BOUT	BIN
#define	BINIT	_fstack_local_info.last_line_passed = 0; _block_origin = __LINE__;
#else
#define	BINIT
#endif /* #if !defined (VOSD_NO_FIN) */



/* State variable definitions */
typedef struct
	{
	FSM_SYS_STATE
	FILE *                  		sv_fp;
	List *	                 		sv_ospf_area_list_ptr;
	OspfT_Area *	           		sv_ospf_backbone_area_ptr;
	OspfT_Router_Id	        		sv_ospf_router_id;
	Objid	                  		sv_ospf_my_objid;
	Prohandle	              		sv_ospf_my_prohandle;
	OmsT_Pr_Handle	         		sv_ospf_my_proc_reg_handle;
	Objid	                  		sv_mcast_dyn_rte_objid;
	Objid                   		sv_igmprouter_objid;
	char                    		sv_router_name[30];
	IpT_Address	            		sv_ospf_message_intf_addr;
	IpT_Address	            		sv_ospf_message_src_addr;
	Ici *	                  		sv_ospf_message_ici_ptr;
	Packet *	               		sv_ospf_message_ptr;
	OspfT_Router *	         		sv_ospf_router_ptr;
	int	                    		sv_ospf_router_ip_outstrm;
	IpT_Rte_Info *	         		sv_ospf_ip_dyn_rte_info_ptr;
	OspfT_Lsa *	            		sv_ospf_dbase_last_lsa_ptr;
	double	                 		sv_ospf_rte_table_interval;
	Boolean	                		sv_ospf_start;
	} ospf_state;

#define pr_state_ptr            		((ospf_state*) SimI_Mod_State_Ptr)
#define fp                      		pr_state_ptr->sv_fp
#define ospf_area_list_ptr      		pr_state_ptr->sv_ospf_area_list_ptr
#define ospf_backbone_area_ptr  		pr_state_ptr->sv_ospf_backbone_area_ptr
#define ospf_router_id          		pr_state_ptr->sv_ospf_router_id
#define ospf_my_objid           		pr_state_ptr->sv_ospf_my_objid
#define ospf_my_prohandle       		pr_state_ptr->sv_ospf_my_prohandle
#define ospf_my_proc_reg_handle 		pr_state_ptr->sv_ospf_my_proc_reg_handle
#define mcast_dyn_rte_objid     		pr_state_ptr->sv_mcast_dyn_rte_objid
#define igmprouter_objid        		pr_state_ptr->sv_igmprouter_objid
#define router_name             		pr_state_ptr->sv_router_name
#define ospf_message_intf_addr  		pr_state_ptr->sv_ospf_message_intf_addr
#define ospf_message_src_addr   		pr_state_ptr->sv_ospf_message_src_addr
#define ospf_message_ici_ptr    		pr_state_ptr->sv_ospf_message_ici_ptr
#define ospf_message_ptr        		pr_state_ptr->sv_ospf_message_ptr
#define ospf_router_ptr         		pr_state_ptr->sv_ospf_router_ptr
#define ospf_router_ip_outstrm  		pr_state_ptr->sv_ospf_router_ip_outstrm
#define ospf_ip_dyn_rte_info_ptr		pr_state_ptr->sv_ospf_ip_dyn_rte_info_ptr
#define ospf_dbase_last_lsa_ptr 		pr_state_ptr->sv_ospf_dbase_last_lsa_ptr
#define ospf_rte_table_interval 		pr_state_ptr->sv_ospf_rte_table_interval
#define ospf_start              		pr_state_ptr->sv_ospf_start

/* This macro definition will define a local variable called	*/
/* "op_sv_ptr" in each function containing a FIN statement.	*/
/* This variable points to the state variable data structure,	*/
/* and can be used from a C debugger to display their values.	*/
#undef FIN_PREAMBLE
#define FIN_PREAMBLE	ospf_state *op_sv_ptr = pr_state_ptr;




/* Process model interrupt handling procedure */



void
ospf ()
	{
	char				proc_model_name [128];
	List*			    proc_record_handle_list_ptr;
	int				    record_handle_list_size;
	OmsT_Pr_Handle	    process_record_handle;
	
	int _block_origin = 0;


	FSM_ENTER (ospf)

	FSM_BLOCK_SWITCH
		{
		/*---------------------------------------------------------*/
		/** state (Init) enter executives **/
		FSM_STATE_ENTER_UNFORCED (0, state0_enter_exec, "Init", "ospf () [Init enter execs]")
			{
			/* Since OSPF receives begin sim by default, the fact that we entered */
			/* this state does not mean OSPF is running.  Only when we enter the  */
			/* exit execs (except by end sim) are we sure OSPF is running.  Use   */
			/* a flag to indicate whether or not OSPF has started.                */
			ospf_start = OPC_FALSE;
			fp = fopen("test_ospf", "w");
			    fprintf (fp, " inside opsf enterexec\n");
			/* Create an area ID that represents the backbone. */
			OspfI_Area_Id_Backbone = ip_address_create ("0.0.0.0");
			
			/* Start by initializing my own object id. */
			ospf_my_objid = op_id_self ();
			
			op_ima_obj_attr_get(op_topo_parent (ospf_my_objid), "name", router_name);
			
			/* Set the IP multicast addresses that OSPF uses. */
			OspfI_All_Spf_Routers = ip_address_create ("224.0.0.5");
			OspfI_All_Dr_Routers = ip_address_create ("224.0.0.6");
			
			/* Also add a reference to my own prohandle. */
			ospf_my_prohandle = op_pro_self ();
			
			/* Get the name of the process model. */
			op_ima_obj_attr_get (ospf_my_objid, "process model", proc_model_name);
			
			/* Register the process in the model-wide registry. */
			ospf_my_proc_reg_handle = (OmsT_Pr_Handle) oms_pr_process_register (op_topo_parent (ospf_my_objid),
				ospf_my_objid, ospf_my_prohandle, proc_model_name);
			
			/* Create a router data structure. */
			ospf_router_ptr = ospf_router_create (ospf_my_prohandle, ospf_my_proc_reg_handle);
			
			/* Read the routing table interval. */
			op_ima_obj_attr_get (ospf_my_objid, "Routing Table Interval", &ospf_rte_table_interval);
			
			/* Set up the routing table. */
			ospf_router_ptr->rte_table_ptr = ospf_rte_table_create ();
			ospf_router_ptr->rte_table_ptr->dirty_flag = OPC_FALSE;
			
			/* Create a dynamic routing structure. */
			ospf_ip_dyn_rte_info_ptr = ip_dyn_rte_info_create ();
			
			/* Fill in the appropriate fields. */
			ospf_ip_dyn_rte_info_ptr->table_handle = ospf_router_ptr->rte_table_ptr;
			ospf_ip_dyn_rte_info_ptr->lookup_proc = ospf_rte_table_lookup;
			ospf_ip_dyn_rte_info_ptr->fast_addr_proc = ospf_rte_table_fast_addr_get;
			
			/* Register the protocol attribute and dynamic routing infomration in the registry. */
			oms_pr_attr_set (ospf_my_proc_reg_handle,
				"protocol", 			OMSC_PR_STRING, 	"ospf",
				"routing information", 	OMSC_PR_ADDRESS,	ospf_ip_dyn_rte_info_ptr,
				OPC_NIL);
			
			/* Perform global statistic initializations. */
			if (!OspfI_Global_Init)
				{
				OspfI_Global_Load_Stat = op_stat_reg ("Global OSPF Load (bits/sec)", OPC_STAT_INDEX_NONE, OPC_STAT_GLOBAL);
				OspfI_Global_Bits_Accum = 0;
				OspfI_Converge_Time = 0.0;
				OspfI_Converge_Time_Written = OPC_FALSE;
				OspfI_Global_Init = OPC_TRUE;
				}
			
			}


		/** blocking after enter executives of unforced state. **/
		FSM_EXIT (1,ospf)


		/** state (Init) exit executives **/
		FSM_STATE_EXIT_UNFORCED (0, state0_exit_exec, "Init", "ospf () [Init exit execs]")
			{
			/* If endsim woke us up, then perform no more initializations. */
			if (op_intrpt_type () != OPC_INTRPT_ENDSIM)
				{
			    
			    fprintf (fp, " inside opsf exec\n");
				/* Record the fact that OSPF has started. */
				ospf_start = OPC_TRUE;
			
			    /* Register these addresses with IP, so IP knows to accept them. */
				ip_address_multicast_register (OspfI_All_Spf_Routers, op_topo_parent (ospf_my_objid));
				ip_address_multicast_register (OspfI_All_Dr_Routers, op_topo_parent (ospf_my_objid));
			
			    /* Initialize the rest of the state variables. */
				ospf_sv_init (ospf_router_ptr);
				
			    /* Following code added by Xin Wang for ospf to get access to higher layer Mcast protocol */
			    /* OSPF needs to inform Mcast model it has finished interface table construction          */
			    /* It also needs to inform the routing table change in the future                         */
			
			    proc_record_handle_list_ptr = op_prg_list_create();
			
			
			    oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr, 
							"protocol", 	OMSC_PR_STRING, "pim_dm",
							"node objid", 	OMSC_PR_OBJID, 	op_topo_parent (ospf_my_objid), 
				             OPC_NIL);
				 
			
				record_handle_list_size = op_prg_list_size (proc_record_handle_list_ptr);
			      //printf (" %s list size = %d +++=======\n", router_name, record_handle_list_size);
				if (record_handle_list_size == 0)
					{
					/*	An error should be created if there are more than	*/
					/*	one dynamic mcast routing process in the local node */
					op_sim_end ("Error: either zero or several mcast dynamic routing proceses in the local node", "", "", "");
					}
				else
					{
					process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, 
										OPC_LISTPOS_HEAD);
			
			         /*	Obtain the object id of the module performing the	*/
					/*	 mcast dynamic routing.							    */
					oms_pr_attr_get (process_record_handle, "module objid", OMSC_PR_OBJID, &mcast_dyn_rte_objid);
					}
			
			    	 
				/*	Deallocate the list pointer.							*/
				op_prg_mem_free (proc_record_handle_list_ptr);	
			
				/*	Schedule a remote interrupt to the local mcast dynamic		*/
				/*	routing protocol process to notify it that the IP		*/
				/*	interface table can now be accessed.					*/
			    //printf ("*****send remote intrt to PIM ******\n");
				op_intrpt_schedule_remote (op_sim_time (), 0, mcast_dyn_rte_objid);
				
			    //printf ("send remote interrupt in ospf ++++ \n");
				/* Schedule the first routing table calculation. */
			
			     proc_record_handle_list_ptr = op_prg_list_create();
			
			
			    oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr, 
							"protocol", 	OMSC_PR_STRING, "igmprouter",
							"node objid", 	OMSC_PR_OBJID, 	op_topo_parent (ospf_my_objid), 
				             OPC_NIL);
			
				record_handle_list_size = op_prg_list_size (proc_record_handle_list_ptr);
			      //printf (" %s list size = %d +++=======\n", router_name, record_handle_list_size);
				if (record_handle_list_size == 0)
					{
					/*	An error should be created if there are more than	*/
					/*	one dynamic mcast routing process in the local node */
					op_sim_end ("Error: either zero or several igmprouter", "", "", "");
					}
				else
					{
					process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, 
										OPC_LISTPOS_HEAD);
			
			         /*	Obtain the object id of the module performing the	*/
					/*	 mcast dynamic routing.							    */
					oms_pr_attr_get (process_record_handle, "module objid", OMSC_PR_OBJID, &igmprouter_objid);
					}
			
			    	 
				/*	Deallocate the list pointer.							*/
				op_prg_mem_free (proc_record_handle_list_ptr);	
			
				/*	Schedule a remote interrupt to the local  igmprouter    */
				/*	routing protocol process to notify it that the IP		*/
				/*	interface table can now be accessed.					*/
			    //printf ("*****send remote intrt to igmp******\n");
				op_intrpt_schedule_remote (op_sim_time (), 0, igmprouter_objid);
				
			    //printf ("send remote interrupt in ospf ++++ \n");
				/* Schedule the first routing table calculation. */
			
				op_intrpt_schedule_self (op_sim_time () + ospf_rte_table_interval, 0);
				}
			}


		/** state (Init) transition processing **/
		FSM_INIT_COND (END_SIM)
		FSM_DFLT_COND
		FSM_TEST_LOGIC ("Init")

		FSM_TRANSIT_SWITCH
			{
			FSM_CASE_TRANSIT (0, 4, state4_enter_exec, ;)
			FSM_CASE_TRANSIT (1, 1, state1_enter_exec, ;)
			}
		/*---------------------------------------------------------*/



		/** state (Idle) enter executives **/
		FSM_STATE_ENTER_UNFORCED (1, state1_enter_exec, "Idle", "ospf () [Idle enter execs]")
			{
			}


		/** blocking after enter executives of unforced state. **/
		FSM_EXIT (3,ospf)


		/** state (Idle) exit executives **/
		FSM_STATE_EXIT_UNFORCED (1, state1_exit_exec, "Idle", "ospf () [Idle exit execs]")
			{
			}


		/** state (Idle) transition processing **/
		FSM_INIT_COND (MESSAGE_RECEIVED)
		FSM_TEST_COND (ROUTING_TABLE_CALC)
		FSM_TEST_COND (END_SIM)
		FSM_TEST_LOGIC ("Idle")

		FSM_TRANSIT_SWITCH
			{
			FSM_CASE_TRANSIT (0, 2, state2_enter_exec, ;)
			FSM_CASE_TRANSIT (1, 3, state3_enter_exec, ;)
			FSM_CASE_TRANSIT (2, 4, state4_enter_exec, ;)
			}
		/*---------------------------------------------------------*/



		/** state (Message Process) enter executives **/
		FSM_STATE_ENTER_FORCED (2, state2_enter_exec, "Message Process", "ospf () [Message Process enter execs]")
			{
			/* This state handles incoming OSPF messages, and retrieves */
			/* the interface address the message arrived on.            */
			
			ospf_message_ici_ptr = op_intrpt_ici ();
			
			if (ospf_message_ici_ptr == OPC_NIL)
				ospf_fatal_error ("Unable to retrieve lower-layer ICI associated with incoming OSPF message.");
			
			/* Get the message. */
			ospf_message_ptr = op_pk_get (op_intrpt_strm ());
			
			/* Get the interface this message arrived on. */
			op_ici_attr_get (ospf_message_ici_ptr, "interface_received", &ospf_message_intf_addr);
			
			/* Get the source address of the message. */
			op_ici_attr_get (ospf_message_ici_ptr, "src_addr", &ospf_message_src_addr);
			
			/* Dispatch the message to the appropriate child processes. */
			ospf_root_message_dispatch (ospf_message_ptr, ospf_message_intf_addr);
			}


		/** state (Message Process) exit executives **/
		FSM_STATE_EXIT_FORCED (2, state2_exit_exec, "Message Process", "ospf () [Message Process exit execs]")
			{
			}


		/** state (Message Process) transition processing **/
		FSM_TRANSIT_FORCE (1, state1_enter_exec, ;)
		/*---------------------------------------------------------*/



		/** state (Routing Table) enter executives **/
		FSM_STATE_ENTER_FORCED (3, state3_enter_exec, "Routing Table", "ospf () [Routing Table enter execs]")
			{
			/* Calculate routing table. */
			ospf_rte_table_calc (ospf_router_ptr);
			
			/*printf ("%s~~~~~~~~~~~~~~~~~\n", router_name);*/
			/*DEBUG Xin Wang */
			/*ospf_rte_table_print (ospf_router_ptr->rte_table_ptr);*/
			
			/* Schedule next calculation. */
			op_intrpt_schedule_self (op_sim_time () + ospf_rte_table_interval, 0);
			}


		/** state (Routing Table) exit executives **/
		FSM_STATE_EXIT_FORCED (3, state3_exit_exec, "Routing Table", "ospf () [Routing Table exit execs]")
			{
			}


		/** state (Routing Table) transition processing **/
		FSM_TRANSIT_FORCE (1, state1_enter_exec, ;)
		/*---------------------------------------------------------*/



		/** state (EndSim) enter executives **/
		FSM_STATE_ENTER_UNFORCED (4, state4_enter_exec, "EndSim", "ospf () [EndSim enter execs]")
			{
			if (!OspfI_Converge_Time_Written && ospf_start)
				{
				/* Write out convergence time statistic. */
				op_stat_scalar_write ("Network Convergence Time", OspfI_Converge_Time);
				OspfI_Converge_Time_Written = OPC_TRUE;
				}
			}


		/** blocking after enter executives of unforced state. **/
		FSM_EXIT (9,ospf)


		/** state (EndSim) exit executives **/
		FSM_STATE_EXIT_UNFORCED (4, state4_exit_exec, "EndSim", "ospf () [EndSim exit execs]")
			{
			}


		/** state (EndSim) transition processing **/
		FSM_TRANSIT_MISSING ("EndSim")
		/*---------------------------------------------------------*/



		}


	FSM_EXIT (0,ospf)
	}




void
ospf_svar (prs_ptr,var_name,var_p_ptr)
	ospf_state		*prs_ptr;
	char			*var_name, **var_p_ptr;
	{

	FIN (ospf_svar (prs_ptr))

	*var_p_ptr = VOS_NIL;
	if (Vos_String_Equal ("fp" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_fp);
	if (Vos_String_Equal ("ospf_area_list_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_area_list_ptr);
	if (Vos_String_Equal ("ospf_backbone_area_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_backbone_area_ptr);
	if (Vos_String_Equal ("ospf_router_id" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_router_id);
	if (Vos_String_Equal ("ospf_my_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_my_objid);
	if (Vos_String_Equal ("ospf_my_prohandle" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_my_prohandle);
	if (Vos_String_Equal ("ospf_my_proc_reg_handle" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_my_proc_reg_handle);
	if (Vos_String_Equal ("mcast_dyn_rte_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_mcast_dyn_rte_objid);
	if (Vos_String_Equal ("igmprouter_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_igmprouter_objid);
	if (Vos_String_Equal ("router_name" , var_name))
		*var_p_ptr = (char *) (prs_ptr->sv_router_name);
	if (Vos_String_Equal ("ospf_message_intf_addr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_message_intf_addr);
	if (Vos_String_Equal ("ospf_message_src_addr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_message_src_addr);
	if (Vos_String_Equal ("ospf_message_ici_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_message_ici_ptr);
	if (Vos_String_Equal ("ospf_message_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_message_ptr);
	if (Vos_String_Equal ("ospf_router_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_router_ptr);
	if (Vos_String_Equal ("ospf_router_ip_outstrm" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_router_ip_outstrm);
	if (Vos_String_Equal ("ospf_ip_dyn_rte_info_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_ip_dyn_rte_info_ptr);
	if (Vos_String_Equal ("ospf_dbase_last_lsa_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_dbase_last_lsa_ptr);
	if (Vos_String_Equal ("ospf_rte_table_interval" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_rte_table_interval);
	if (Vos_String_Equal ("ospf_start" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_start);

	FOUT;
	}




void
ospf_diag ()
	{
	char				proc_model_name [128];
	List*			    proc_record_handle_list_ptr;
	int				    record_handle_list_size;
	OmsT_Pr_Handle	    process_record_handle;
	
	int _block_origin;

	FIN (ospf_diag ())

	BINIT
	if (op_prg_odb_ltrace_active ("ospf_summary") || 
		op_prg_odb_ltrace_active ("ospf_area_summary"))
		ospf_area_list_summary_print (ospf_area_list_ptr);
	
	if (op_prg_odb_ltrace_active ("ospf_summary") ||
		op_prg_odb_ltrace_active ("ospf_interface_summary"))
		ospf_area_list_interfaces_print (ospf_area_list_ptr, OPC_FALSE);
	
	if (op_prg_odb_ltrace_active ("ospf_interface_detailed"))
		ospf_area_list_interfaces_print (ospf_area_list_ptr, OPC_TRUE);
	
	if (op_prg_odb_ltrace_active ("ospf_ls_database"))
		ospf_area_list_lsa_print (ospf_area_list_ptr);
	
	if (op_prg_odb_ltrace_active ("ospf_rte_table"))
		ospf_rte_table_print (ospf_router_ptr->rte_table_ptr);
	
	if (op_prg_odb_ltrace_active ("ospf_area_config"))
		ospf_area_list_config_print (ospf_area_list_ptr);

	FOUT;
	}




void
ospf_terminate ()
	{
	char				proc_model_name [128];
	List*			    proc_record_handle_list_ptr;
	int				    record_handle_list_size;
	OmsT_Pr_Handle	    process_record_handle;
	
	int _block_origin;

	FIN (ospf_terminate ())

	BINIT

	FOUT;
	}



Compcode
ospf_init (pr_state_pptr)
	ospf_state			**pr_state_pptr;
	{
	int _block_origin = 0;
	static VosT_Cm_Obtype	obtype = OPC_NIL;

	extern int				Vos_Nop ();

	FIN (ospf_init (pr_state_pptr))

	if (obtype == OPC_NIL)
		{
		if (Vos_Catmem_Register ("proc state vars (ospf)",
			sizeof (ospf_state), Vos_Nop, &obtype) == VOSC_FAILURE)
			FRET (OPC_COMPCODE_FAILURE)
		}

	if ((*pr_state_pptr = (ospf_state*) Vos_Catmem_Alloc (obtype, 1)) == OPC_NIL)
		FRET (OPC_COMPCODE_FAILURE)
	else
		{
		(*pr_state_pptr)->current_block = 0;
		FRET (OPC_COMPCODE_SUCCESS)
		}
	}



enum { _block_origin = __LINE__ };
void
ospf_sv_init (OspfT_Router *router_ptr)
	{
	char				router_id_str [IPC_ADDR_STR_LEN];
	Objid				interface_table_objid;
	int					outstrm_count;
	Objid				outstrm_objid;
	int					num_intfs, intf_index;
	int					num_areas, area_index;
	OspfT_Area *		area_ptr;
	OspfT_Interface *	intf_ptr;
	IpT_Address			best_addr;
	Objid				area_config_objid;
	char				assign_router_id_str [IPC_ADDR_STR_LEN];

	/** Initialize state variables of OSPF root/dispatch process.      **/
	/** This function assumes that the state variable 'ospf_my_objid'  **/
	/** has already been set by the client.  Also initialize a router  **/
	/** object, which corresponds to the state/behavior of ospf_root.  **/
	FIN (ospf_sv_init (router_ptr));

	/* Get router ID from attribute. */
	op_ima_obj_attr_get (ospf_my_objid, "Router ID", router_id_str);
	
	/* Check for router ID auto-assignment. */
	if (!strcmp (router_id_str, OSPFC_ROUTER_ID_AUTO_ASSIGN))
		{
		sprintf (assign_router_id_str, "%d", OspfI_Auto_Router_Id++);
		ospf_router_id = ip_address_create (assign_router_id_str);
		}
	else
		ospf_router_id = ip_address_create (router_id_str);

	router_ptr->router_id = ip_address_copy (ospf_router_id);

	/* Get the outgoing stream index to IP. */
	outstrm_count = op_topo_assoc_count (ospf_my_objid, OPC_TOPO_ASSOC_OUT, OPC_OBJTYPE_STRM);

	/* Make sure there's only one outgoing stream from OSPF. */
	if (outstrm_count != 1)
		ospf_fatal_error ("OSPF has zero or several outgoing streams.");

	outstrm_objid = op_topo_assoc (ospf_my_objid, OPC_TOPO_ASSOC_OUT, OPC_OBJTYPE_STRM, 0);
	
	/* Retrieve the index of the stream. */
	op_ima_obj_attr_get (outstrm_objid, "src stream", &ospf_router_ip_outstrm);
	router_ptr->ip_outstrm = ospf_router_ip_outstrm;

	/* Get the object ID that represents the interface table. */
	op_ima_obj_attr_get (ospf_my_objid, "Interface Table", &interface_table_objid);
	
	/* Create the area list based on the interface table. */
	ospf_area_list_ptr = ospf_area_list_create (interface_table_objid, router_ptr);
    
    /* Xin added for debugging */
   /* printf("%s num_of_area = %d\n", router_name, op_prg_list_size (ospf_area_list_ptr));*/

	/* Create a reference to the backbone (if the router is connected to the backbone). */
	ospf_backbone_area_ptr = ospf_area_lookup (ospf_area_list_ptr, OspfI_Area_Id_Backbone);

	/* Configure the area address ranges. */
	op_ima_obj_attr_get (ospf_my_objid, "Area Configuration Table", &area_config_objid);
	ospf_router_area_config (area_config_objid, ospf_area_list_ptr);

	/* Publish the state variables through the process registry. */
	oms_pr_attr_set (router_ptr->proc_reg_handle,
		"area list",			OMSC_PR_ADDRESS,		&ospf_area_list_ptr,
		"backbone area",		OMSC_PR_ADDRESS,		&ospf_backbone_area_ptr,
		OPC_NIL);

	/* Last LSA kept by slave router during database exchange. */
	ospf_dbase_last_lsa_ptr = OPC_NIL;

	FOUT;
	}

/***** Area sub-package functions *****/
List *
ospf_area_list_create (Objid interface_table_objid, OspfT_Router *parent_router_ptr)
	{
	List *					area_list_ptr;
	int						num_interfaces, num_intf, interface_index, intf_index;
	Objid					interface_objid;
	char					area_id_str [IPC_ADDR_STR_LEN];
	OspfT_Area *			area_ptr;
	OspfT_Area_Id			area_id;
	OspfT_Interface *		interface_ptr;
	List *					proc_record_handle_list_ptr;
	IpT_Info *				ip_info_ptr = OPC_NIL;
	IpT_Interface_Info *	ip_interface_ptr;
	OmsT_Pr_Handle			ip_proc_record_handle;
	Boolean					intf_found;

	/** Create an area list based on the interface table specified by the user. **/
	/** This procedure is also responsible for creating the interface data      **/
	/** structures that reside in each area, which means creating and invoking  **/
	/** interface processes.                                                    **/
	FIN (ospf_area_list_create (interface_table_objid, parent_router_ptr));

	/* Create area list. */
	area_list_ptr = op_prg_list_create ();

	/* Loop through the sub-objects of the interface table (which represent */
	/* individual interfaces) and install each one.                         */
	num_interfaces = op_topo_child_count (interface_table_objid, OPC_OBJTYPE_GENERIC);

	proc_record_handle_list_ptr = op_prg_list_create ();
	oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr,
		"protocol", 	OMSC_PR_STRING, 	"ip",
		"node objid", 	OMSC_PR_OBJID, 		op_topo_parent (op_id_self ()),
		OPC_NIL);

	/* Flag an error if more than one IP module was found. */
	if (op_prg_list_size (proc_record_handle_list_ptr) != 1)
		{
		/* Having more than one IP module is a serious error.  End simulation. */
		op_sim_end ("Error: either zero or several IP processes found in the local node", 
			OPC_NIL, OPC_NIL, OPC_NIL);
		}

	/* If one IP module was found, then retrieve the IP interface information. */
	ip_proc_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr,
		OPC_LISTPOS_HEAD);
	oms_pr_attr_get (ip_proc_record_handle, 
		"interface information", OMSC_PR_ADDRESS, &ip_info_ptr);

	/* Deallocate the temporary list pointer. */
	op_prg_mem_free (proc_record_handle_list_ptr);

	for (interface_index = 0; interface_index < num_interfaces; interface_index++)
		{
		/* Once we have the IP interface information, use the interface index to associate */
		/* a member of the interface list with the interface we're working on. The         */
		/* interface index refers to order of sub-objects shown in the editor, but does    */
		/* not necessarily correspond to the order within the interface list given by IP.  */
		/* Match the interface index to the addr_index element of the interface.           */
		num_intf = op_prg_list_size (ip_info_ptr->ip_iface_table_ptr);
		intf_found = OPC_FALSE;
		for (intf_index = 0; intf_index < num_intf; intf_index++)
			{
			ip_interface_ptr = (IpT_Interface_Info *) op_prg_list_access (ip_info_ptr->ip_iface_table_ptr,
				intf_index);
		
			/* Check for a match. */
			if (ip_interface_ptr->addr_index == interface_index)
				{
				intf_found = OPC_TRUE;
				break;
				}
			}

		/* If the corresponding IP interface was not found (which would happen if */
		/* the interface was not actually connected to anything), then skip the   */
		/* creation of the interface object.                                      */
		if (!intf_found)
			continue;

		/* Get the current interface object. */
		interface_objid = op_topo_child (interface_table_objid, OPC_OBJTYPE_GENERIC, 
			interface_index);
		
		/* Get the current area ID. */
		op_ima_obj_attr_get (interface_objid, "Area ID", area_id_str);
		area_id = ip_address_create (area_id_str);

		/* Do we have this area yet? */
		area_ptr = ospf_area_lookup (area_list_ptr, area_id);
	
		if (area_ptr == OPC_NIL)
			{
			/* If not, then create a new one. */
			area_ptr = ospf_area_create ();

			/* Set the parent of the area. */
			area_ptr->parent_router_ptr = parent_router_ptr;

			/* And add it to the area list. */
			op_prg_list_insert (area_list_ptr, area_ptr, OPC_LISTPOS_TAIL);

			/* Set the area id. */
			area_ptr->area_id = area_id;
			}

		/* Create an interface based on the object id.  Note */
		/* that this will add the interface to the area.     */
		interface_ptr = ospf_interface_create (interface_objid, ip_interface_ptr, area_ptr, ospf_my_objid);
		}

	FRET (area_list_ptr);
	}

/***** Root sub-package *****/
void
ospf_root_message_dispatch (Packet *message_ptr, IpT_Address intf_addr)
	{
	char					intf_addr_str [IPC_ADDR_STR_LEN];
	char					src_addr_str [IPC_ADDR_STR_LEN];
	char					err_msg [256], msg_str [256];
	int						message_type;

	/** Process and dispatch the OSPF message to the appropriate child process. **/
	FIN (ospf_root_message_dispatch (message_ptr, intf_addr));

	/* Get type of message. */
	message_type = ospf_message_type_get (message_ptr, OPC_NIL);

	ip_address_print (intf_addr_str, intf_addr);
	ip_address_print (src_addr_str, ospf_message_src_addr);

	/* Dispatch the message according to type. */
	switch (message_type)
		{
		case OSPFC_MSG_TYPE_HELLO:
			{
			if (op_prg_odb_ltrace_active ("ospf_message_hello"))
				{
				sprintf (msg_str, "Hello message received on interface %s from %s", intf_addr_str,
					src_addr_str);
				op_prg_odb_print_minor (msg_str, OPC_NIL);
				}

			ospf_hello_message_rcvd (message_ptr, intf_addr);
			break;
			}

		case OSPFC_MSG_TYPE_DBASE_DESC:
			{
			if (op_prg_odb_ltrace_active ("ospf_message_dbase_desc"))
				{
				sprintf (msg_str, "Database Description message received on interface %s from %s", 
					intf_addr_str, src_addr_str);
				op_prg_odb_print_minor (msg_str, OPC_NIL);
				}

			ospf_dbase_desc_message_rcvd (message_ptr, intf_addr);
			break;
			}

		case OSPFC_MSG_TYPE_LSR:
			{
			if (op_prg_odb_ltrace_active ("ospf_message_ls_request"))
				{
				sprintf (msg_str, "Link State Request message received on interface %s from %s", 
					intf_addr_str, src_addr_str);
				op_prg_odb_print_minor (msg_str, OPC_NIL);
				}

			ospf_link_state_request_message_rcvd (message_ptr, intf_addr);
			break;
			}

		case OSPFC_MSG_TYPE_LSU:
			{
			if (op_prg_odb_ltrace_active ("ospf_message_ls_update"))
				{
				sprintf (msg_str, "Link State Update message received on interface %s from %s", intf_addr_str,
					src_addr_str);
				op_prg_odb_print_minor (msg_str, OPC_NIL);
				}

			ospf_link_state_update_message_rcvd (message_ptr, intf_addr);
			break;
			}

		case OSPFC_MSG_TYPE_LSACK:
			{
			if (op_prg_odb_ltrace_active ("ospf_message_ls_ack"))
				{
				sprintf (msg_str, "Link State Acknowledgement message received on interface %s from %s", 
					intf_addr_str, src_addr_str);
				op_prg_odb_print_minor (msg_str, OPC_NIL);
				}

			ospf_link_state_ack_message_rcvd (message_ptr, intf_addr);
			break;
			}

		default:
			{
			sprintf (err_msg, "OSPF message of unknown type received on interface %s from %s", 
				intf_addr_str, src_addr_str);
			ospf_fatal_error (err_msg);
			break;
			}
		}

	FOUT;
	}

OspfT_Router *
ospf_router_create (Prohandle proc_handle, OmsT_Pr_Handle proc_reg_handle)
	{
	OspfT_Router *			router_ptr;

	/** Allocate and initialize a router data structure. **/
	FIN (ospf_router_create (proc_handle, proc_reg_handle));

	/* Allocate memory for DS. */
	router_ptr = op_prg_mem_alloc (sizeof (OspfT_Router));

	/* Set DS members to reasonable values. */
	router_ptr->router_id = IpI_Default_Addr;
	router_ptr->proc_handle = proc_handle;
	router_ptr->proc_reg_handle = proc_reg_handle;
	router_ptr->load_stat = op_stat_reg ("OSPF Load (bits/sec)", OPC_STAT_INDEX_NONE, OPC_STAT_LOCAL);
	router_ptr->bits_accum = 0;

	/* Set the optional capabilities of the router.  This model does */
	/* not currently support TOS routing or external routes, so this */
	/* will remain at 0 for the time being.                          */
	router_ptr->options = 0;

	FRET (router_ptr);
	}

OspfT_Interface *
ospf_router_interface_find (IpT_Address intf_addr)
	{
	int						num_areas, area_index;
	OspfT_Area *			area_ptr;
	OspfT_Interface *		interface_ptr = OPC_NIL;

	/** Find and return an interface on this router that matches the address. **/
	/** Returns OPC_NIL if no interface on this router matches the address.   **/
	FIN (ospf_router_interface_find (intf_addr));

	/* Loop through the areas the router attaches to and try to find the matching interface within the area. */
    
    /* added by Xin for debugging */ 
    /*printf ("ospf_area_list_ptr = %p\n", ospf_area_list_ptr);
    num_areas = 1;                                             */
	
    num_areas = op_prg_list_size (ospf_area_list_ptr);

   /* printf ("num_areas = %d\n", num_areas);*/
	for (area_index = 0; area_index < num_areas; area_index++)
		{
		/* Get current area. */
		area_ptr = op_prg_list_access (ospf_area_list_ptr, area_index);

		/* See if the interface exists in the interface list of the area. */
		interface_ptr = ospf_interface_list_find (area_ptr->interface_list_ptr, intf_addr);

		if (interface_ptr != OPC_NIL)
			FRET (interface_ptr);
		}

	FRET (OPC_NIL);
	}

void
ospf_router_area_config (Objid area_config_objid, List *area_list_ptr)
	{
	Objid				addr_range_objid;
	int					num_ranges, range_index;
	char				area_id_str [IPC_ADDR_STR_LEN];
	OspfT_Area_Id 		area_id;
	OspfT_Area *		area_ptr;
	char				router_id_str [IPC_ADDR_STR_LEN];
	char				addr_str [IPC_ADDR_STR_LEN];
	char				subnet_mask_str [IPC_ADDR_STR_LEN];
	char				status_str [16];
	IpT_Address_Range *	addr_range_ptr;
	OspfT_Area_Range *	area_range_ptr;
	char				warn_msg [128];

	/** Configure the address ranges of the areas in the area list, based on **/
	/** the given Area Configuration Table.                                  **/
	FIN (ospf_router_area_config (area_config_objid, area_list_ptr));

	/* Get the number of configuration entries. */
	num_ranges = op_topo_child_count (area_config_objid, OPC_OBJTYPE_GENERIC);

	/* Loop through each range and add it to the appropriate range list in the given area. */
	for (range_index = 0; range_index < num_ranges; range_index++)
		{
		addr_range_objid = op_topo_child (area_config_objid, OPC_OBJTYPE_GENERIC,
			range_index);

		/* Get the area ID. */
		op_ima_obj_attr_get (addr_range_objid, "Area ID", area_id_str);
		area_id = ip_address_create (area_id_str);

		/* Do we have this area? */
		area_ptr = ospf_area_lookup (area_list_ptr, area_id);
		
		/* If this area doesn't exist, issue a warning, and move on to the next range. */
		if (area_ptr == OPC_NIL)
			{
			ip_address_print (router_id_str, ospf_router_id);
			ip_address_print (area_id_str, area_id);
			sprintf (warn_msg, "Error in area configuration, area %s does not exist on router %s",
				area_id_str, router_id_str);

			ospf_warn_error (warn_msg);

			continue;
			}

		/* We have the area, add the address range to the area. */
		op_ima_obj_attr_get (addr_range_objid, "Address", addr_str);
		op_ima_obj_attr_get (addr_range_objid, "Subnet Mask", subnet_mask_str);
		addr_range_ptr = ip_address_range_create (ip_address_create (addr_str),
			ip_address_create (subnet_mask_str));
		area_range_ptr = ospf_area_range_create ();
		area_range_ptr->addr_range_ptr = addr_range_ptr;

		/* Should the range be advertised, or hidden from other areas? */
		op_ima_obj_attr_get (addr_range_objid, "Status", status_str);

		if (!strcmp (status_str, "Advertise"))
			{
			/* The range should be advertised to other areas. */
			op_prg_list_insert (area_ptr->address_list_ptr, area_range_ptr, OPC_LISTPOS_TAIL);
			}
		else
			{
			/* The range should be hidden from other areas. */
			op_prg_list_insert (area_ptr->hidden_addr_list_ptr, area_range_ptr, OPC_LISTPOS_TAIL);
			}
		}

	FOUT;
	}

/***** Message Handling Procedures *****/
void
ospf_hello_message_rcvd (Packet *message_ptr, IpT_Address intf_addr)
	{
	OspfT_Interface *				intf_ptr;
	char							intf_addr_str [IPC_ADDR_STR_LEN];
	char							err_msg [256];
	IpT_Address						msg_network_mask;
	double							msg_hello_interval, msg_router_dead_interval;
	char							msg_network_mask_str [IPC_ADDR_STR_LEN];
	char							intf_network_mask_str [IPC_ADDR_STR_LEN];
	OspfT_Neighbor *				neighbor_ptr;
	OspfT_Router_Id					msg_router_id;
	OspfT_Router_Id					msg_dr, msg_backup_dr;
	int								msg_router_prio;
	List *							neighbor_id_list_ptr;
	int								nbr_id_index, num_nbr_ids;
	OspfT_Router_Id					nbr_id, self_router_id;
	Boolean							two_way_estab = OPC_FALSE;
	Boolean							backup_seen = OPC_FALSE, neighbor_change = OPC_FALSE;

	/** Process the hello message.  This procedure corresponds to section 10.5 of RFC 1247. **/
	FIN (ospf_hello_message_rcvd (message_ptr, intf_addr));

	/* Create string representation of address. */
	ip_address_print (intf_addr_str, intf_addr);

	/* First step is to retrieve the interface data structure that */
	/* corresponds to the interface the message was received on.   */
	intf_ptr = ospf_router_interface_find (intf_addr);

	/* Exit if we can't find the interface. */
	if (intf_ptr == OPC_NIL)
		{
		sprintf (err_msg, "Unable to find interface %s", intf_addr_str);
		ospf_fatal_error (err_msg);
		}

	/* Now that we found the interface, verify that the message gives */
	/* correct information about the interface.  If this information  */
	/* does not match the info on the interface received, drop the    */
	/* packet and issue a warning.  This indicates a misconfiguration */
	/* error.                                                         */
	
	/* Check network mask on multi-access network interfaces. */
	op_pk_nfd_get (message_ptr, "network mask", &msg_network_mask);
	if ((intf_ptr->interface_type != OspfC_Interface_Type_Pt_To_Pt) &&
		(!ip_address_equal (msg_network_mask, intf_ptr->interface_mask)))
		{
		ip_address_print (msg_network_mask_str, msg_network_mask);
		ip_address_print (intf_network_mask_str, intf_ptr->interface_mask);

		/* Give warning, drop packet, and finish processing. */
		sprintf (err_msg, "Hello packet received with inconsistent network mask: %s received on interface mask %s",
			msg_network_mask_str, intf_network_mask_str);
		ospf_warn_error (err_msg);
		op_pk_destroy (message_ptr);
		FOUT;
		}

	/* Check hello timer. */
	op_pk_nfd_get (message_ptr, "hello interval", &msg_hello_interval);
	if (intf_ptr->hello_interval != msg_hello_interval)
		{
		/* Give warning, drop packet, and finish processing. */
		sprintf (err_msg, "Hello packet received with inconsistent hello interval: %f received on interface with %f",
			msg_hello_interval, intf_ptr->hello_interval);
		ospf_warn_error (err_msg);
		op_pk_destroy (message_ptr);
		FOUT;
		}

	/* Check router dead interval. */
	op_pk_nfd_get (message_ptr, "dead interval", &msg_router_dead_interval);
	if (intf_ptr->router_dead_interval != msg_router_dead_interval)
		{	
		/* Give warning, drop packet, and finish processing. */
		sprintf (err_msg, "Hello packet received with inconsistent dead interval: %f received on interface with %f",
			msg_router_dead_interval, intf_ptr->router_dead_interval);
		ospf_warn_error (err_msg);
		op_pk_destroy (message_ptr);
		FOUT;
		}

	/* At this point, we should be checking the consistency of the E-bit in      */
	/* the packet; however, since external routes have not yet been implemented, */
	/* this check is not performed.                                              */

	/* Get the router ID of the neighbor. */
	op_pk_nfd_get (message_ptr, "router ID", &msg_router_id);

	/* Get the router priority of the neighbor. */
	op_pk_nfd_get (message_ptr, "router priority", &msg_router_prio);
	
	/* Now determine which neighbor this message came from.  The key we use to */
	/* search depends on the underlying interface.                             */
	if (intf_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt)
		neighbor_ptr = ospf_interface_neighbor_find (intf_ptr, msg_router_id);
	else
		{
		/* Use the source IP address of the message to search. */
		neighbor_ptr = ospf_interface_neighbor_find (intf_ptr, ospf_message_src_addr);

		if (neighbor_ptr != OPC_NIL)
			{
			/* For multi-access networks, set the router ID. */
			ospf_neighbor_router_id_set (neighbor_ptr, msg_router_id);
			}
		}
	
	/* If we didn't find a neighbor, then we need to create one. */
	if (neighbor_ptr == OPC_NIL)
		{
		/* Create a neighbor that is invoked with a Hello Received event */
		/* since that is what caused the neighbor to be created.         */
		neighbor_ptr = ospf_neighbor_create (ip_address_copy (ospf_message_src_addr),
			OspfC_Neighbor_Event_Hello_Rcvd, op_id_self (), msg_router_prio, intf_ptr);

		/* Add this neighbor to the interface list. */
		ospf_interface_neighbor_add (intf_ptr, neighbor_ptr);

		/* Set the router ID of the neighbor. */
		ospf_neighbor_router_id_set (neighbor_ptr, msg_router_id);
		}
	else
		{
		/* If we did find a neighbor, invoke it with the Hello Received message. */
		ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Hello_Rcvd);
		}

	/* Next, examine the hello packet itself for the list of neighbors */
	/* that the sending router sees.  If this router appears in the    */
	/* list, then execute the hello state machine with 2-Way Received, */
	/* otherwise execute with 1-Way Received.                          */
	neighbor_id_list_ptr = ospf_message_hello_nbr_list_get (message_ptr);

	/* Loop through the neighbor ID list and see if any match the current router ID. */
	num_nbr_ids = op_prg_list_size (neighbor_id_list_ptr);

	/* Create a reference to this router ID. */
	self_router_id = intf_ptr->parent_area_ptr->parent_router_ptr->router_id;

	for (nbr_id_index = 0; nbr_id_index < num_nbr_ids; nbr_id_index++)
		{
		nbr_id = (OspfT_Router_Id) op_prg_list_access (neighbor_id_list_ptr, nbr_id_index);
		
		if (ip_address_equal (nbr_id, self_router_id))
			{
			/* This establishes two-way communication. */
			ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Two_Way_Rcvd);
			two_way_estab = OPC_TRUE;
			break;
			}
		}

	/* If we haven't established a two-way connection, invoke the neighbor with */
	/* 1-Way Received, and stop processing the packet.                          */
	if (!two_way_estab)
		{
		ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_One_Way_Rcvd);
		op_pk_destroy (message_ptr);
		FOUT;
		}

	/* Compare the current neighbor priority with the priority specified in */
	/* the hello message.  If they're different, schedule a Neighbor Change */
	/* event for the interface (since a new DR might be in order).  Change  */
	/* the priority in the neighbor to match the message.                   */
	if (ospf_neighbor_priority_get (neighbor_ptr) != msg_router_prio)
		{
		ospf_neighbor_priority_set (neighbor_ptr, msg_router_prio);
		neighbor_change = OPC_TRUE;
		}

	/* Now let's see if this neighbor has declared itself to be designated */
	/* router, or if this neighbor has changed its mind about being        */
	/* (or not being) designated router.  In either case, the interface    */
	/* needs to run the designated router election.                        */
	op_pk_nfd_get (message_ptr, "designated router", &msg_dr);
	op_pk_nfd_get (message_ptr, "backup designated router", &msg_backup_dr);

	if (ip_address_equal (neighbor_ptr->ip_address, msg_dr) &&
		ip_address_equal (IpI_Default_Addr, msg_backup_dr) &&
		(ospf_interface_state_get (intf_ptr, OPC_NIL) == OspfC_Interface_State_Waiting))
		backup_seen = OPC_TRUE;
	else
		{
		if ((ip_address_equal (neighbor_ptr->ip_address, msg_dr) && 
    			!ip_address_equal (ospf_neighbor_dr_get (neighbor_ptr), neighbor_ptr->ip_address)) ||
			(!ip_address_equal (neighbor_ptr->ip_address, msg_dr) &&
				ip_address_equal (ospf_neighbor_dr_get (neighbor_ptr), neighbor_ptr->ip_address)))
			neighbor_change = OPC_TRUE;
		}

	/* If the hello message has a new idea of the designated */
	/* router, change the neighbor's idea of the DR.         */
	if (!ip_address_equal (ospf_neighbor_dr_get (neighbor_ptr), msg_dr))
		ospf_neighbor_dr_set (neighbor_ptr, msg_dr);

	/* Finally, check the backup designated router, and perform similar checks as above. */
	if (ip_address_equal (neighbor_ptr->ip_address, msg_backup_dr) &&
		(ospf_interface_state_get (intf_ptr, OPC_NIL) == OspfC_Interface_State_Waiting))
		backup_seen = OPC_TRUE;
	else
		{
		if ((ip_address_equal (neighbor_ptr->ip_address, msg_backup_dr) &&
			    !ip_address_equal (ospf_neighbor_backup_dr_get (neighbor_ptr), neighbor_ptr->ip_address)) ||
			(!ip_address_equal (neighbor_ptr->ip_address, msg_backup_dr) &&
				ip_address_equal (ospf_neighbor_backup_dr_get (neighbor_ptr), neighbor_ptr->ip_address)))
			neighbor_change = OPC_TRUE;
		}

	/* If the hello message has a new idea of the backup DR, */
	/* change the neighbor's idea of the backup DR.          */
	if (!ip_address_equal (ospf_neighbor_backup_dr_get (neighbor_ptr), msg_backup_dr))
		ospf_neighbor_backup_dr_set (neighbor_ptr, msg_backup_dr);

	/* Now handle the interface events. */
	if (backup_seen)
		ospf_interface_invoke (intf_ptr, OspfC_Interface_Event_Bkup_Seen);
	else if (neighbor_change)
		ospf_interface_invoke (intf_ptr, OspfC_Interface_Event_Nbr_Change);

	/* Done processing the message, so destroy it. */
	op_pk_destroy (message_ptr);

	FOUT;
	}

void
ospf_dbase_desc_message_rcvd (Packet *message_ptr, IpT_Address intf_addr)
	{
	char						intf_addr_str [IPC_ADDR_STR_LEN];	
	OspfT_Neighbor *			neighbor_ptr;
	OspfT_Interface *			intf_ptr;
	OspfT_Router *				self_router_ptr;
	OspfT_Router_Id				msg_router_id, self_router_id;
	OspfT_Neighbor_State 		nbr_state;
	int							msg_ims_bits;
	int							msg_seq_num, nbr_seq_num;
	int							msg_options;
	Boolean						accept_pk;
	Packet *					rxmt_msg_ptr;
	List *						dbase_sum_list_ptr;
	int							num_dbase_sum;
	List *						lsa_header_list_ptr;
	OspfT_Lsa_Header *			lsa_header_ptr;
	int							num_lsh, lsh_index;
	OspfT_Lsa *					lsa_ptr;
	List *						nbr_lsr_list_ptr;
	Packet *					reply_msg_ptr;
	char						err_msg [128];
	char						bits_str [16] = "";
	char						trace_msg [128];

	/** Process the database description message.  This procedure corresponds to **/
	/** section 10.6 of RFC 1247.                                                **/
	FIN (ospf_dbase_desc_message_rcvd (message_ptr, intf_addr));

	/* Create a string representation of address. */
	ip_address_print (intf_addr_str, intf_addr);

	/* Retrieve the interface data structure that corresponds to the */
	/* interface the message was received on.                        */
	intf_ptr = ospf_router_interface_find (intf_addr);

	/* Exit if we can't find the interface. */
	if (intf_ptr == OPC_NIL)
		{
		sprintf (err_msg, "Unable to find interface %s", intf_addr_str);
		ospf_fatal_error (err_msg);
		}

	/* Create a reference to this router's ID. */
	self_router_ptr = ospf_interface_parent_router_get (intf_ptr);
	self_router_id = self_router_ptr->router_id;

	/* Get the router ID from the message header. */
	op_pk_nfd_get (message_ptr, "router ID", &msg_router_id);

	/* Get the sequence number from the message itself. */
	op_pk_nfd_get (message_ptr, "sequence number", &msg_seq_num);

	/* Get the Init, More and Master bits from the message. */
	op_pk_nfd_get (message_ptr, "IMS bits", &msg_ims_bits);

	/* Issue trace message. */
	if (op_prg_odb_ltrace_active ("ospf_message_dbase_desc"))
		{
		if (msg_ims_bits & OSPFC_MSG_DBASE_DESC_INIT_SET)
			strcat (bits_str, "I ");
		
		if (msg_ims_bits & OSPFC_MSG_DBASE_DESC_MORE_SET)
			strcat (bits_str, "M ");

		if (msg_ims_bits & OSPFC_MSG_DBASE_DESC_MASTER_SET)
			strcat (bits_str, "M/S");

		if (msg_ims_bits == 0)
			strcat (bits_str, "<None>");

		sprintf (trace_msg, "<DD Sequence Num = %d, Bits = %s>", msg_seq_num, bits_str);
		op_prg_odb_print_minor (trace_msg, OPC_NIL);
		}

	/* Get the optional capabilities of the router. */
	op_pk_nfd_get (message_ptr, "options", &msg_options);

	/* Now search for the associated neighbor. The search key is based on the */
	/* underlying interface: use the source IP address for multi-access       */
	/* networks, use the router ID for point-to-point links.                  */
	if (intf_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt)
		neighbor_ptr = ospf_interface_neighbor_find (intf_ptr, msg_router_id);
	else
		neighbor_ptr = ospf_interface_neighbor_find (intf_ptr, ospf_message_src_addr);

	/* We should find the neighbor at this point, since database descriptions */
	/* should only be sent on forming adjacencies.                            */
	if (neighbor_ptr == OPC_NIL)
		{
		/* Implementation problem, so abort the simulation. */
		sprintf (err_msg, "Unable to find neighbor for Database Description received on interface %s",
			intf_addr_str);
		ospf_fatal_error (err_msg);
		}

	/* Now process the packet.  The processing depends on the state of the neighbor. */
	nbr_state = ospf_neighbor_state_get (neighbor_ptr, OPC_NIL);

	/* Get current neighbor sequence number. */
	nbr_seq_num = ospf_neighbor_seq_num_get (neighbor_ptr);

	/* In 'Init', just invoke 2-Way Received and continue processing. */
	if (nbr_state == OspfC_Neighbor_State_Init)
		{
		ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Two_Way_Rcvd);
		
		/* Re-query for the neighbor state, since it should have */
		/* changed as a result of the above invocation.          */
		nbr_state = ospf_neighbor_state_get (neighbor_ptr, OPC_NIL);
		}

	/* In '2-Way', ignore the packet.  Don't issue a warning or */
	/* error, since this is not an unexpected event.            */
	if (nbr_state == OspfC_Neighbor_State_Two_Way)
		{
		op_pk_destroy (message_ptr);
		FOUT;
		}

	/* In 'ExStart', see if we've negotiated master/slave status with */
	/* the neighbor.  If so, invoke NegotiationDone.                  */
	if (nbr_state == OspfC_Neighbor_State_Ex_Start)
		{
		/* Check two cases.  If we've received the initial 'blank' Database Description */
		/* from a neighbor whose router ID is higher, then this router becomes the      */
		/* Slave.  If the received packet contains a sequence number equal to the       */
		/* sequence number defined in this neighbor structure, this is treated as an    */
		/* acknowledgement by a neighbor who has become a Slave.  In this case, this    */
		/* router becomes Master.                                                       */

		if ((msg_ims_bits & OSPFC_MSG_DBASE_DESC_INIT_SET) &&
			(msg_ims_bits & OSPFC_MSG_DBASE_DESC_MORE_SET) &&
			(msg_ims_bits & OSPFC_MSG_DBASE_DESC_MASTER_SET) &&
			(op_pk_fd_max_index (message_ptr) == (OSPFC_DBASE_DESC_NUM_STATIC_FIELDS - 1)) &&
			(ip_address_to_int (msg_router_id) > ip_address_to_int (self_router_id)))
			{
			/* In this case, the router declares itself as slave. */
			ospf_neighbor_master_status_set (neighbor_ptr, OPC_FALSE);

			/* Since this router is the slave, match the neighbor structure's */
			/* sequence number to that of the received packet.                */
			ospf_neighbor_seq_num_set (neighbor_ptr, msg_seq_num);

			/* Set the optional capabilities of the neighbor. */
			neighbor_ptr->options = msg_options;

			/* Cancel any pending transmissions once we're ready to start exchange. */
			if (op_ev_valid (neighbor_ptr->rxmt_timer))
				op_ev_cancel (neighbor_ptr->rxmt_timer);
			
			/* Master/Slave negotiation is done. */
			ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Neg_Done);
			}
		else if (!(msg_ims_bits & OSPFC_MSG_DBASE_DESC_INIT_SET) &&
			!(msg_ims_bits & OSPFC_MSG_DBASE_DESC_MASTER_SET) &&
			(msg_seq_num == nbr_seq_num) &&
			(ip_address_to_int (msg_router_id) < ip_address_to_int (self_router_id)))
			{
			/* In this case, the router declares itself as master. */
			ospf_neighbor_master_status_set (neighbor_ptr, OPC_TRUE);

			/* Set the optional capabilities of the neighbor. */
			op_pk_nfd_get (message_ptr, "options", &neighbor_ptr->options);

			/* In this case, a neighbor has acknowledged a previous packet sent */
			/* by this router.  Cancel the retransmission timer.                */
			op_ev_cancel (neighbor_ptr->rxmt_timer);

			/* Master/Slave negotiation is done. */
			ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Neg_Done);
			}
		else 
			{
			/* If we haven't met one of the above sets of conditions, simply ignore the packet. */
			op_pk_destroy (message_ptr);
			FOUT;
			}
		}

	dbase_sum_list_ptr = ospf_neighbor_dbase_summary_list_get (neighbor_ptr);
	num_dbase_sum = op_prg_list_size (dbase_sum_list_ptr);

	/* In 'Exchange', we are in the process of exchanging link state   */
	/* through the database descriptions.  Just make sure the received */
	/* message contains the correct sequence numbers, bits set, etc.   */
	if (nbr_state == OspfC_Neighbor_State_Exchange)
		{
		/* If the Init bit is set (it shouldn't be), generate Seq Number Mismatch */
		/* and stop processing the packet.                                        */
		if (msg_ims_bits & OSPFC_MSG_DBASE_DESC_INIT_SET)
			{
			ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Seq_Num_Mismatch);
			op_pk_destroy (message_ptr);
			FOUT;
			}

		/* If the packet options fields are different than what had previously */
		/* been set in the neighbor structure, generate Seq Number Mismatch    */
		/* and stop processing the packet.                                     */
		if (msg_options != neighbor_ptr->options)
			{
			ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Seq_Num_Mismatch);
			op_pk_destroy (message_ptr);
			FOUT;
			}

		/* Now check the sequence number, and see if it matches our expectations, */
		/* which depend on our master/slave relationship with the neighbor.       */
		accept_pk = OPC_FALSE;
		if (ospf_neighbor_master_status_get (neighbor_ptr))
			{
			/* Router is master.  Check the sequence number. */
			if (msg_seq_num == nbr_seq_num)
				{
				/* The slave has sent the proper sequence number, so accept the packet. */
				accept_pk = OPC_TRUE;
				}
			else if (msg_seq_num == (nbr_seq_num - 1))
				{
				/* This packet is a duplicate, so discard it. */
				op_pk_destroy (message_ptr);
				FOUT;
				}
			}
		else
			{
			/* Router is slave.  Check the sequence number. */
			if (msg_seq_num == (nbr_seq_num + 1))
				{
				/* The master has sent the proper sequence number, so accept the packet. */
				accept_pk = OPC_TRUE;
				}
			else if (msg_seq_num == nbr_seq_num)
				{
				/* This packet is a duplicate.  In this case, a slave responds */
				/* by repeating the last packet that it sent.                  */
				rxmt_msg_ptr = ospf_message_dbase_desc_create (neighbor_ptr, OSPFC_MSG_DBASE_DESC_MORE_SET);

				if (num_dbase_sum > 0)
					{
					lsa_ptr = op_prg_list_access (dbase_sum_list_ptr, OPC_LISTPOS_HEAD);
					ospf_message_lsa_header_add (rxmt_msg_ptr, lsa_ptr->lsa_header_ptr);
					}

				ospf_neighbor_message_send (neighbor_ptr, rxmt_msg_ptr, OPC_FALSE);

				op_pk_destroy (message_ptr);
				FOUT;
				}
			}

		/* If none of the above sequence number scenarios match the current situation, */
		/* generate Seq Number Mismatch and stop processing the packet.  If the packet */
		/* was accepted, then it is an implicit acknowledgement of the last database   */
		/* description sent.  Remove a database summary if there are any left.         */
		if (accept_pk)
			{
			if (num_dbase_sum > 0)
				op_prg_list_remove (dbase_sum_list_ptr,	OPC_LISTPOS_HEAD);

			/* Also need to cancel the retransmission timer, if master. */
			if (ospf_neighbor_master_status_get (neighbor_ptr))
				op_ev_cancel (neighbor_ptr->rxmt_timer);
			}
		else
			{
			ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Seq_Num_Mismatch);
			op_pk_destroy (message_ptr);
			FOUT;
			}
		}

	num_dbase_sum = op_prg_list_size (dbase_sum_list_ptr);

	/* In 'Loading' or 'Full', the Database Description exchange should have been finished. */
	/* Packets received in this state should be duplicates; any others should generate the  */
	/* Seq Number Mismatch event.  Masters should discard duplicates, slaves should send    */
	/* last Database Description packet.  Note that the slave is only expected to hold on   */
	/* to the last packet for RouterDeadInterval seconds; reception after this time period  */
	/* will generate Seq Number Mismatch.                                                   */
	if ((nbr_state == OspfC_Neighbor_State_Loading) || (nbr_state == OspfC_Neighbor_State_Full))
		{
		/* The packet's advertised options field should match the options established  */
		/* for the neighbor, and the Init bit should not be set.  If the options don't */
		/* match, or if the Init bit is set, then generate Seq Number Mismatch.        */
		if ((msg_options != neighbor_ptr->options) ||
			(msg_ims_bits & OSPFC_MSG_DBASE_DESC_INIT_SET))
			{
			ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Seq_Num_Mismatch);
			op_pk_destroy (message_ptr);
			FOUT;
			}

		if (ospf_neighbor_master_status_get (neighbor_ptr))
			{
			/* Router is master, check for a duplicate. */
			if (msg_seq_num == (nbr_seq_num - 1))	
				{
				/* Discard duplicates. */
				op_pk_destroy (message_ptr);
				FOUT;
				}
			}
		else
			{
			/* Router is slave, check for a duplicate. */
			if (msg_seq_num == nbr_seq_num)
				{
				/* Resend the last packet if we're still within the RouterDeadInterval */
				/* time window, otherwise, generate Seq Number Mismatch.  We'll know   */
				/* whether or not we're in the time window by checking the state of    */
				/* the last packet to be sent.                                         */
				if (ospf_dbase_last_lsa_ptr == OPC_NIL)
					{
					/* This is past the timeout window, so simply generate Seq Number Mismatch. */
					ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Seq_Num_Mismatch);
					op_pk_destroy (message_ptr);
					FOUT;
					}
				else 
					{
					/* We're still within the timeout window, so resend the packet. */
					rxmt_msg_ptr = ospf_message_dbase_desc_create (neighbor_ptr, 0);

					ospf_message_lsa_header_add (rxmt_msg_ptr, ospf_dbase_last_lsa_ptr->lsa_header_ptr);
					ospf_neighbor_message_send (neighbor_ptr, rxmt_msg_ptr, OPC_FALSE);

					op_pk_destroy (message_ptr);
					FOUT;
					}
				}
			}
		
		/* Any other cases, generate Seq Num Mismatch. */
		ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Seq_Num_Mismatch);
		op_pk_destroy (message_ptr);
		FOUT;
		}

	/* At this point, if we have not yet exited, the message has been deemed */
	/* acceptable for processing.  At this point we process the set of Link  */
	/* State Headers contained within the message.                           */
	lsa_header_list_ptr = ospf_message_lsa_header_list_get (message_ptr);
	num_lsh = op_prg_list_size (lsa_header_list_ptr);
	nbr_lsr_list_ptr = ospf_neighbor_ls_request_list_get (neighbor_ptr);

	for (lsh_index = 0; lsh_index < num_lsh; lsh_index++)
		{
		lsa_header_ptr = (OspfT_Lsa_Header *) op_prg_list_access (lsa_header_list_ptr, lsh_index);

		/* Verify that we have a usable LS type.  Currently this model does not support */
		/* LSA types 4 and 5, so receiving a header with these types is an error.       */
		if ((lsa_header_ptr->type < OspfC_Lsa_Router_Links) || 
			(lsa_header_ptr->type > OspfC_Lsa_Summary_Links))
			ospf_fatal_error ("Received unknown LS type in Database Description.");

		/* Look for a matching LSA in the database. */
		lsa_ptr = ospf_lsa_area_lookup (intf_ptr->parent_area_ptr, lsa_header_ptr->link_state_id,
			lsa_header_ptr->router_id, lsa_header_ptr->type);

		/* If we didn't find the LSA, or the incoming header is more recent, place the */
		/* header in the Link State Request list.                                      */
		if ((lsa_ptr == OPC_NIL) ||
			(ospf_lsa_compare (lsa_header_ptr, lsa_ptr->lsa_header_ptr) == 1))
			op_prg_list_insert (nbr_lsr_list_ptr, lsa_header_ptr, OPC_LISTPOS_TAIL);
		}

	/* The last part in Database Description processing is sending a reply. */
	/* A different reply is sent depending on the master/slave status.      */
	if (ospf_neighbor_master_status_get (neighbor_ptr))
		{
		/* Router is master, so increment the sequence number and send the next */
		/* header in the database summary list.  If no more headers are to be   */
		/* sent, and the message received does not have the More bit set        */
		/* (indicating no more LSA headers are forthcoming), then generate      */
		/* Exchange Done.                                                       */
		ospf_neighbor_seq_num_set (neighbor_ptr, ++nbr_seq_num);

		if ((num_dbase_sum == 0) && !(msg_ims_bits & OSPFC_MSG_DBASE_DESC_MORE_SET))
			{
			/* The database exchange is done, so generate Exchange Done. */
			ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Exchg_Done);
			}
		else if (num_dbase_sum == 0)
			{
			/* The slave hasn't finished sending the database descriptions, so */
			/* just send a blank Database Description in reply.                */
			reply_msg_ptr = ospf_message_dbase_desc_create (neighbor_ptr, 0);
			ospf_neighbor_message_send (neighbor_ptr, reply_msg_ptr, OPC_TRUE);
			}
		else
			{
			/* The master still has database descriptions to send, so send the */
			/* next LSA in the summary list.                                   */
			if (num_dbase_sum == 1)
				reply_msg_ptr = ospf_message_dbase_desc_create (neighbor_ptr, 0);
			else
				reply_msg_ptr = ospf_message_dbase_desc_create (neighbor_ptr, OSPFC_MSG_DBASE_DESC_MORE_SET);
			
			lsa_ptr = op_prg_list_access (dbase_sum_list_ptr, OPC_LISTPOS_HEAD);
			ospf_message_lsa_header_add (reply_msg_ptr, lsa_ptr->lsa_header_ptr);
			ospf_neighbor_message_send (neighbor_ptr, reply_msg_ptr, OPC_TRUE);
			}
		}
	else
		{
		/* Router is slave, so set the sequence number to that which was set in */
		/* the message.  If the message that we received does not have the More */
		/* bit set (indicating no more LSA headers are forthcoming), and the    */
		/* slave has no more headers to send, generate the Exchange Done event. */
		/* Otherwise, send a database description.                              */
		ospf_neighbor_seq_num_set (neighbor_ptr, msg_seq_num);

		if ((num_dbase_sum == 1) || (num_dbase_sum == 0))
			{
			/* We're down to our last LSA.  Make sure we save the last LSA since */
			/* it is sent in response to duplicates in the 'Loading' and 'Full'  */
			/* states.  If the master has also finished (i.e. the More bit in    */
			/* the message is not set), then generate Exchange Done, and set up  */
			/* a timer to free the last LSA after RouterDeadInterval seconds.    */
			reply_msg_ptr = ospf_message_dbase_desc_create (neighbor_ptr, 0);

			if (num_dbase_sum == 1)
				{
				/* If a summary is remaining, add it to the message. */
				lsa_ptr = op_prg_list_access (dbase_sum_list_ptr, OPC_LISTPOS_HEAD);
				ospf_dbase_last_lsa_ptr = lsa_ptr;
				ospf_message_lsa_header_add (reply_msg_ptr, lsa_ptr->lsa_header_ptr);
				}

			ospf_neighbor_message_send (neighbor_ptr, reply_msg_ptr, OPC_FALSE);

			if (!(msg_ims_bits & OSPFC_MSG_DBASE_DESC_MORE_SET))
				{
				/* We need to hang on to the last header for RouterDeadInterval seconds. */
				/* It will be sent in response to duplicates in the 'Loading' or 'Full'  */
				/* state.  Set up a timer to free the last header from the list.         */
				op_intrpt_schedule_call (op_sim_time () + 
					neighbor_ptr->parent_interface_ptr->router_dead_interval,
					0, ospf_neighbor_dbase_sum_free, neighbor_ptr);

				/* If there's a header left in the list, free it, since we don't */
				/* expect a response from the master for it.                     */
				if (num_dbase_sum == 1)
					op_prg_list_remove (dbase_sum_list_ptr, OPC_LISTPOS_HEAD);

				ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Exchg_Done);
				}
			}
		else 
			{
			/* Still at least one LSA header to send, so send it. */
			reply_msg_ptr = ospf_message_dbase_desc_create (neighbor_ptr, OSPFC_MSG_DBASE_DESC_MORE_SET);
			lsa_ptr = op_prg_list_access (dbase_sum_list_ptr, OPC_LISTPOS_HEAD);
			ospf_message_lsa_header_add (reply_msg_ptr, lsa_ptr->lsa_header_ptr);
			ospf_neighbor_message_send (neighbor_ptr, reply_msg_ptr, OPC_FALSE);
			}
		}

	/* Done processing the packet, so destroy it. */
	op_pk_destroy (message_ptr);

	FOUT;
	}

void
ospf_neighbor_dbase_sum_free (OspfT_Neighbor *neighbor_ptr, int intrpt_code)
	{
	/** Procedure to handle the freeing of the last database summary LSA. **/
	FIN (ospf_neighbor_dbase_sum_free (neighbor_ptr, intrpt_code));

	ospf_dbase_last_lsa_ptr = OPC_NIL;
	
	FOUT;
	}

void
ospf_link_state_request_message_rcvd (Packet *message_ptr, IpT_Address intf_addr)
	{
	char					intf_addr_str [IPC_ADDR_STR_LEN];
	OspfT_Interface *		intf_ptr;
	char					err_msg [128];
	int						num_fields;
	int						num_requests, request_index;
	OspfT_Lsa_Type			msg_lsa_type;
	IpT_Address				msg_link_id;
	OspfT_Router_Id			msg_adv_router_id;
	OspfT_Lsa *				lsa_ptr;
	OspfT_Area *			area_ptr;
	OspfT_Neighbor *		neighbor_ptr;
	OspfT_Router_Id			msg_router_id;
	Packet *				lsu_msg_ptr;
	OspfT_Neighbor_State	nbr_state;

	/** Handles an incoming Link State Request message.  **/
	/** Corresponds to section 10.7 of RFC 1247.         **/
	FIN (ospf_link_state_request_message_rcvd (message_ptr, intf_addr));

	/* Create a string representation of address. */
	ip_address_print (intf_addr_str, intf_addr);

	/* Retreive the interface data structure that corresponds to the */
	/* interface the message was received on.                        */
	intf_ptr = ospf_router_interface_find (intf_addr);

	/* Exit if we can't find the interface. */
	if (intf_ptr == OPC_NIL)
		{
		sprintf (err_msg, "Unable to find interface %s", intf_addr_str);
		ospf_fatal_error (err_msg);
		}

	/* Get the area associated with this message. */
	area_ptr = intf_ptr->parent_area_ptr;

	/* Get the router ID from the message header. */
	op_pk_nfd_get (message_ptr, "router ID", &msg_router_id);

	/* Now search for the associated neighbor. The search key is based on the */
	/* underlying interface: use the source IP address for multi-access       */
	/* networks, use the router ID for point-to-point links.                  */
	if (intf_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt)
		neighbor_ptr = ospf_interface_neighbor_find (intf_ptr, msg_router_id);
	else
		neighbor_ptr = ospf_interface_neighbor_find (intf_ptr, ospf_message_src_addr);

	/* Get the neighbor state. */
	nbr_state = ospf_neighbor_state_get (neighbor_ptr, OPC_NIL);

	/* Don't handle link state requests in states lesser than Exchange. */
	if (nbr_state < OspfC_Neighbor_State_Exchange)
		{
		op_pk_destroy (message_ptr);
		FOUT;
		}

	/* Get the total number of fields in the packet.  This will help */
	/* us determine the number of requests being made.               */
	num_fields = op_pk_fd_max_index (message_ptr) + 1;

	/* There are three fields per request, so subtract the number of header    */
	/* fields from the LSR, and divide by three to get the number of requests. */
	num_requests = (num_fields - OSPFC_LSR_NUM_STATIC_FIELDS) / 3;

	lsu_msg_ptr = ospf_message_lsu_create (neighbor_ptr);

	/* Scan through the rest of the fields to fufill the requests. */
	for (request_index = 0; request_index < num_requests; request_index++)
		{
		/* Get the components of the request. */
		op_pk_fd_get (message_ptr, OSPFC_LSR_NUM_STATIC_FIELDS - 1 + (request_index * 3) + 
			OSPFC_MSG_LSR_FD_TYPE, &msg_lsa_type);

		op_pk_fd_get (message_ptr, OSPFC_LSR_NUM_STATIC_FIELDS - 1 + (request_index * 3) +
			OSPFC_MSG_LSR_FD_LINK_ID, &msg_link_id);

		op_pk_fd_get (message_ptr, OSPFC_LSR_NUM_STATIC_FIELDS - 1 + (request_index * 3) +
			OSPFC_MSG_LSR_FD_ROUTER_ID, &msg_adv_router_id);

		/* Search for the advertisement being requested in the database. */
		lsa_ptr = ospf_lsa_area_lookup (area_ptr, msg_link_id, msg_adv_router_id,
			msg_lsa_type);

		/* If we didn't find the advertisement, then something has gone wrong */
		/* with the database exchange process.  Restart the process and       */
		/* discard the message.                                               */
		if (lsa_ptr == OPC_NIL)
			{
			ospf_neighbor_invoke (neighbor_ptr, OspfC_Neighbor_Event_Bad_Ls_Req);
			op_pk_destroy (message_ptr);
			FOUT;
			}

		/* The LSA has been found, so add it to the Link State Update message. */
		ospf_message_lsa_add (lsu_msg_ptr, lsa_ptr);
		}

	/* Send the link state update in response. */
	ospf_neighbor_message_send (neighbor_ptr, lsu_msg_ptr, OPC_FALSE);

	/* Finished processing the packet, so destroy it. */
	op_pk_destroy (message_ptr);

	FOUT;
	}

void
ospf_link_state_update_message_rcvd (Packet *message_ptr, IpT_Address intf_addr)
	{
	char					intf_addr_str [IPC_ADDR_STR_LEN];
	char					err_msg [128];
	OspfT_Interface *		intf_ptr;
	OspfT_Area *			area_ptr;
	OspfT_Router_Id			msg_router_id;
	OspfT_Neighbor *		neighbor_ptr;
	List *					msg_lsa_list_ptr;

	/** Handles an incoming Link State Update message.  Corresponds partly **/
	/** to section 10.9 and mostly to section 13 of RFC 1247.              **/
	FIN (ospf_link_state_update_message_rcvd (message_ptr, intf_addr));	

	/* Create a string representation of address. */
	ip_address_print (intf_addr_str, intf_addr);

	/* Retrieve the interface data structure that corresponds */
	/* to the interface the message was received on.          */
	intf_ptr = ospf_router_interface_find (intf_addr);

	/* Exit if we can't find the interface. */
	if (intf_ptr == OPC_NIL)
		{
		sprintf (err_msg, "Unable to find interface %s", intf_addr_str);
		ospf_fatal_error (err_msg);
		}

	/* Get the area associated with this message. */
	area_ptr = intf_ptr->parent_area_ptr;

	/* Get the router ID from the message header. */
	op_pk_nfd_get (message_ptr, "router ID", &msg_router_id);

	/* Now search for the associated neighbor. The search key is based on the */
	/* underlying interface: use the source IP address for multi-access       */
	/* networks, use the router ID for point-to-point links.                  */
	if (intf_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt)
		neighbor_ptr = ospf_interface_neighbor_find (intf_ptr, msg_router_id);
	else
		neighbor_ptr = ospf_interface_neighbor_find (intf_ptr, ospf_message_src_addr);

	/* Check the validity of neighbor_ptr, if it is equal to OPC_NIL then we  */
	/* could not find the neighbor that sent this link state update. Destroy  */
	/* the packet and move on.												  */
	if (neighbor_ptr == OPC_NIL)
		{
		op_pk_destroy (message_ptr);
		}
	else
		{
		/* Extract the list of LSAs from this message.						  */
		msg_lsa_list_ptr = ospf_message_lsa_list_get (message_ptr);

		/* Execute the flooding procedure.									  */
		ospf_lsa_list_process (msg_lsa_list_ptr, area_ptr, neighbor_ptr);

		/* Done processing packet, so destroy it.							  */
		op_pk_destroy (message_ptr);
		}

	FOUT;
	}


void
ospf_link_state_ack_message_rcvd (Packet *message_ptr, IpT_Address intf_addr)
	{
	char					intf_addr_str [IPC_ADDR_STR_LEN];
	char					err_msg [128];
	OspfT_Interface *		intf_ptr;
	OspfT_Area *			area_ptr;
	OspfT_Router_Id			msg_router_id;
	OspfT_Neighbor *		neighbor_ptr;
	List *					msg_lsa_header_list_ptr;
	int						num_headers, header_index;
	OspfT_Lsa_Header *		lsa_header_ptr;
	OspfT_Lsa *				lsa_ptr;
	List *					nbr_rxmt_list_ptr;
	char					trace_msg [128];
	char					router_id_str [IPC_ADDR_STR_LEN];
	char					nbr_addr_str [IPC_ADDR_STR_LEN];

	/** Handles an incoming Link State Acknowledgement message. **/
	/** Corresponds to section 13.7 of RFC 1247.                **/
	FIN (ospf_link_state_ack_message_rcvd (message_ptr, intf_addr));

	/* Create a string representation of address. */
	ip_address_print (intf_addr_str, intf_addr);

	/* Retrieve the interface data structure that corresponds */
	/* to the interface the message was received on.          */
	intf_ptr = ospf_router_interface_find (intf_addr);

	/* Exit if we can't find the interface. */
	if (intf_ptr == OPC_NIL)
		{
		sprintf (err_msg, "Unable to find interface %s", intf_addr_str);
		ospf_fatal_error (err_msg);
		}

	/* Get the area associated with this message. */
	area_ptr = intf_ptr->parent_area_ptr;

	/* Get the router ID from the message header. */
	op_pk_nfd_get (message_ptr, "router ID", &msg_router_id);

	/* Now search for the associated neighbor. The search key is based on the */
	/* underlying interface: use the source IP address for multi-access       */
	/* networks, use the router ID for point-to-point links.                  */
	if (intf_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt)
		neighbor_ptr = ospf_interface_neighbor_find (intf_ptr, msg_router_id);
	else
		neighbor_ptr = ospf_interface_neighbor_find (intf_ptr, ospf_message_src_addr);

	/* Drop the packet if in lesser state than Exchange. */
	if (ospf_neighbor_state_get (neighbor_ptr, OPC_NIL) < OspfC_Neighbor_State_Exchange)
		{
		op_pk_destroy (message_ptr);
		FOUT;
		}

	/* Extract the LSA headers from this message. */
	msg_lsa_header_list_ptr = ospf_message_lsa_header_list_get (message_ptr);

	/* Loop through the LSA headers and remove the corresponding LSAs */
	/* from the retransmission list.                                  */
	num_headers = op_prg_list_size (msg_lsa_header_list_ptr);
	nbr_rxmt_list_ptr = ospf_neighbor_ls_transmit_list_get (neighbor_ptr);
	for (header_index = 0; header_index < num_headers; header_index++)
		{
		lsa_header_ptr = op_prg_list_access (msg_lsa_header_list_ptr, header_index);
		
		/* Do we have an instance on the retransmission list? */
		lsa_ptr = ospf_lsa_lookup (nbr_rxmt_list_ptr, lsa_header_ptr->link_state_id,
			lsa_header_ptr->router_id, lsa_header_ptr->type);

		/* Is it the same instance? */
		if ((lsa_ptr != OPC_NIL) &&
			(ospf_lsa_compare (lsa_header_ptr, lsa_ptr->lsa_header_ptr) == 0))
			{
			/* Remove from retransmission list; the LSA has been acked. */
			ospf_neighbor_lsa_rxmt_remove (neighbor_ptr, lsa_ptr);

			/* Issue trace message. */
			if (op_prg_odb_ltrace_active ("ospf_message_ls_ack"))
				{
				ip_address_print (router_id_str, lsa_header_ptr->router_id);
				ip_address_print (nbr_addr_str, neighbor_ptr->ip_address);
				sprintf (trace_msg, "LSA (%d) originated by router %s, is acknowledged by neighbor %s",
					lsa_header_ptr->sequence_num, router_id_str, nbr_addr_str);
				op_prg_odb_print_minor (trace_msg, OPC_NIL);
				}
			}
		}

	/* Destroy packet after finished processing. */
	op_pk_destroy (message_ptr);

	FOUT;
	}
