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



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



/* Header block */
#include <stdio.h>
#include <malloc.h>
#include <string.h>

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

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

/* OSPF specific headers */
#include "ospf_defs.h"

/* pim header */
#include "pim_dm.h"

/*PIM control message type */
#define Hello       0
#define Join_Prune  3
#define Assert      5
#define Graft       6
#define Graft_Ack   7


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

/* researved address for PIM control message */
IpT_Address	  ALL_PIM_ROUTERS;
extern IpT_Address	  ALL_IGMP_ROUTERS;
extern IpT_Address	  ALL_IGMP_GROUPS;
extern        IpT_Address IpI_Default_Addr; /* CHANGE this has to be set somewhere */

/* functions defined in function block */
void pim_dm_intface_table_init ();
void pim_sv_init (int *pim_router_ip_outstrm);
void pim_root_message_dispatch (Packet *message_ptr, IpT_Address intf_addr, IpT_Address message_src_addr);
void pim_root_data_process (Packet *data, IpT_Address msg_intf_addr, IpT_Address src_addr, 
       IpT_Address group_addr, int Mroute_hit);

void ospf_area_list_interfaces_print1 (List *area_list_ptr, Boolean detailed);
void ospf_interface_list_print1 (List *interface_list_ptr);
void ospf_rte_table_handle_get (IpT_Rte_Table_Lookup_Proc *ospf_rte_select_func_ptr, 
          IpT_Rte_Table_Handle *ospf_dyn_route_table_ptr);
List * ospf_area_list_get_in_pim ();

void pim_hello_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, IpT_Address neighbor_address);
void pim_prune_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, IpT_Address neighbor_address);
void pim_graft_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, IpT_Address neighbor_address);
void pim_graft_ack_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, IpT_Address neighbor_address);
void pim_join_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, IpT_Address neighbor_address);
void pim_assert_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, 
     IpT_Address neighbor_address);
Packet* pim_hello_message_create ();
Packet* pim_join_message_create (PimDM_Mroute_Entry *Mroute_entry);
Packet* pim_prune_message_create (PimDM_Mroute_Entry *Mroute_entry);
Packet* pim_graft_message_create (PimDM_Mroute_Entry *Mroute_entry);
Packet* pim_assert_message_create (PimDM_Mroute_Entry *Mroute_entry, int metric);
void pim_assert_message_send (PimDM_Mroute_Entry *Mroute_entry, IpT_Address in_addr, int metric);
void pim_hello_message_send (PimDM_Interface *interface_ptr);
void pim_join_message_send (PimDM_Mroute_Entry *Mroute_entry); 
void pim_graft_message_send (PimDM_Mroute_Entry *Mroute_table_entry);
void pim_graft_ack_message_send (PimDM_Mroute_Entry *Mroute_entry); 
void pim_prune_message_send (PimDM_Mroute_Entry *Mroute_entry, IpT_Address prune_intf_addr) ;
void pim_neighbor_message_send (PimDM_Neighbor *neighbor_ptr, Packet *message, Boolean status);
void pim_interface_hello_send ();
void ip_data_send (Packet *message_ptr, IpT_Address dest_addr, IpT_Address src_addr);

void pim_neighbor_update (PimDM_Interface* intf_ptr,  IpT_Address neighbor_addr,  Packet* msg_ptr);
PimDM_Neighbor *pim_neighbor_create (PimDM_Interface* intf_ptr, IpT_Address neighbor_addr, Packet *message_ptr);
PimDM_Interface* pim_interface_find (IpT_Address in_addr);
void neighbor_delete (PimDM_Neighbor *neighbor_ptr, int intrpt_code);
void negative_cache_entry_delete (SrcGrpPair *S_G, int intrpt_code);
void pim_interface_rebroadcast(OIF_Interface *oif_intf, int intrpt_code); 
void delay_prune (PimDM_Delay_Prune_Message *pim_delay_prune_message, int intrpt_code);
double get_negative_cache_time (List *oif);

OIF_Interface *find_intf_in_oifs (List *oif, IpT_Address in_addr);
void Mlocal_create ();
void dummy_Mlocal_create ();
Mlocal_find (IpT_Address intf_addr, IpT_Address group_addr);
void pim_dm_Mroute_entry_create (IpT_Address src_addr, IpT_Address group_addr, IpT_Address RPF_nbr, IpT_Address msg_in_addr);
PimDM_Mroute_Entry *Mroute_look_up (List* Mrotue_tbl_ptr, SrcGrpPair *S_G, int *entry_index); 
/*Compcode Mroute_look_up (SrcGrpPair *S_G, int *entry_index, PimDM_Mroute_Entry **Mroute_entry);*/
void Mroute_update (PimDM_Interface* interface_ptr); 
List * Mroute_look_up_for_G (IpT_Address group);
void Mroute_print ();

/* functions added by Anindya */
PimDMT_Rte_Table *PopulateMROUTE(char *rtable);
  
/* back up functions */
PimDMT_Router *pim_dm_router_create (Prohandle proc_handle, OmsT_Pr_Handle proc_reg_handle);
/*PimDMT_Rte_Table *pim_dm_rte_table_create (void);*/
/*Compcode pim_dm_rte_table_lookup (int fast_address, IpT_Rte_Table_Handle rte_table_ptr, IpT_Address dest_addr,
	IpT_Address *next_addr_ptr);*/
/* Saved a lot of files in A: drive. Need to define all the missing functions MM */void Mlocal_find_G (List ** join_src_grp_list, IpT_Address source, IpT_Address group);
/*void Mlocal_find (List ** join_src_grp_list, IpT_Address intf_addr, IpT_Address group_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
	PimDMT_Router_Id	       		sv_pim_dm_router_id;
	IpT_Address             		sv_pim_dm_my_ip_address;
	Objid	                  		sv_pim_dm_my_objid;
	Objid	                  		sv_own_node_objid;
	Objid                   		sv_ip_objid;
	double                  		sv_RebroadcastInterval;
	double                  		sv_Negative_Cache_Time_Out;
	double                  		sv_Delay_Prune_Time;
	double                  		sv_Delay_Join_Time;
	double                  		sv_Graft_Resend_Time;
	double                  		sv_Pim_Hello_Interval;
	double                  		sv_Pim_Hello_Holdtime;
	double                  		sv_Prune_Holdtime;
	double                  		sv_Default_Entry_Time;
	Boolean	                		sv_dynamic_routing_enabled;
	IpT_Rte_Table_Lookup_Proc			sv_unicast_rte_select_func_ptr;
	IpT_Rte_Table_Handle	   		sv_unicast_dyn_route_table_ptr;
	IpT_Rte_Info *	         		sv_pim_dm_mcast_dyn_rte_info_ptr;
	IpT_Address	            		sv_pim_message_intf_addr;
	IpT_Address	            		sv_pim_message_src_addr;
	IpT_Address	            		sv_pim_message_dest_addr;
	Ici *	                  		sv_pim_message_ici_ptr;
	Packet *	               		sv_pim_message_ptr;
	int	                    		sv_pim_dm_router_ip_outstrm;
	List *                  		sv_Mroute_table_ptr;
	List *                  		sv_Mlocal_table_ptr;
	List *                  		sv_pim_dm_interface_table_ptr;
	PimDMT_Router *	        		sv_pim_dm_router_ptr;
	int                     		sv_min_assert_metric;
	Boolean                 		sv_leaf_router;
	List *                  		sv_ospf_area_list_ptr_in_pim;
	List *                  		sv_mcast_addr_list_ptr;
	Boolean                 		sv_pim_dm_start;
	IpT_Info *	             		sv_ip_info_ptr;
	IpT_Interface_Info *	   		sv_ip_interface_ptr;
	char                    		sv_routing_table[40];
	char                    		sv_mlocal_table[40];
	char                    		sv_name[10];
	} A_pim_dm_state;

#define pr_state_ptr            		((A_pim_dm_state*) SimI_Mod_State_Ptr)
#define pim_dm_router_id        		pr_state_ptr->sv_pim_dm_router_id
#define pim_dm_my_ip_address    		pr_state_ptr->sv_pim_dm_my_ip_address
#define pim_dm_my_objid         		pr_state_ptr->sv_pim_dm_my_objid
#define own_node_objid          		pr_state_ptr->sv_own_node_objid
#define ip_objid                		pr_state_ptr->sv_ip_objid
#define RebroadcastInterval     		pr_state_ptr->sv_RebroadcastInterval
#define Negative_Cache_Time_Out 		pr_state_ptr->sv_Negative_Cache_Time_Out
#define Delay_Prune_Time        		pr_state_ptr->sv_Delay_Prune_Time
#define Delay_Join_Time         		pr_state_ptr->sv_Delay_Join_Time
#define Graft_Resend_Time       		pr_state_ptr->sv_Graft_Resend_Time
#define Pim_Hello_Interval      		pr_state_ptr->sv_Pim_Hello_Interval
#define Pim_Hello_Holdtime      		pr_state_ptr->sv_Pim_Hello_Holdtime
#define Prune_Holdtime          		pr_state_ptr->sv_Prune_Holdtime
#define Default_Entry_Time      		pr_state_ptr->sv_Default_Entry_Time
#define dynamic_routing_enabled 		pr_state_ptr->sv_dynamic_routing_enabled
#define unicast_rte_select_func_ptr		pr_state_ptr->sv_unicast_rte_select_func_ptr
#define unicast_dyn_route_table_ptr		pr_state_ptr->sv_unicast_dyn_route_table_ptr
#define pim_dm_mcast_dyn_rte_info_ptr		pr_state_ptr->sv_pim_dm_mcast_dyn_rte_info_ptr
#define pim_message_intf_addr   		pr_state_ptr->sv_pim_message_intf_addr
#define pim_message_src_addr    		pr_state_ptr->sv_pim_message_src_addr
#define pim_message_dest_addr   		pr_state_ptr->sv_pim_message_dest_addr
#define pim_message_ici_ptr     		pr_state_ptr->sv_pim_message_ici_ptr
#define pim_message_ptr         		pr_state_ptr->sv_pim_message_ptr
#define pim_dm_router_ip_outstrm		pr_state_ptr->sv_pim_dm_router_ip_outstrm
#define Mroute_table_ptr        		pr_state_ptr->sv_Mroute_table_ptr
#define Mlocal_table_ptr        		pr_state_ptr->sv_Mlocal_table_ptr
#define pim_dm_interface_table_ptr		pr_state_ptr->sv_pim_dm_interface_table_ptr
#define pim_dm_router_ptr       		pr_state_ptr->sv_pim_dm_router_ptr
#define min_assert_metric       		pr_state_ptr->sv_min_assert_metric
#define leaf_router             		pr_state_ptr->sv_leaf_router
#define ospf_area_list_ptr_in_pim		pr_state_ptr->sv_ospf_area_list_ptr_in_pim
#define mcast_addr_list_ptr     		pr_state_ptr->sv_mcast_addr_list_ptr
#define pim_dm_start            		pr_state_ptr->sv_pim_dm_start
#define ip_info_ptr             		pr_state_ptr->sv_ip_info_ptr
#define ip_interface_ptr        		pr_state_ptr->sv_ip_interface_ptr
#define routing_table           		pr_state_ptr->sv_routing_table
#define mlocal_table            		pr_state_ptr->sv_mlocal_table
#define name                    		pr_state_ptr->sv_name

/* 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	A_pim_dm_state *op_sv_ptr = pr_state_ptr;




/* Process model interrupt handling procedure */



void
A_pim_dm ()
	{
	char				proc_model_name [128];
	List *              proc_record_handle_list_ptr;
	OmsT_Pr_Handle	    proc_reg_handle;
	Prohandle		    pim_dm_my_prohandle;
	
	FILE               *fp1; 
	IpT_Rte_Info*	    unicast_dyn_rte_info_ptr;
	
	/*for debugging */
	IpT_Address         tmp_src_addr;
	IpT_Address         tmp_dest_addr;
	char                tmp_src_addr_str [IPC_ADDR_STR_LEN]; 
	char                tmp_dest_addr_str [IPC_ADDR_STR_LEN]; 
	char                tmp_err_msg [256];
	int                 Mroute_hit;
	
	char				cj_pkfmt[128];
	int					igmptype;
	IpT_Address			grp_ip;
	IpT_Address			interface_address;
	char				char_grp_ip[IPC_ADDR_STR_LEN];
	int _block_origin = 0;


	FSM_ENTER (A_pim_dm)

	FSM_BLOCK_SWITCH
		{
		/*---------------------------------------------------------*/
		/** state (init) enter executives **/
		FSM_STATE_ENTER_UNFORCED (0, state0_enter_exec, "init", "A_pim_dm () [init enter execs]")
			{
			/* Since PIM 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.                */
			pim_dm_start = OPC_FALSE;
			
			/* Start by initializing my own object id. */
			pim_dm_my_objid = op_id_self ();
			own_node_objid = op_topo_parent(pim_dm_my_objid);
			
			/* Get the name of the process model. */
			op_ima_obj_attr_get (pim_dm_my_objid , "process model", proc_model_name);
			
			/* get the name of the node model (router name) */
			op_ima_obj_attr_get (own_node_objid, "name", name);
			
			/* Also add a reference to my own prohandle. */
			pim_dm_my_prohandle = op_pro_self ();
			
			/*sprintf(routing_table, "c:\\\\op_models\\\\%smroute.txt", name);*/
			/*sprintf(routing_table, "/u/mask/xinwang/op_models/%smroute.txt", name);
			sprintf(mlocal_table, "/u/mask/xinwang/op_models/%slocal.txt", name);*/
			
			/* Create global Mroute table. Mlocal is dummy */
			Mroute_table_ptr = op_prg_list_create ();
			Mlocal_table_ptr = op_prg_list_create ();
			
			/* Create the IP multicast addresses that PIM resserves. */
			ALL_PIM_ROUTERS = ip_address_create ("224.0.0.13");
			
			/* Register the process in the model-wide registry. */
			proc_reg_handle = (OmsT_Pr_Handle) oms_pr_process_register (own_node_objid, pim_dm_my_objid,
			      pim_dm_my_prohandle, proc_model_name);
			
			
			/* Create a dynamic routing structure. */
			pim_dm_mcast_dyn_rte_info_ptr = ip_dyn_rte_info_create ();
			pim_dm_mcast_dyn_rte_info_ptr-> table_handle = Mroute_table_ptr;
			
			/* Register the protocol attribute and dynamic routing infomration in the registry. */
			/* IP or other models can get access to multicast table created by PIM */
			oms_pr_attr_set (proc_reg_handle, 
			       "protocol",            OMSC_PR_STRING, "pim_dm", 
			       "routing information", OMSC_PR_ADDRESS, pim_dm_mcast_dyn_rte_info_ptr, OPC_NIL);
			
			/* Get unicast routing table handle (ospf routing table handle) */
			/* First get the process handle                                 */
			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, "ospf",
							"node objid", 	OMSC_PR_OBJID, 	own_node_objid, 
							OPC_NIL);
				
			if (op_prg_list_size (proc_record_handle_list_ptr) != 1)  {
					/*	An error should be created if there are more than	*/
					/*	one dynamic routing process in the local node.		*/
				op_sim_end ("Error: either zero or several dynamic routing proceses in the local node", "", "", "");
			}
			else {
				proc_reg_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, 
										OPC_LISTPOS_HEAD);
			
			    /*	Obtain the routing information structure pointer.	*/
				oms_pr_attr_get (proc_reg_handle, "routing information", OMSC_PR_ADDRESS, &unicast_dyn_rte_info_ptr);
			}
			
			/*	Deallocate the list pointer.							*/
			op_prg_mem_free (proc_record_handle_list_ptr);	
				   
			/*	Obtain the function pointer to the (ospf) routing table	*/
			/*	lookup function.										*/
			unicast_rte_select_func_ptr = unicast_dyn_rte_info_ptr->lookup_proc;
			
			/*	Obtain a pointer to the dynamic routing table.			*/
			unicast_dyn_route_table_ptr = unicast_dyn_rte_info_ptr->table_handle; 
			
			/* Get parameters from configuration*/
			op_ima_obj_attr_get (pim_dm_my_objid, "Rebroadcast Interval", &RebroadcastInterval);
			op_ima_obj_attr_get (pim_dm_my_objid, "Negative Cache Time Out", &Negative_Cache_Time_Out);
			op_ima_obj_attr_get (pim_dm_my_objid, "Delay Prune Time", &Delay_Prune_Time);
			op_ima_obj_attr_get (pim_dm_my_objid, "Graft Resend Time", &Graft_Resend_Time);
			op_ima_obj_attr_get (pim_dm_my_objid, "Pim Hello Interval", &Pim_Hello_Interval);
			op_ima_obj_attr_get (pim_dm_my_objid, "Hello Holdtime", &Pim_Hello_Holdtime);
			op_ima_obj_attr_get (pim_dm_my_objid, "Prune Holdtime", &Prune_Holdtime);
			op_ima_obj_attr_get (pim_dm_my_objid, "Default Entry Time", &Default_Entry_Time);
			op_ima_obj_attr_get (pim_dm_my_objid, "leaf_router", &leaf_router);
			
			}


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


		/** state (init) exit executives **/
		FSM_STATE_EXIT_UNFORCED (0, state0_exit_exec, "init", "A_pim_dm () [init exit execs]")
			{
			/* If endsim woke us up, then perform no more initializations. */
			if (op_intrpt_type () == OPC_INTRPT_REMOTE)
				{
				/* Record the fact that PIM has started. */
				pim_dm_start = OPC_TRUE;
			
			    /* Register these addresses with IP, so IP knows to accept them. */
				ip_address_multicast_register (ALL_PIM_ROUTERS, own_node_objid);   
			
			    /* Initialize the rest of the state variables. */
				pim_sv_init (&pim_dm_router_ip_outstrm);
			 
			    /* We need the ospf interface table to fill our interface table */
			    /* The ospf interface table is in the area list                 */
			    ospf_area_list_ptr_in_pim = ospf_area_list_get_in_pim ();    
			    pim_dm_intface_table_init ();
			    dummy_Mlocal_create ();
			
			    ospf_rte_table_handle_get (&unicast_rte_select_func_ptr, &unicast_dyn_route_table_ptr);   
			   
			    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,   own_node_objid,
					        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);
			    }
			    else {
				    proc_reg_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, 
										OPC_LISTPOS_HEAD);
			        mcast_addr_list_ptr = op_prg_list_create ();
			        /*	Obtain the routing information structure pointer.	*/
				    oms_pr_attr_get (proc_reg_handle, "module objid", OMSC_PR_OBJID, &ip_objid);
			        oms_pr_attr_get (proc_reg_handle, "interface information", OMSC_PR_ADDRESS, &ip_info_ptr);
			       
					/* Commented by cjin. If executing the following, it seams disallow other process */
					/* such as igmp to register */
			        /*oms_pr_attr_set (proc_reg_handle, */
					/*		"multicast address list", OMSC_PR_ADDRESS, mcast_addr_list_ptr,*/
					/*		OPC_NIL);*/
			    }
				
				/* Deallocate the temporary list pointer. */
				op_prg_mem_free (proc_record_handle_list_ptr);
			
			    /* Send remote interrupt to IP so IP can get mcast table handle from PIM */
			    op_intrpt_schedule_remote (op_sim_time (), 0, ip_objid);
			
			    printf("send REMOTE interrupt to IP *********\n");
			   
			    /* Start sending hello to neighbors          */ 
			    pim_interface_hello_send ();
			
			}
			
			}


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

		FSM_TRANSIT_SWITCH
			{
			FSM_CASE_TRANSIT (0, 3, state3_enter_exec, ;)
			FSM_CASE_TRANSIT (1, 1, state1_enter_exec, ;)
			}
		/*---------------------------------------------------------*/



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


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


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


		/** state (idle) transition processing **/
		FSM_INIT_COND (MESSAGE_RECEIVED)
		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, ;)
			}
		/*---------------------------------------------------------*/



		/** state (Message) enter executives **/
		FSM_STATE_ENTER_FORCED (2, state2_enter_exec, "Message", "A_pim_dm () [Message enter execs]")
			{
			/* This state handles incoming PIM messages, and retrieves */
			/* the interface address the message arrived on.            */
			
			pim_message_ici_ptr = op_intrpt_ici ();
			
			if (pim_message_ici_ptr == OPC_NIL)
				printf ("Unable to retrieve lower-layer ICI associated with incoming PIM message.");
			
			/* Get the message. */
			pim_message_ptr = op_pk_get (op_intrpt_strm ());
			
			/* Begin added by cjin to process the notification from igmp */
			op_pk_format( pim_message_ptr, cj_pkfmt );
			if ( strcmp(cj_pkfmt, "igmp_pim_pkfmt") == 0) {
				op_pk_nfd_get( pim_message_ptr,"Type",&igmptype);
				op_pk_nfd_get( pim_message_ptr,"GRP_IP",&grp_ip);
				op_pk_nfd_get( pim_message_ptr,"Interface_address",&interface_address);
			
				ip_address_print( char_grp_ip, grp_ip );
				ip_address_print( tmp_src_addr_str, interface_address);
			
				if ( igmptype == 22 ) {
					/* Here should be inserting new group */
					printf( "%s pim got a message from igmp, group %s want to be joined on interface %s\n",
						name, char_grp_ip, tmp_src_addr_str );
				}
				else if ( igmptype == 23 ) {
					/* Here should be removing the group */
					printf( "%s pim got a message from igmp, group %s want to be left on interface %s\n",
						name, char_grp_ip, tmp_src_addr_str );
				}
				else {
					printf( "Unknown operation from igmp for group %s\n", char_grp_ip );
				}
			
				op_pk_destroy( pim_message_ptr );
			
			}
			else { // Embrace all original stuff in "else" by cjin to process igmp, following is original
			
			/* Get the interface this message arrived on. */
			op_ici_attr_get (pim_message_ici_ptr, "interface_received", &pim_message_intf_addr);
			
			/* Get the source address of the message. */
			op_ici_attr_get (pim_message_ici_ptr, "src_addr", &pim_message_src_addr);
			
			/* Get the source address of the message. */
			op_ici_attr_get (pim_message_ici_ptr, "dest_addr", &pim_message_dest_addr);
			
			/* Dispatch the message to the appropriate child processes. */
			/* if control message, send to message dispatch function    */
			ip_address_print (tmp_src_addr_str, pim_message_src_addr);
			ip_address_print (tmp_dest_addr_str, pim_message_dest_addr);
			//printf ("%s pim_dm message: src_addr = %s, dest_addr = %s\n",  name, tmp_src_addr_str, tmp_dest_addr_str);
			
			if (ip_address_is_pim_reserved (pim_message_dest_addr)) {    
			    pim_root_message_dispatch (op_pk_copy (pim_message_ptr), ip_address_copy (pim_message_intf_addr), 
			        ip_address_copy (pim_message_src_addr));
			}
			else if (!address_is_mcast_reserved (pim_message_dest_addr)) {
			 
			    /* if data packet, send to data processing block     */
			    op_ici_attr_get (pim_message_ici_ptr, "Mroute_hit", &Mroute_hit);
			    pim_root_data_process (pim_message_ptr, pim_message_intf_addr, pim_message_src_addr, 
			        pim_message_dest_addr, Mroute_hit);
			    
			}
			
			} // Added by cjin to process igmp table change
			
			
			}


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


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



		/** state (Endsim) enter executives **/
		FSM_STATE_ENTER_UNFORCED (3, state3_enter_exec, "Endsim", "A_pim_dm () [Endsim enter execs]")
			{
			}


		/** blocking after enter executives of unforced state. **/
		FSM_EXIT (7,A_pim_dm)


		/** state (Endsim) exit executives **/
		FSM_STATE_EXIT_UNFORCED (3, state3_exit_exec, "Endsim", "A_pim_dm () [Endsim exit execs]")
			{
			}


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



		}


	FSM_EXIT (0,A_pim_dm)
	}




void
A_pim_dm_svar (prs_ptr,var_name,var_p_ptr)
	A_pim_dm_state		*prs_ptr;
	char			*var_name, **var_p_ptr;
	{

	FIN (A_pim_dm_svar (prs_ptr))

	*var_p_ptr = VOS_NIL;
	if (Vos_String_Equal ("pim_dm_router_id" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_dm_router_id);
	if (Vos_String_Equal ("pim_dm_my_ip_address" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_dm_my_ip_address);
	if (Vos_String_Equal ("pim_dm_my_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_dm_my_objid);
	if (Vos_String_Equal ("own_node_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_own_node_objid);
	if (Vos_String_Equal ("ip_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ip_objid);
	if (Vos_String_Equal ("RebroadcastInterval" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_RebroadcastInterval);
	if (Vos_String_Equal ("Negative_Cache_Time_Out" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_Negative_Cache_Time_Out);
	if (Vos_String_Equal ("Delay_Prune_Time" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_Delay_Prune_Time);
	if (Vos_String_Equal ("Delay_Join_Time" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_Delay_Join_Time);
	if (Vos_String_Equal ("Graft_Resend_Time" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_Graft_Resend_Time);
	if (Vos_String_Equal ("Pim_Hello_Interval" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_Pim_Hello_Interval);
	if (Vos_String_Equal ("Pim_Hello_Holdtime" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_Pim_Hello_Holdtime);
	if (Vos_String_Equal ("Prune_Holdtime" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_Prune_Holdtime);
	if (Vos_String_Equal ("Default_Entry_Time" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_Default_Entry_Time);
	if (Vos_String_Equal ("dynamic_routing_enabled" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_dynamic_routing_enabled);
	if (Vos_String_Equal ("unicast_rte_select_func_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_unicast_rte_select_func_ptr);
	if (Vos_String_Equal ("unicast_dyn_route_table_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_unicast_dyn_route_table_ptr);
	if (Vos_String_Equal ("pim_dm_mcast_dyn_rte_info_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_dm_mcast_dyn_rte_info_ptr);
	if (Vos_String_Equal ("pim_message_intf_addr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_message_intf_addr);
	if (Vos_String_Equal ("pim_message_src_addr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_message_src_addr);
	if (Vos_String_Equal ("pim_message_dest_addr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_message_dest_addr);
	if (Vos_String_Equal ("pim_message_ici_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_message_ici_ptr);
	if (Vos_String_Equal ("pim_message_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_message_ptr);
	if (Vos_String_Equal ("pim_dm_router_ip_outstrm" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_dm_router_ip_outstrm);
	if (Vos_String_Equal ("Mroute_table_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_Mroute_table_ptr);
	if (Vos_String_Equal ("Mlocal_table_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_Mlocal_table_ptr);
	if (Vos_String_Equal ("pim_dm_interface_table_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_dm_interface_table_ptr);
	if (Vos_String_Equal ("pim_dm_router_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_dm_router_ptr);
	if (Vos_String_Equal ("min_assert_metric" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_min_assert_metric);
	if (Vos_String_Equal ("leaf_router" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_leaf_router);
	if (Vos_String_Equal ("ospf_area_list_ptr_in_pim" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ospf_area_list_ptr_in_pim);
	if (Vos_String_Equal ("mcast_addr_list_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_mcast_addr_list_ptr);
	if (Vos_String_Equal ("pim_dm_start" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_pim_dm_start);
	if (Vos_String_Equal ("ip_info_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ip_info_ptr);
	if (Vos_String_Equal ("ip_interface_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ip_interface_ptr);
	if (Vos_String_Equal ("routing_table" , var_name))
		*var_p_ptr = (char *) (prs_ptr->sv_routing_table);
	if (Vos_String_Equal ("mlocal_table" , var_name))
		*var_p_ptr = (char *) (prs_ptr->sv_mlocal_table);
	if (Vos_String_Equal ("name" , var_name))
		*var_p_ptr = (char *) (prs_ptr->sv_name);

	FOUT;
	}




void
A_pim_dm_diag ()
	{
	char				proc_model_name [128];
	List *              proc_record_handle_list_ptr;
	OmsT_Pr_Handle	    proc_reg_handle;
	Prohandle		    pim_dm_my_prohandle;
	
	FILE               *fp1; 
	IpT_Rte_Info*	    unicast_dyn_rte_info_ptr;
	
	/*for debugging */
	IpT_Address         tmp_src_addr;
	IpT_Address         tmp_dest_addr;
	char                tmp_src_addr_str [IPC_ADDR_STR_LEN]; 
	char                tmp_dest_addr_str [IPC_ADDR_STR_LEN]; 
	char                tmp_err_msg [256];
	int                 Mroute_hit;
	
	char				cj_pkfmt[128];
	int					igmptype;
	IpT_Address			grp_ip;
	IpT_Address			interface_address;
	char				char_grp_ip[IPC_ADDR_STR_LEN];
	int _block_origin;

	FIN (A_pim_dm_diag ())

	BINIT

	FOUT;
	}




void
A_pim_dm_terminate ()
	{
	char				proc_model_name [128];
	List *              proc_record_handle_list_ptr;
	OmsT_Pr_Handle	    proc_reg_handle;
	Prohandle		    pim_dm_my_prohandle;
	
	FILE               *fp1; 
	IpT_Rte_Info*	    unicast_dyn_rte_info_ptr;
	
	/*for debugging */
	IpT_Address         tmp_src_addr;
	IpT_Address         tmp_dest_addr;
	char                tmp_src_addr_str [IPC_ADDR_STR_LEN]; 
	char                tmp_dest_addr_str [IPC_ADDR_STR_LEN]; 
	char                tmp_err_msg [256];
	int                 Mroute_hit;
	
	char				cj_pkfmt[128];
	int					igmptype;
	IpT_Address			grp_ip;
	IpT_Address			interface_address;
	char				char_grp_ip[IPC_ADDR_STR_LEN];
	int _block_origin;

	FIN (A_pim_dm_terminate ())

	BINIT

	FOUT;
	}



Compcode
A_pim_dm_init (pr_state_pptr)
	A_pim_dm_state			**pr_state_pptr;
	{
	int _block_origin = 0;
	static VosT_Cm_Obtype	obtype = OPC_NIL;

	extern int				Vos_Nop ();

	FIN (A_pim_dm_init (pr_state_pptr))

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

	if ((*pr_state_pptr = (A_pim_dm_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
pim_sv_init (int *pim_router_ip_outstrm)
	{
	
	int					outstrm_count;
	Objid				outstrm_objid;
	
	/** Initialize state variables of PIM root/dispatch process.      **/
	
	FIN (pim_sv_init (pim_router_ip_outstrm));

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

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

	outstrm_objid = op_topo_assoc (pim_dm_my_objid, OPC_TOPO_ASSOC_OUT, OPC_OBJTYPE_STRM, 0);
	
	/* Retrieve the index of the stream from PIM to IP */
	op_ima_obj_attr_get (outstrm_objid, "src stream", pim_router_ip_outstrm);
	
	
	FOUT;
	}


/***** Root sub-package *****/
/**   Dispatch a control message received by the pim_model to different message processing block **/ 
void
pim_root_message_dispatch (Packet *message_ptr, IpT_Address intf_addr, IpT_Address message_src_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;
    int                     num_joined_srcs = 0;
    int                     num_pruned_srcs = 0;

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

	/* Get type of message. */
	op_pk_nfd_get (message_ptr, "Type", &message_type);
 
    //printf ("message type == %d \n\n",message_type); 
	ip_address_print (intf_addr_str, intf_addr);
	ip_address_print (src_addr_str, message_src_addr);

	/* Dispatch the message according to type. */
	switch (message_type)
		{
		case Hello:
			{
			/*if (op_prg_odb_ltrace_active ("pim_message_hello"))
				{
				sprintf (msg_str, "%s Hello message received on interface %s from %s", 
                  router_name, intf_addr_str, src_addr_str);
				op_prg_odb_print_minor (msg_str, OPC_NIL);
				}*/
            sprintf (err_msg, " %s pim message of Hello type received on interface %s from %s\n", 
				name, intf_addr_str, src_addr_str);
			//printf (err_msg);

			pim_hello_message_rcvd (message_ptr, intf_addr, message_src_addr);
			break;
			}

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

            op_pk_nfd_get (message_ptr, "Num_Joined_Sources", &num_joined_srcs);
            op_pk_nfd_get (message_ptr, "Num_Pruned_Sources", &num_pruned_srcs);
            //printf ("num_joined_srcs = %d, num_pruned_srcs = %d\n", num_joined_srcs, num_pruned_srcs);
            if (num_joined_srcs == 1) {
                sprintf (msg_str, "%s Join message received on interface %s from %s", name,
					intf_addr_str, src_addr_str);
                //printf (msg_str);
                pim_join_message_rcvd (message_ptr, intf_addr, message_src_addr);  
                
            }    
			else if (num_pruned_srcs == 1){
                sprintf (msg_str, "%s Prune message received on interface %s from %s \n ", name,
					intf_addr_str, src_addr_str);
                pim_prune_message_rcvd (message_ptr, intf_addr, message_src_addr);   
                //printf (msg_str);
            }            
			break;
			}

		case Assert:
			{
			/*if (op_prg_odb_ltrace_active ("pim_message_Assert"))
				{
				sprintf (msg_str, "Assert message received on interface %s from %s", 
					intf_addr_str, src_addr_str);
				op_prg_odb_print_minor (msg_str, OPC_NIL);
				}*/
            sprintf (msg_str, "%s Assert message received on interface %s from %s \n", name,
					intf_addr_str, src_addr_str);  
            printf (msg_str);
			pim_assert_message_rcvd (message_ptr, intf_addr, message_src_addr);
           
			break;
			}

		case Graft:
			{
			/*if (op_prg_odb_ltrace_active ("pim_message_Graft"))
				{
				sprintf (msg_str, "Graft message received on interface %s from %s", intf_addr_str,
					src_addr_str);
				op_prg_odb_print_minor (msg_str, OPC_NIL);
				}*/
            sprintf (msg_str, "%s Graft message received on interface %s from %s", name,
					intf_addr_str, src_addr_str);  
            printf (msg_str);
			pim_graft_message_rcvd (message_ptr, intf_addr, message_src_addr);
			break;
			}

		case Graft_Ack:
			{
			/*if (op_prg_odb_ltrace_active ("pim_message_graft_ack"))
				{
				sprintf (msg_str, "Graft Acknowledgement message received on interface %s from %s", 
					intf_addr_str, src_addr_str);
				op_prg_odb_print_minor (msg_str, OPC_NIL);
				}*/
            sprintf (msg_str, "%s Graft_ACK message received on interface %s from %s", name,
					intf_addr_str, src_addr_str);  
            printf (msg_str);
			pim_graft_ack_message_rcvd (message_ptr, intf_addr, message_src_addr);
			break;
			}

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

	FOUT;
	}


void
pim_root_data_process (Packet *data, IpT_Address msg_intf_addr, IpT_Address src_addr, 
     IpT_Address group_addr, int Mroute_hit)
{
   int         entry_index;
   PimDM_Mroute_Entry *Mroute_entry;
   OIF_Interface *oif_intf_ptr;
   int         num_oif_interfaces, oif_index;
   char        src_addr_str [IPC_ADDR_STR_LEN];
   char        group_addr_str [IPC_ADDR_STR_LEN];
   char        msg_addr_str [IPC_ADDR_STR_LEN];
   char        RPF_addr_str [IPC_ADDR_STR_LEN];
   char        RPF_in_addr_str [IPC_ADDR_STR_LEN]; 
    char       tmp_addr_str [IPC_ADDR_STR_LEN]; 
    
   IpT_Address RPF_in_addr;    
   char        err_msg [256];
   SrcGrpPair *S_G, *S_G_copy; 
   int         my_metric = 1; 
   IpT_Address RPF_addr;  
   IpT_Address RPF_intf_addr; 
   PimDM_Interface * pim_intf;   
  

   FIN (pim_root_data_process (data, msg_intf_addr, src_addr, group_addr, Mroute_hit));
  
   ip_address_print (src_addr_str, src_addr);
   ip_address_print (group_addr_str, group_addr);
   ip_address_print (msg_addr_str, msg_intf_addr);
   //printf ("%s **********data_proc: src_addr = %s group_addr = %s msg_addr = %s\n", name, src_addr_str, group_addr_str, msg_addr_str);
   
   S_G = (SrcGrpPair *) op_prg_mem_alloc (sizeof (SrcGrpPair));
   S_G->source = ip_address_copy (src_addr);
   S_G->group = ip_address_copy (group_addr);

   /* IP could not find (S, G) in Mroute table, forward the data packet to PIM    */
   /* for constructing the (S, G) entry                                           */        
   if (!Mroute_hit) { /* 1000 is dummy since it is not useful in the ospf lookup function */
       if ((*unicast_rte_select_func_ptr) (1000, unicast_dyn_route_table_ptr, src_addr,
		  &RPF_addr) == OPC_COMPCODE_SUCCESS) { 
           /* RPF_addr is the interface address of the next hop router towards source */
           /* RPF_intf_addr is current router interface address connected to RPF_addr */
           if ((RPF_intf_addr = RPF_interface_get (RPF_addr)) != OPC_NIL) { 
               /* If packet is sent from RPF neighbor, construct Mroute entry         */   
               if (ip_address_equal (msg_intf_addr, RPF_intf_addr)) {
                  pim_dm_Mroute_entry_create (ip_address_copy (src_addr), ip_address_copy (group_addr), 
                     RPF_addr, ip_address_copy (msg_intf_addr)); 
                  /* Send data back to IP for forwarding based on new Mroute entry */
                  ip_data_send (data, ip_address_copy (group_addr), ip_address_copy (src_addr));
               }
               else
                  op_pk_destroy (data);
           }
           else {
               op_pk_destroy (data);   
               //printf ("unable to get RPF_intf_addr\n");    
           }
       }
       else {
           op_pk_destroy (data);   
           //printf ("unable to get RPF_addr\n");
       }    
   }
   /* When upstream  pruned interface expires, Data packet arrival will trigger re-prune to upstream neighbor*/
   else { 
      op_pk_destroy (data);
      Mroute_entry = Mroute_look_up (Mroute_table_ptr, S_G, &entry_index);
      /* For serial link, send prune to the wrong forwarding neighbor */
      /* If message comes from broadcast interface, send ASSERT to neighor forwarder */ 
      ip_address_print (tmp_addr_str, Mroute_entry->iif);
      //printf ("msg_intf_addr = %s, iif = %s\n", msg_addr_str, tmp_addr_str);
      if (!ip_address_equal (msg_intf_addr, Mroute_entry->iif)) {
         if ((pim_intf = pim_interface_find (msg_intf_addr))->interface_type != 
                   OspfC_Interface_Type_Broadcast)  
            pim_prune_message_send (Mroute_entry, ip_address_copy (msg_intf_addr));
         else { /* Make sure a packest is sent from oif */
            if (find_intf_in_oifs (Mroute_entry->oif, ip_address_copy (msg_intf_addr)) != NULL) {
               if (ospf_rte_table_cost_lookup (1000, unicast_dyn_route_table_ptr, src_addr,
		          &my_metric) == OPC_COMPCODE_SUCCESS)
               //printf ("before assert *************************************\n");
               //printf ("====before send msg_str = %s\n", msg_addr_str);
               pim_assert_message_send (Mroute_entry, ip_address_copy (msg_intf_addr), my_metric); 
               //printf ("assert send successfully\n");
            } 
         }
      }
      else {
         /*if (Mroute_entry->num_forward_intf == 0) {  Otherwise, can't send prune */
            if (op_ev_valid (Mroute_entry->entry_timer))
               op_ev_cancel (Mroute_entry->entry_timer);
            num_oif_interfaces = op_prg_list_size (Mroute_entry->oif);
            if (num_oif_interfaces != 0) {
               for (oif_index = 0; oif_index <num_oif_interfaces; oif_index++) {
                  oif_intf_ptr = op_prg_list_access (Mroute_entry->oif, oif_index); 
                  if (((pim_intf = pim_interface_find (oif_intf_ptr->interface_addr))->interface_type == 
                     OspfC_Interface_Type_Broadcast) && (op_prg_list_size (pim_intf->pim_neighbor_list) == 0) 
                     && (oif_intf_ptr->forward_status == Forward) && (op_prg_list_size (Mlocal_table_ptr) == 0)) {   
                     if (op_ev_valid (oif_intf_ptr->rebroadcast_timer))
                        op_ev_cancel (oif_intf_ptr->rebroadcast_timer); 
                     oif_intf_ptr->rebroadcast_timer = op_intrpt_schedule_call 
                        (op_sim_time () + RebroadcastInterval, 0, 
                         pim_interface_rebroadcast, oif_intf_ptr); 
                     oif_intf_ptr->forward_status = Prune;
                     Mroute_entry->num_forward_intf --;
                     if (Mroute_entry->num_forward_intf == 0) { 
                        pim_prune_message_send (Mroute_entry, Mroute_entry->iif);
                        S_G_copy = (SrcGrpPair *) op_prg_mem_alloc (sizeof (SrcGrpPair));
                        S_G_copy->source = ip_address_copy (S_G->source);
                        S_G_copy->group = ip_address_copy (S_G->group);

                        Mroute_entry->entry_timer = op_intrpt_schedule_call 
                          (op_sim_time () + get_negative_cache_time (Mroute_entry->oif), 0, negative_cache_entry_delete, S_G_copy);       
                     }
                 } 
               }
            } 
         /*} */
         /*else Reset the entry timer 
         if (op_ev  
         Mroute_entry->entry_time = op_intrpt_schedule_call 
           (op_sim_time () + Entry_Time, 0, negative_cache_entry_delete, Mroute_enttry); */       
      }
  }
 /* else { 
      Mroute_entry = Mroute_look_up (Mroute_table_ptr, S_G, &entry_index);
      //printf (" Mroute_entry_ptr = %p\n", Mroute_entry); 
      if ((Mroute_entry->num_forward_intf == 0) && ip_address_equal (msg_intf_addr, Mroute_entry->iif)) {
         num_oif_interfaces = op_prg_list_size (Mroute_entry->oif);
         //printf ("num of oif = %d\n", num_oif_interfaces);
         if (num_oif_interfaces != 0) {
            for (oif_index = 0; oif_index <num_oif_interfaces; oif_index++) {
                oif_intf_ptr = op_prg_list_access (Mroute_entry->oif, oif_index); 
                if (op_ev_valid (oif_intf_ptr->rebroadcast_timer)) {
                      op_ev_cancel (oif_intf_ptr->rebroadcast_timer); 
                      oif_intf_ptr->rebroadcast_timer = op_intrpt_schedule_call 
                         (op_sim_time () + RebroadcastInterval, 0, 
                           pim_interface_rebroadcast, oif_intf_ptr); 
               }
            } 
         }
            
         pim_prune_message_send (Mroute_entry);
         Mroute_entry->entry_timer = op_intrpt_schedule_call 
             (op_sim_time () + Negative_Cache_Time_Out, 0, negative_cache_entry_delete, S_G);
         op_pk_destroy (data);
     } 
      
  }*/
   
   ip_address_destroy (S_G->source);
   ip_address_destroy (S_G->group);
   op_prg_mem_free (S_G);     
   ip_address_destroy (msg_intf_addr);
   ip_address_destroy (src_addr);
   ip_address_destroy (group_addr); 
   Mroute_print ();
   FOUT;
}              
       
 
/* Get same interface table structure as OSPF inteface_table. */
/* Need to wait for the ospf interface table completed        */
void
pim_dm_intface_table_init ()
{
  
  PimDM_Interface*    intf_table_entry_ptr;

  OspfT_Area *		  area_ptr;
  int		    	  num_areas, area_index;                
  int		    	  num_interfaces, interface_index;
  OspfT_Interface *	  interface_ptr;
  List *              ospf_interface_list_ptr;

  /* Create pim_dm interface table */
  pim_dm_interface_table_ptr = op_prg_list_create ();

  /** Find the interfaces connected to the areas in the area list. **/
  /** This report is similar to the one described in D.2.2 in RFC 1247. **/

  /* Loop through the areas and find out an interface table for each one. */
  num_areas = op_prg_list_size (ospf_area_list_ptr_in_pim);
	
  for (area_index = 0; area_index < num_areas; area_index++) {
         
	  /* Get current area. */
	  area_ptr = (OspfT_Area *) op_prg_list_access (ospf_area_list_ptr_in_pim, area_index);
      ospf_interface_list_ptr = area_ptr->interface_list_ptr; 
      num_interfaces = op_prg_list_size (ospf_interface_list_ptr);
	  for (interface_index = 0; interface_index < num_interfaces; interface_index++)
		{
		interface_ptr = (OspfT_Interface *) op_prg_list_access (ospf_interface_list_ptr, 
                 interface_index);
        intf_table_entry_ptr = op_prg_mem_alloc (sizeof (PimDM_Interface));
        intf_table_entry_ptr->interface_type = interface_ptr->interface_type;
        intf_table_entry_ptr->interface_address = ip_address_copy (interface_ptr->interface_address);
        intf_table_entry_ptr->interface_mask = interface_ptr->interface_mask; 
        intf_table_entry_ptr->mtu = interface_ptr->mtu;
        intf_table_entry_ptr->pim_neighbor_list = op_prg_list_create ();
        op_prg_list_insert (pim_dm_interface_table_ptr, intf_table_entry_ptr, 
             OPC_LISTPOS_TAIL);
		}  
   }
}


/* If a Mroute_entry does not exist in Mroute_table, need to create an entry */
void
pim_dm_Mroute_entry_create (IpT_Address src_addr, IpT_Address group_addr, IpT_Address RPF_nbr, 
    IpT_Address msg_intf_addr)
{
  
   PimDM_Mroute_Entry *Mroute_entry;
   PimDM_Interface*    intf_table_entry_ptr;
   int                 pim_dim_intf_table_size;
   int                 intf_index;
   SrcGrpPair         *S_G;
   List               *oif;
   OIF_Interface      *oif_intf_ptr;
   List               *join_list;
   
   FIN (pim_dm_Mroute_entry_create (src_addr, group_addr, RPF_nbr, msg_in_addr));

   Mroute_entry = (PimDM_Mroute_Entry *) op_prg_mem_alloc (sizeof(PimDM_Mroute_Entry));
   S_G = (SrcGrpPair *) op_prg_mem_alloc (sizeof (SGPair));    
   S_G->source = ip_address_copy (src_addr);
   S_G->group = ip_address_copy (group_addr);
   Mroute_entry->SrcGrp = S_G;
   
   Mroute_entry->RPF_neighbor = ip_address_copy (RPF_nbr);
   Mroute_entry->forwarder = ip_address_copy (RPF_nbr);
   Mroute_entry->iif = ip_address_copy (msg_intf_addr); 
  
   
   oif = op_prg_list_create ();
   Mroute_entry->num_forward_intf = 0;
   
   pim_dim_intf_table_size = op_prg_list_size (pim_dm_interface_table_ptr);
   /* Construct output interface list for this entry */ 
   for (intf_index = 0; intf_index < pim_dim_intf_table_size; intf_index++) {
       intf_table_entry_ptr = (PimDM_Interface *) op_prg_list_access 
             (pim_dm_interface_table_ptr, intf_index);
       /* If an interface is connected to a serial link and not the packet arriving interface */
       /* this interface is always set to forward state.                                      */
       if (intf_table_entry_ptr->interface_type != OspfC_Interface_Type_Broadcast) { 
          if (!ip_address_equal (intf_table_entry_ptr->interface_address, msg_intf_addr)) {  
             if (op_prg_list_size (intf_table_entry_ptr->pim_neighbor_list) != 0) {
                oif_intf_ptr = (OIF_Interface *) op_prg_mem_alloc (sizeof (OIF_Interface));
                oif_intf_ptr->forward_status = Forward;
                oif_intf_ptr->interface_addr = ip_address_copy (intf_table_entry_ptr->interface_address); 
                oif_intf_ptr->parent_Mroute_entry_ptr =  Mroute_entry;
                op_prg_list_insert (oif, oif_intf_ptr, OPC_LISTPOS_TAIL); 
                Mroute_entry->num_forward_intf ++;
             }  
          }        
       }    
       else if (!ip_address_equal (intf_table_entry_ptr->interface_address, msg_intf_addr)) { 
             /* If a link is not serial-link, need to consider difference cases */        
             oif_intf_ptr = (OIF_Interface *) op_prg_mem_alloc (sizeof (OIF_Interface));
             
             /* If a router has other down stream neighbors or it has local hosts join the group */
             /* insert this interface into oif list                                              */                                                
             if ((op_prg_list_size (intf_table_entry_ptr->pim_neighbor_list) != 0) || 
                       Mlocal_find (intf_table_entry_ptr->interface_address, group_addr)) {         
                 oif_intf_ptr->forward_status = Forward;          
                 Mroute_entry->num_forward_intf ++;  
             }     
	     	 else {
                oif_intf_ptr->forward_status = Prune;
                //printf ("%s oif_intf_ptr->forward_status = %d\n", name, oif_intf_ptr->forward_status); 
                oif_intf_ptr->rebroadcast_timer = op_intrpt_schedule_call 
                   (op_sim_time () + RebroadcastInterval, 0, pim_interface_rebroadcast, oif_intf_ptr); 
             }
          
             oif_intf_ptr->interface_addr = ip_address_copy (intf_table_entry_ptr->interface_address);
             oif_intf_ptr->parent_Mroute_entry_ptr =  Mroute_entry;
             op_prg_list_insert (oif, oif_intf_ptr, OPC_LISTPOS_TAIL);
            
      }            
   }

   Mroute_entry->oif = oif;
   Mroute_entry->entry_timer = op_intrpt_schedule_call 
           (op_sim_time () + Default_Entry_Time, 0, negative_cache_entry_delete, S_G);

   /* negative cache entry, need to send prune upstream        */
   /* need to find how to detect time out belong to this entry */
   if (Mroute_entry->num_forward_intf == 0) {
       pim_prune_message_send (Mroute_entry, Mroute_entry->iif);
       if (op_ev_valid (Mroute_entry->entry_timer))
          op_ev_cancel (Mroute_entry->entry_timer);
       Mroute_entry->entry_timer = op_intrpt_schedule_call 
           (op_sim_time () + get_negative_cache_time (Mroute_entry->oif), 0, negative_cache_entry_delete, S_G);
   }
   
   op_prg_list_insert (Mroute_table_ptr, Mroute_entry, OPC_LISTPOS_TAIL); 

   
   ip_address_destroy (src_addr);
   ip_address_destroy (group_addr);
   ip_address_destroy (RPF_nbr);
   ip_address_destroy (msg_intf_addr);
  
   //printf ("inside create call mroute print \n");
   Mroute_print ();
   FOUT;
}

 
 
void
pim_hello_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, 
     IpT_Address neighbor_address)
{
   PimDM_Interface* interface_ptr;
 
   FIN (pim_hello_message_rcvd (message_ptr, msg_intf_addr, neighbor_address));
 
   interface_ptr = pim_interface_find (msg_intf_addr);
   if (interface_ptr != OPC_NIL) {
      pim_neighbor_update (interface_ptr, neighbor_address, message_ptr);
      Mroute_update (interface_ptr);
   }
   ip_address_destroy (neighbor_address);
   ip_address_destroy (msg_intf_addr);  
   op_pk_destroy (message_ptr);   
 
   FOUT;

}


void
pim_prune_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, 
     IpT_Address neighbor_addr)
{
   SrcGrpPair *S_G;
   PimDM_Mroute_Entry *Mroute_entry;
   int Mroute_entry_index;
   PimDM_Interface* msg_intf_ptr;
   OIF_Interface *oif_intf_ptr;
   double Holdtime;
   Distribution   *join_delay_time_dist;
   PimDM_Delay_Prune_Message *pim_delay_prune_message;
   IpT_Address   Upstream_Neighbor_Address;
   char        msg_addr_str [IPC_ADDR_STR_LEN];   

   FIN (pim_prune_message_rcvd (message_ptr, msg_intf_addr, neighbor_address));

   ip_address_print (msg_addr_str, msg_intf_addr);
   printf("****** inside %s prune rcvd prune for interface %s\n", name, msg_addr_str);
   S_G = (SrcGrpPair *) op_prg_mem_alloc (sizeof (SrcGrpPair));
   op_pk_nfd_get (message_ptr, "Group_Address", &S_G->group); 
   op_pk_nfd_get (message_ptr, "Pruned_Source_Address", &S_G->source);
   op_pk_nfd_get (message_ptr, "Holdtime", &Holdtime);
   Mroute_entry = Mroute_look_up (Mroute_table_ptr, S_G, &Mroute_entry_index);
   if (Mroute_entry == OPC_NIL) {
       printf("Pruned entry does not exist\n");
       op_pk_destroy (message_ptr);
       ip_address_destroy (msg_intf_addr);
       ip_address_destroy (neighbor_addr);
       ip_address_destroy (S_G->source);
       ip_address_destroy (S_G->group); 
       op_prg_mem_free (S_G);
       FOUT; 
   }
   msg_intf_ptr = pim_interface_find (msg_intf_addr);
   if (msg_intf_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt) {
       /* If a prune is received from a serial link, it will be pruned right away. */
       if ((op_prg_list_size (Mroute_entry->oif) != 0) && ((oif_intf_ptr = find_intf_in_oifs 
           (Mroute_entry->oif, msg_intf_addr)) != OPC_NIL)) {
          if (oif_intf_ptr->forward_status == Forward) {
              oif_intf_ptr->forward_status = Prune;
              Mroute_entry->num_forward_intf --;
          }
          /* A new prune message is received, need to reset the rebroadcast timer */
          if (op_ev_valid (oif_intf_ptr->rebroadcast_timer))
             op_ev_cancel (oif_intf_ptr->rebroadcast_timer);  
          oif_intf_ptr->rebroadcast_timer = op_intrpt_schedule_call 
                  (op_sim_time () + Holdtime, 0, 
                  pim_interface_rebroadcast, oif_intf_ptr); 
          if (Mroute_entry->num_forward_intf == 0) {
             pim_prune_message_send (Mroute_entry, Mroute_entry->iif);
             /* Set the negative cache timer to the oifs that has the maximum time out period */    
             Mroute_entry->entry_timer = op_intrpt_schedule_call 
             (op_sim_time () + get_negative_cache_time (Mroute_entry->oif), 0,
              negative_cache_entry_delete, S_G);
          } 
       } 
   }
   else if (msg_intf_ptr->interface_type == OspfC_Interface_Type_Broadcast) {
       //printf (" %s type = broadcast\n", name);
       if ((op_prg_list_size (Mroute_entry->oif) != 0) && ((oif_intf_ptr = find_intf_in_oifs 
          (Mroute_entry->oif, msg_intf_addr)) != OPC_NIL)) {
          if (oif_intf_ptr->forward_status == Forward) {          
             pim_delay_prune_message = op_prg_mem_alloc (sizeof (PimDM_Delay_Prune_Message));
             pim_delay_prune_message->Mroute_entry = Mroute_entry;
             pim_delay_prune_message->oif_intf_ptr = oif_intf_ptr; 
             pim_delay_prune_message->SrcGrp = S_G;
             pim_delay_prune_message->prune_intf_addr = ip_address_copy (msg_intf_addr);
             oif_intf_ptr->delay_prune_timer = op_intrpt_schedule_call 
                (op_sim_time () +  Delay_Prune_Time, 0, delay_prune, pim_delay_prune_message);
            
          }
          else {
             if (op_ev_valid (oif_intf_ptr->rebroadcast_timer))
                  op_ev_cancel (oif_intf_ptr->rebroadcast_timer);  
             oif_intf_ptr->rebroadcast_timer = op_intrpt_schedule_call 
                  (op_sim_time () + Holdtime, 0, 
                  pim_interface_rebroadcast, oif_intf_ptr); 
             ip_address_destroy (S_G->source);
             ip_address_destroy (S_G->group); 
             op_prg_mem_free (S_G);  
          }
       }
       else {
          /* If a prune is received from input interface, i.e.,an ethernet interface */
          /* a downstream routers need to send join to averwrite the prune. A random */
          /* join timer is set to avoid join explosion from all downs tream routers  */
          if (ip_address_equal (msg_intf_addr, Mroute_entry->iif)) {
             join_delay_time_dist = op_dist_load("uniform", 1, 3);
             Delay_Join_Time = op_dist_outcome (join_delay_time_dist);    
             Mroute_entry->delay_join_timer = op_intrpt_schedule_call 
               (op_sim_time () + Delay_Join_Time, 0, pim_join_message_send, Mroute_entry);                 
          }
          ip_address_destroy (S_G->source);
          ip_address_destroy (S_G->group); 
          op_prg_mem_free (S_G);  
       }
   }
   
     
   ip_address_destroy (msg_intf_addr);
   ip_address_destroy (neighbor_addr); 
   op_pk_nfd_get (message_ptr, "Upstream_Neighbor_Address", &Upstream_Neighbor_Address);
   ip_address_destroy (Upstream_Neighbor_Address);
   op_pk_destroy (message_ptr);
     
 
   FOUT

}            
   


void
pim_graft_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, 
     IpT_Address neighbor_address)
{  
   OIF_Interface *oif_intf_ptr;
   int num_Mroute_entry;
   int Mroute_index;
   PimDM_Mroute_Entry * Mroute_entry;
   IpT_Address Upstream_Neighbor_Address;
   IpT_Address group;
   IpT_Address source;
   
   FIN (pim_graft_message_rcvd (message_ptr, msg_intf_addr, neighbor_address));

   op_pk_nfd_get (message_ptr, "Group_Address", &group);   
   num_Mroute_entry = op_prg_list_size (Mroute_table_ptr);
   for (Mroute_index = 0; Mroute_index < num_Mroute_entry; Mroute_index++) {
      Mroute_entry = op_prg_list_access (Mroute_table_ptr, Mroute_index);        
      if (ip_address_equal (Mroute_entry->SrcGrp->group, group)) {
         oif_intf_ptr = find_intf_in_oifs (Mroute_entry->oif, msg_intf_addr);
         if ((oif_intf_ptr != NULL) && (oif_intf_ptr->forward_status == Prune)) { 
            oif_intf_ptr->forward_status = Forward;          
            if (Mroute_entry->num_forward_intf == 0) {
               Mroute_entry->graft_message_ptr = pim_graft_message_create (Mroute_entry);
               pim_graft_message_send (Mroute_entry); 
               printf ("%s GRAFT SEND +++++++++++++++++++++++++++++++++++++++in rcvd\n", name);
              
            }   
            Mroute_entry->num_forward_intf++;
            pim_graft_ack_message_send (Mroute_entry);
         } 
      }
   }
  
  
   ip_address_destroy (msg_intf_addr);
   ip_address_destroy (neighbor_address); 
   ip_address_destroy (group); 
   op_pk_nfd_get (message_ptr, "Grafted_Source_Address", &source);
   ip_address_destroy (source); 
   op_pk_nfd_get (message_ptr, "Upstream_Neighbor_Address", &Upstream_Neighbor_Address);
   ip_address_destroy (Upstream_Neighbor_Address);
   op_pk_destroy (message_ptr);
     
   FOUT;
}

/* When a graft messge is sent upstream, a resend timer is set. If graft_ack is not received   */
/* in time, graft message will be resend. When graft message is acknowledged, the resend timer */
/* need to be cancelled                                                                        */ 
void
pim_graft_ack_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, 
     IpT_Address neighbor_address)
{ 
   SrcGrpPair *S_G;
   PimDM_Mroute_Entry *Mroute_entry;
   int  Mroute_entry_index;  
   IpT_Address Upstream_Neighbor_Address;
            
   FIN (pim_graft_ack_message_rcvd (message_ptr, msg_intf_addr, neighbor_address));

   op_pk_nfd_get (message_ptr, "Group_Address", &S_G->group);
   op_pk_nfd_get (message_ptr, "Grafted_Source_Address", &S_G->source);
   Mroute_entry = Mroute_look_up (Mroute_table_ptr, S_G, &Mroute_entry_index);
   op_ev_cancel (Mroute_entry-> graft_resend_timer);
   op_pk_destroy (Mroute_entry-> graft_message_ptr);

   ip_address_destroy (S_G->source);
   ip_address_destroy (S_G->group); 
   op_prg_mem_free (S_G);
   ip_address_destroy (msg_intf_addr);
   ip_address_destroy (neighbor_address); 
   op_pk_nfd_get (message_ptr, "Upstream_Neighbor_Address", &Upstream_Neighbor_Address);
   ip_address_destroy (Upstream_Neighbor_Address);
   op_pk_destroy (message_ptr);
   FOUT;
}


void
pim_join_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, 
     IpT_Address neighbor_address)
{
   SrcGrpPair *S_G;
   PimDM_Mroute_Entry *Mroute_entry = OPC_NIL;
   int Mroute_entry_index;
   OIF_Interface *oif_intf_ptr;
   double Holdtime;
   Distribution       *join_delay_time_dist;
   PimDM_Delay_Prune_Message *pim_delay_prune_message;

   FIN (pim_prune_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, 
     IpT_Address neighbor_address));

   //printf("inside join\n");
   S_G = (SrcGrpPair *) op_prg_mem_alloc (sizeof (SrcGrpPair));
   op_pk_nfd_get (message_ptr, "Group_Address", &S_G->group); 
   op_pk_nfd_get (message_ptr, "Joined_Source_Address", &S_G->source);
   //printf("call Mroute lookup\n"); 
   /* DEBUG */
   Mroute_entry = Mroute_look_up (Mroute_table_ptr, S_G, &Mroute_entry_index);
   if (Mroute_entry == OPC_NIL) {
       printf ("Mroute_entry is NULL\n");
       FOUT; 
   }
   
   /* An upsteam router will cancel the pending prune if a join is received from the pending pruned oif */
   if ((op_prg_list_size (Mroute_entry->oif) != 0) && ((oif_intf_ptr = find_intf_in_oifs 
          (Mroute_entry->oif, msg_intf_addr)) != OPC_NIL)) 
       if (op_ev_valid (oif_intf_ptr->delay_prune_timer))
             op_ev_cancel (oif_intf_ptr->delay_prune_timer);
   else /* A router will not send join if other fellow down stream router have snet */  
       if (ip_address_equal (msg_intf_addr, Mroute_entry->iif)) 
             if (op_ev_valid (Mroute_entry->delay_join_timer)) 
                 op_ev_cancel (Mroute_entry->delay_join_timer);             
   FOUT;    
} 


void
pim_assert_message_rcvd (Packet *message_ptr, IpT_Address msg_intf_addr, 
     IpT_Address neighbor_address)
{
   PimDM_Mroute_Entry *Mroute_entry;
   int Mroute_entry_index;
   OIF_Interface *oif_intf_ptr;
   int   my_metric;
   int   assert_metric; 
   SrcGrpPair *S_G;
   char        msg_addr_str [IPC_ADDR_STR_LEN];
   PimDM_Delay_Prune_Message *pim_delay_prune_message;

   FIN (pim_assert_message_rcvd (message_ptr, msg_intf_addr, neighbor_address));

   ip_address_print (msg_addr_str, msg_intf_addr);

   
   op_pk_nfd_get (message_ptr, "Group_Address", &S_G->group); 
   op_pk_nfd_get (message_ptr, "Unicast_Source_Address", &S_G->source);
   op_pk_nfd_get (message_ptr, "Metric", &assert_metric);
   
   /* need to modify mospf to get metric */
   ospf_rte_table_cost_lookup (1000, unicast_dyn_route_table_ptr, S_G->source,
		          &my_metric);

   Mroute_entry = Mroute_look_up (Mroute_table_ptr, S_G, &Mroute_entry_index); 
   if (Mroute_entry == OPC_NIL)
      FOUT;  
   if (Mroute_entry->oif != NULL && ((oif_intf_ptr = find_intf_in_oifs 
          (Mroute_entry->oif, msg_intf_addr)) != NULL)) {
       if (oif_intf_ptr->forward_status == Prune)
          FOUT     
       if ((assert_metric < my_metric) || ((assert_metric == my_metric) && 
              !ip_address_equal (msg_intf_addr, find_bigger_address (msg_intf_addr, neighbor_address)))) {
          /* I am loser */
          if (oif_intf_ptr->forward_status == Forward){            
             oif_intf_ptr->forward_status = Prune;
             oif_intf_ptr->rebroadcast_timer = op_intrpt_schedule_call 
                  (op_sim_time () + Prune_Holdtime, 0, 
                  pim_interface_rebroadcast, oif_intf_ptr); 
             Mroute_entry->num_forward_intf --;
             if (Mroute_entry->num_forward_intf == 0) {
                pim_prune_message_send (Mroute_entry, Mroute_entry->iif);
                Mroute_entry->entry_timer = op_intrpt_schedule_call 
                  (op_sim_time () + get_negative_cache_time (Mroute_entry->oif), 0,
                  negative_cache_entry_delete, S_G);
             
             }   
          }    
          printf ("%s I am loser\n", name);        
       }
       else { /* I am winner. I need to inform others */           
           pim_assert_message_send (Mroute_entry, ip_address_copy (msg_intf_addr), my_metric);
           if (!Mlocal_find (msg_intf_addr, S_G->group)) {
              pim_delay_prune_message = op_prg_mem_alloc (sizeof (PimDM_Delay_Prune_Message));
              pim_delay_prune_message->Mroute_entry = Mroute_entry;
              pim_delay_prune_message->oif_intf_ptr = oif_intf_ptr;
              pim_delay_prune_message->SrcGrp = S_G;
              pim_delay_prune_message->prune_intf_addr = ip_address_copy (msg_intf_addr);
              oif_intf_ptr->delay_prune_timer = op_intrpt_schedule_call 
                (op_sim_time () +  Delay_Prune_Time, 0, delay_prune, pim_delay_prune_message);
           }
       }
   } 
   else {
       if (ip_address_equal (msg_intf_addr, Mroute_entry->iif)) {
          if (!ip_address_equal (neighbor_address, Mroute_entry->forwarder)){
             Mroute_entry->forwarder = neighbor_address;
             Mroute_entry->iif = RPF_interface_get (neighbor_address);
             pim_join_message_send (Mroute_entry);
          }            
      }
  }                 
  ip_address_destroy (S_G->group);
  ip_address_destroy (S_G->source);
  op_prg_mem_free (S_G);
  op_pk_destroy (message_ptr);
  ip_address_destroy (neighbor_address);
  ip_address_destroy (msg_intf_addr);
  FOUT
}



Packet *
pim_hello_message_create ()
{
   Packet *pkptr;

   FIN ( pim_hello_message_create ());

   pkptr = op_pk_create_fmt ("pim_hello_msg");
   op_pk_nfd_set (pkptr, "Type", Hello);
 
   FRET (pkptr);
}


Packet*
pim_join_message_create (PimDM_Mroute_Entry *Mroute_entry)
{
   Packet *pkptr;

   FIN (pim_join_message_create (PimDM_Mroute_Entry *Mroute_entry));

   pkptr = op_pk_create_fmt ("pim_join_prune_msg");
   op_pk_nfd_set (pkptr, "Type", Join_Prune);
   op_pk_nfd_set (pkptr, "Num_Joined_Sources", 1);
   op_pk_nfd_set (pkptr, "Num_Pruned_Sources",0); 
   op_pk_nfd_set (pkptr, "Upstream_Neighbor_Address", ip_address_copy (Mroute_entry->RPF_neighbor), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
   op_pk_nfd_set (pkptr, "Group_Address", ip_address_copy (Mroute_entry->SrcGrp->group), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
   op_pk_nfd_set (pkptr, "Joined_Source_Address", ip_address_copy (Mroute_entry->SrcGrp->source), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
   FRET (pkptr);
}


Packet*
pim_prune_message_create (PimDM_Mroute_Entry *Mroute_entry)
{
   Packet *pkptr;

   FIN (pim_prune_message_create (PimDM_Mroute_Entry *Mroute_entry));

   pkptr = op_pk_create_fmt ("pim_join_prune_msg");
   op_pk_nfd_set (pkptr, "Type", Join_Prune);
   op_pk_nfd_set (pkptr, "Num_Pruned_Sources", 1);
   op_pk_nfd_set (pkptr, "Holdtime", Prune_Holdtime);
   op_pk_nfd_set (pkptr, "Upstream_Neighbor_Address", ip_address_copy (Mroute_entry->RPF_neighbor), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
   op_pk_nfd_set (pkptr, "Group_Address", ip_address_copy (Mroute_entry->SrcGrp->group), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
   op_pk_nfd_set (pkptr, "Pruned_Source_Address", ip_address_copy (Mroute_entry->SrcGrp->source), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
   
   FRET (pkptr);

}

   
Packet*
pim_graft_message_create (PimDM_Mroute_Entry *Mroute_entry)
{
   Packet *pkptr;

   FIN (pim_graft_message_create (PimDM_Mroute_Entry *Mroute_entry));

   pkptr = op_pk_create_fmt ("pim_graft_msg");
   op_pk_nfd_set (pkptr, "Type", Graft);
   op_pk_nfd_set (pkptr, "Upstream_Neighbor_Address", ip_address_copy (Mroute_entry->iif), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
   op_pk_nfd_set (pkptr, "Group_Address", ip_address_copy (Mroute_entry->SrcGrp->group), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
   op_pk_nfd_set (pkptr, "Grafted_Source_Address", ip_address_copy (Mroute_entry->SrcGrp->source), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));

   FRET (pkptr);
}


Packet*
pim_assert_message_create (PimDM_Mroute_Entry *Mroute_entry, int metric)
{
   Packet *pkptr;
 
   FIN (pim_assert_message_create (PimDM_Mroute_Entry *Mroute_entry, int metric));

   //printf ("inside assert create =====\n");
   pkptr = op_pk_create_fmt ("pim_assert_msg");
   op_pk_nfd_set (pkptr, "Type", Assert);
   op_pk_nfd_set (pkptr, "Metric", metric);
   op_pk_nfd_set (pkptr, "Group_Address", ip_address_copy (Mroute_entry->SrcGrp->group), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char)); 
   op_pk_nfd_set (pkptr, "Unicast_Source_Address", ip_address_copy (Mroute_entry->SrcGrp->source), op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
 
   FRET (pkptr);

}


void
pim_hello_message_send (PimDM_Interface *interface_ptr)
	{
	static Ici *		ip_encap_ici_ptr = OPC_NIL;
    List *				neighbor_list_ptr;
	OspfT_Neighbor *	neighbor_ptr;
	int					num_nbrs, nbr_index;
	char				trace_str [128];
    Packet *            message_ptr;
    int                 message_type;
    char                 msg_addr_str [IPC_ADDR_STR_LEN];

	/** Send a hello message on the interface.  On point-to-point and broadcast **/
	/** interfaces, the message should be sent as a multicast on AllSPFRouters. **/
	/** For non-broadcast multi-access interfaces, the message should be sent   **/
	/** as successive unicasts for each neighbor.                               **/
	FIN (pim_hello_message_send (interface_ptr));

    message_ptr = op_pk_copy (interface_ptr->hello_message_ptr);
    interface_ptr-> interface_hello_timer = op_intrpt_schedule_call 
             (op_sim_time () + Pim_Hello_Interval, 0,
              pim_hello_message_send, interface_ptr);

	/* 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");

	if ((interface_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt) ||
		(interface_ptr->interface_type == OspfC_Interface_Type_Broadcast)) {
		/* Set the destination to be All_PIM_ROUTERS. */
		op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", ALL_PIM_ROUTERS);

		/* Since the destination is multicast, then specify the interface to send on. */
        ip_address_print (msg_addr_str, interface_ptr->interface_address);
        //printf ("=============%s Hello send on %s\n", name, msg_addr_str);
		op_ici_attr_set (ip_encap_ici_ptr, "multicast_interface_address", interface_ptr->interface_address);

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

		/* Send the message. */
        
		op_pk_send_forced (message_ptr, pim_dm_router_ip_outstrm);
	}
	else {
		num_nbrs = op_prg_list_size (interface_ptr->pim_neighbor_list);
		for (nbr_index = 0; nbr_index < num_nbrs; nbr_index++) {
			neighbor_ptr = op_prg_list_access (interface_ptr->pim_neighbor_list, nbr_index);
			pim_neighbor_message_send (neighbor_ptr, op_pk_copy (message_ptr), OPC_FALSE);
	    }
		/* Since we never sent the original, destroy it. */
		op_pk_destroy (message_ptr);
	}

	FOUT;
}


void 
pim_join_message_send (PimDM_Mroute_Entry *Mroute_entry) 
{
    static Ici *		ip_encap_ici_ptr = OPC_NIL;
    Packet *            message_ptr;
   char                 msg_addr_str [IPC_ADDR_STR_LEN];

	/** Send a hello message on the interface.  On point-to-point and broadcast **/
	/** interfaces, the message should be sent as a multicast on AllSPFRouters. **/
	FIN (pim_join_message_send (Mroute_entry));

    message_ptr = pim_join_message_create (Mroute_entry);
    
	/* 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 to be All_PIM_ROUTERS. */
	op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", ALL_PIM_ROUTERS);

	/* Since the destination is multicast, then specify the interface to send on. */
    ip_address_print (msg_addr_str, Mroute_entry->iif);
    //printf ("=============%s Join send on %s\n", name, msg_addr_str);
	op_ici_attr_set (ip_encap_ici_ptr, "multicast_interface_address", Mroute_entry->iif);

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

	/* Send the message. */
	op_pk_send_forced (message_ptr, pim_dm_router_ip_outstrm);

    FOUT;
}      


void 
pim_graft_message_send (PimDM_Mroute_Entry *Mroute_entry)
{
    static Ici *		ip_encap_ici_ptr = OPC_NIL;
    Packet *            message_ptr;
   char                 msg_addr_str [IPC_ADDR_STR_LEN];

	/** Send a hello message on the interface.  On point-to-point and broadcast **/
	/** interfaces, the message should be sent as a multicast on AllSPFRouters. **/
	/** For non-broadcast multi-access interfaces, the message should be sent   **/
	/** as successive unicasts for each neighbor.                               **/
	FIN (pim_graft_message_send (Mroute_table_entry));

    message_ptr = op_pk_copy (Mroute_entry->graft_message_ptr);
    
	/* 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 to be All_PIM_ROUTERS. */
	op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", ALL_PIM_ROUTERS);

	/* Since the destination is multicast, then specify the interface to send on. */
    ip_address_print (msg_addr_str, Mroute_entry->iif);
    //printf ("=============%s Graft send on %s\n", name, msg_addr_str);
	op_ici_attr_set (ip_encap_ici_ptr, "multicast_interface_address", Mroute_entry->iif);

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

	/* Send the message. */
	op_pk_send_forced (message_ptr, pim_dm_router_ip_outstrm);

    Mroute_entry->graft_resend_timer = op_intrpt_schedule_call 
             (op_sim_time () + Graft_Resend_Time, 0, pim_graft_message_send, Mroute_entry);  
    FOUT;   
}   


void 
pim_prune_message_send (PimDM_Mroute_Entry *Mroute_entry, IpT_Address prune_intf_addr) 
{
    static Ici *		ip_encap_ici_ptr = OPC_NIL;
    Packet *            message_ptr;
    char                 msg_addr_str [IPC_ADDR_STR_LEN];

	/** Send a prune message on the interface.  On point-to-point and broadcast **/
	/** interfaces, the message should be sent as a multicast on AllSPFRouters. **/
	/** For non-broadcast multi-access interfaces, the message should be sent   **/
	/** as successive unicasts for each neighbor.                               **/
	FIN (pim_prune_message_send (Mroute_entry, prune_intf_addr));

    message_ptr = pim_prune_message_create (Mroute_entry);
    
	/* 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 to be All_PIM_ROUTERS. */
	op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", ALL_PIM_ROUTERS);

	/* Since the destination is multicast, then specify the interface to send on. */
   
    ip_address_print (msg_addr_str, Mroute_entry->iif);
    //printf ("=============%s Prune send on %s\n", name, msg_addr_str);
	op_ici_attr_set (ip_encap_ici_ptr, "multicast_interface_address", prune_intf_addr);

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

	/* Send the message. */
	op_pk_send_forced (message_ptr, pim_dm_router_ip_outstrm);

    FOUT;
}      


void 
pim_assert_message_send (PimDM_Mroute_Entry *Mroute_entry, IpT_Address in_addr, int metric)
{
    static Ici *		ip_encap_ici_ptr = OPC_NIL;
    Packet *            message_ptr;
   char                 msg_addr_str [IPC_ADDR_STR_LEN];

	/** Send a hello message on the interface.  On point-to-point and broadcast **/
	/** interfaces, the message should be sent as a multicast on AllSPFRouters. **/
	/** For non-broadcast multi-access interfaces, the message should be sent   **/
	/** as successive unicasts for each neighbor.                               **/
	FIN (pim_assert_message_send (Mroute_table_entry));

    message_ptr = pim_assert_message_create (Mroute_entry, metric);
    
    //printf ("assert create\n");

	/* 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 to be All_PIM_ROUTERS. */
	op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", ALL_PIM_ROUTERS);

	/* Since the destination is multicast, then specify the interface to send on. */
     ip_address_print (msg_addr_str, in_addr);
    //printf ("=============%s Assert send on %s\n", name, msg_addr_str);
	op_ici_attr_set (ip_encap_ici_ptr, "multicast_interface_address", in_addr);

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

	/* Send the message. */
	op_pk_send_forced (message_ptr, pim_dm_router_ip_outstrm);

    FOUT;   
}   

void
ip_data_send (Packet *message_ptr, IpT_Address dest_addr, IpT_Address src_addr)
	{
	static Ici *		ip_encap_ici_ptr = OPC_NIL;
    
	/**  Send data packet back to IP to forward it based on the new Mroute_entry **/
	FIN (ip_data_send (Packet *message_ptr, IpT_Address dest_addr, IpT_Address src_addr));
    
	/* 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 ("ip3m_encap_req");

	/* Set the destination and source address */
	op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", dest_addr);
    op_ici_attr_set (ip_encap_ici_ptr, "src_addr", src_addr);

    /* Since it is data packet instead of control packet, specify it for IP forward*/
	op_ici_attr_set (ip_encap_ici_ptr, "data_packet", 1);

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

	/* Send the message. */    
	op_pk_send_forced (message_ptr, pim_dm_router_ip_outstrm);
	
	FOUT;
}


void 
pim_graft_ack_message_send (PimDM_Mroute_Entry *Mroute_entry) 
{
}

void
pim_neighbor_message_send (PimDM_Neighbor *neighbor_ptr, Packet *message, Boolean status)
{

}

void
pim_interface_hello_send ()
{
   int                 num_pim_dm_interface_table;
   int                 intf_index;
   PimDM_Interface*    intf_table_entry_ptr;
   PimDM_Interface   * interface_ptr;

   
   num_pim_dm_interface_table = op_prg_list_size (pim_dm_interface_table_ptr);
   for (intf_index = 0; intf_index < num_pim_dm_interface_table; intf_index++) {
      interface_ptr = (PimDM_Interface *) op_prg_list_access 
         (pim_dm_interface_table_ptr, intf_index); 
      interface_ptr->hello_message_ptr = pim_hello_message_create ();
      pim_hello_message_send (interface_ptr);
  }
        
}


void 
delay_prune (PimDM_Delay_Prune_Message *pim_delay_prune_message, int intrpt_code)
{
   OIF_Interface *oif_intf_ptr;
   PimDM_Mroute_Entry *Mroute_entry;
   SrcGrpPair* S_G;   

   FIN (delay_prune (pim_delay_prune_message, intrpt_code));

   Mroute_entry = pim_delay_prune_message->Mroute_entry;
   oif_intf_ptr = pim_delay_prune_message->oif_intf_ptr; 
   S_G = pim_delay_prune_message->SrcGrp; 

   //printf ("%s pim_delay_prune_message ptr = %p,  pim_delay_prune_message->Mroute_entry = %p 
      //oif_intf_ptr = %p \n", name, pim_delay_prune_message, pim_delay_prune_message->Mroute_entry, oif_intf_ptr);
   oif_intf_ptr->forward_status = Prune;
 
   oif_intf_ptr->rebroadcast_timer = op_intrpt_schedule_call 
                  (op_sim_time () + Prune_Holdtime, 0, 
                  pim_interface_rebroadcast, oif_intf_ptr); 
   //printf ("here4\n");
   Mroute_entry->num_forward_intf --;
  
   if (Mroute_entry->num_forward_intf == 0) {
      pim_prune_message_send (Mroute_entry, pim_delay_prune_message->prune_intf_addr);
      Mroute_entry->entry_timer = op_intrpt_schedule_call 
             (op_sim_time () + get_negative_cache_time (Mroute_entry->oif), 0,
              negative_cache_entry_delete, S_G);
   }

   FOUT;

}     


double
get_negative_cache_time (List *oif)
{  
   int num_oifs;
   OIF_Interface *oif_intf_ptr;
   int oif_index;
   double max_time_out = 0;
   double rebroadcast_time;  
 
   FIN (get_negative_cache_time (oif));

   num_oifs = op_prg_list_size (oif);
   if (num_oifs) {
      for (oif_index = 0; oif_index<num_oifs; oif_index++) {
          oif_intf_ptr = (OIF_Interface *) op_prg_list_access (oif, oif_index);
          if ((rebroadcast_time = op_ev_time (oif_intf_ptr->rebroadcast_timer)) > max_time_out) 
             max_time_out = rebroadcast_time;
      }
   }
   FRET (max_time_out);
}


OIF_Interface *
find_intf_in_oifs (List *oif, IpT_Address in_addr)
{
   int num_oifs;
   OIF_Interface *oif_intf_ptr;
   int oif_index;

   FIN (find_intf_in_oifs (List *oif, IpT_Address in_addr));
   
   //printf ("inside find_intf_in_oifs\n");

   num_oifs = op_prg_list_size (oif); 
   if (num_oifs) {
      for (oif_index = 0; oif_index<num_oifs; oif_index++) {
          oif_intf_ptr = (OIF_Interface *) op_prg_list_access (oif, oif_index);
          if (ip_address_equal (oif_intf_ptr->interface_addr, in_addr)) {
             //printf ("inside find_intf_in_oifs return item\n");
             FRET (oif_intf_ptr);
          }
      }
   }
    //printf ("inside find_intf_in_oifs return null\n");
   FRET (OPC_NIL);
}



PimDM_Mroute_Entry *
Mroute_look_up (List *Mroute_tbl_ptr, SrcGrpPair *S_G, int *entry_index)
{
   int num_Mroute_entry;
   int Mroute_index;
   PimDM_Mroute_Entry * Mroute_table_entry;
   char                tmp_src_addr_str [IPC_ADDR_STR_LEN]; 
   char                tmp_dest_addr_str [IPC_ADDR_STR_LEN];
 
   FIN (Mroute_look_up (SrcGrpPair *S_G, int *entry_index));

   num_Mroute_entry = op_prg_list_size (Mroute_tbl_ptr);
   if (num_Mroute_entry == 0) {    
      *entry_index = 0;
       //printf ("Mroute empty\n"); 
       FRET (OPC_NIL);
     
   }
   /*
   //printf ("S_G ptr = %p", S_G);
   //printf ("src_ptr = %p  group_ptr = %p", S_G->source, S_G->group);
   ip_address_print (tmp_src_addr_str,  S_G->source);
   ip_address_print (tmp_dest_addr_str, S_G->group);
   //printf ("in Mroute_look_up: src_addr = %s, dest_addr = %s num_Mroute_entry = %d\n", tmp_src_addr_str, tmp_dest_addr_str,num_Mroute_entry);
   */

   for (Mroute_index = 0; Mroute_index < num_Mroute_entry; Mroute_index++) { 
       Mroute_table_entry = op_prg_list_access (Mroute_tbl_ptr, Mroute_index);
       
       if (ip_address_equal (Mroute_table_entry->SrcGrp->source, S_G->source) && 
            ip_address_equal (Mroute_table_entry->SrcGrp->group, S_G->group))  {
         *entry_index = Mroute_index;
         /*ip_address_destroy (S_G->source);
         ip_address_destroy (S_G->group);  
         op_prg_mem_free (S_G);*/  
            
         FRET (Mroute_table_entry);
         
       }
   }
   /*ip_address_destroy (S_G->source);
   ip_address_destroy (S_G->group);  
   op_prg_mem_free (S_G); */
   FRET (OPC_NIL);
   
}

/*
void
Mlocal_find_G (List ** join_src_grp_list, IpT_Address source, IpT_Address group)  
{   
  Mlocal_Entry * mlocal_entry_ptr;
  int            num_mlocal_entry;
  int            mlocal_entry_index;
  List *         tmp_list;
  Mlocal_Entry * tmp_entry_ptr; 
   
  FIN (Mlocal_find_G (List ** join_src_grp_list, IpT_Address source, IpT_Address group));

  tmp_list = op_prg_list_create ();
  //printf ("Mlocal_Table_ptr = %p\n", Mlocal_table_ptr);
  if ((num_mlocal_entry =  op_prg_list_size (Mlocal_table_ptr)) == 0) {
     //printf ("Mlocal is empty\n");
     op_prg_list_free (tmp_list);
     FOUT;
  }
  else {
     for (mlocal_entry_index = 0; mlocal_entry_index <num_mlocal_entry; mlocal_entry_index++) {
         mlocal_entry_ptr = op_prg_list_access (Mlocal_table_ptr, mlocal_entry_index);
         //printf ("inside Mlocal_find_G\n");
          
         if (ip_address_equal (mlocal_entry_ptr->group, group)) {
             tmp_entry_ptr = (Mlocal_Entry *)op_prg_mem_alloc (sizeof (Mlocal_Entry));
             tmp_entry_ptr->group = ip_address_copy (group);
             op_prg_list_insert (tmp_list, tmp_entry_ptr, OPC_LISTPOS_TAIL);
         }
         
     }
     *join_src_grp_list = tmp_list;                     
  }
  FOUT;
}
*/


/* DEBUG need to consider what to put on the src_grp_list*/
/*void
Mlocal_find (List ** join_src_grp_list, IpT_Address intf_addr, IpT_Address group_addr)  
{   
  Mlocal_Entry * mlocal_entry_ptr;
  int            num_mlocal_entry;
  int            mlocal_entry_index;
  int            num_group_list, group_index;
  List *         tmp_list;
  Mlocal_Entry * tmp_entry_ptr; 
   
  FIN (Mlocal_find (join_src_grp_list, intf_addr, group));

  tmp_list = op_prg_list_create ();
  if ((num_mlocal_entry =  op_prg_list_size (Mlocal_table_ptr)) == 0) {
     //printf ("Mlocal is empty\n");
    *join_src_grp_list = tmp_list;
     FOUT;
  }
  else {
     for (mlocal_entry_index = 0; mlocal_entry_index <num_mlocal_entry; mlocal_entry_index++) {
         mlocal_entry_ptr = op_prg_list_access (Mlocal_table_ptr, mlocal_entry_index);
         //printf ("inside Mlocal_find\n");
          
         if (ip_address_equal (mlocal_entry_ptr->intf_addr, intf_addr)) {
             if ((num_group_list = op_prg_list_size (mlocal_entry_ptr->group_list)) == 0) {
               *join_src_grp_list = tmp_list;
                FOUT;
             }
             else {
                for (group_index = 0; group_index < num_group_list;  group_index++) {
                   if (ip_address_equal ((IpT_Address) op_prg_list_access (mlocal_entry_ptr->group_list, group_index),
                       group_addr))  {       
                       tmp_entry_ptr = (Mlocal_Entry *)op_prg_mem_alloc (sizeof (Mlocal_Entry));
                       tmp_entry_ptr->intf_addr = ip_address_copy (intf_addr);
                       tmp_entry_ptr->group_list = op_prg_list_create ();
                       op_prg_list_insert (tmp_entry_ptr->group_list, ip_address_copy (group_addr), OPC_LISTPOS_TAIL);
                       op_prg_list_insert (tmp_list, tmp_entry_ptr, OPC_LISTPOS_TAIL);
                      *join_src_grp_list = tmp_list; 
                       FOUT;    
                   }
                }
             }
         }        
     }
    *join_src_grp_list = tmp_list;
     FOUT;               
  }
 
}*/


Mlocal_find (IpT_Address intf_addr, IpT_Address group_addr)  
{   
  Mlocal_Entry * mlocal_entry_ptr;
  int            num_mlocal_entry;
  int            mlocal_entry_index;
  int            num_group_list, group_index;
  
   
  FIN (Mlocal_find (intf_addr, group));

  //printf ("*****************%s inside Mlocal_find ***********************\n", name);
 
  if ((num_mlocal_entry =  op_prg_list_size (Mlocal_table_ptr)) == 0) {
     //printf ("Mlocal is empty\n");
     FRET(FALSE);
  }
  else {
     for (mlocal_entry_index = 0; mlocal_entry_index <num_mlocal_entry; mlocal_entry_index++) {
         mlocal_entry_ptr = op_prg_list_access (Mlocal_table_ptr, mlocal_entry_index);
         if (ip_address_equal (mlocal_entry_ptr->intf_addr, intf_addr)) 
             if ((num_group_list = op_prg_list_size (mlocal_entry_ptr->group_list)) == 0) 
                FRET (FALSE)
             else 
                for (group_index = 0; group_index < num_group_list;  group_index++) 
                   if (ip_address_equal ((IpT_Address) op_prg_list_access (mlocal_entry_ptr->group_list, group_index),
                       group_addr))                      
                       FRET (TRUE);          
     }
     FRET (FALSE);               
  } 
}


List *
Mroute_look_up_for_G (IpT_Address group)
{

}



PimDM_Interface*
pim_interface_find (IpT_Address in_addr)
{
  int              num_pim_dm_interface_table;
  int              intf_index;
  PimDM_Interface* intf_table_entry_ptr;
  
  FIN (pim_interface_find (in_addr));

  num_pim_dm_interface_table = op_prg_list_size (pim_dm_interface_table_ptr);
  
  for (intf_index = 0; intf_index < num_pim_dm_interface_table; intf_index++) {
      intf_table_entry_ptr = (PimDM_Interface *) op_prg_list_access 
         (pim_dm_interface_table_ptr, intf_index);      
      if (ip_address_equal (in_addr, intf_table_entry_ptr->interface_address)) 
         FRET (intf_table_entry_ptr) 
  }
  FRET (OPC_NIL)
}    
 


void 
pim_neighbor_update (PimDM_Interface* intf_ptr,  IpT_Address neighbor_addr,  Packet* msg_ptr) 
{

   int             num_neighbor_list;
   int             neighbor_index;
   PimDM_Neighbor *neighbor_ptr;
   int             neighbor_hold_time;
   
   FIN (pim_neighbor_update (intf_ptr, neighbor_addr, msg_ptr));
   if (op_prg_list_size (intf_ptr -> pim_neighbor_list) != 0) {
      num_neighbor_list = op_prg_list_size (intf_ptr -> pim_neighbor_list);
      for (neighbor_index = 0; neighbor_index < num_neighbor_list; neighbor_index++) {
          neighbor_ptr = op_prg_list_access (intf_ptr->pim_neighbor_list, neighbor_index);
          if (ip_address_equal (neighbor_ptr->neighbor_address, neighbor_addr)) {
             /*op_pk_nfd_get (msg_ptr, "Hello_Hold_Time", &neighbor_hold_time);*/
             if (op_ev_valid (neighbor_ptr->neighbor_time_out_handle))
                op_ev_cancel (neighbor_ptr->neighbor_time_out_handle);
             neighbor_ptr->neighbor_time_out_handle = op_intrpt_schedule_call 
                (op_sim_time () + Pim_Hello_Holdtime, 0, neighbor_delete, neighbor_ptr); 
             FOUT;
          }                       
      }    
   }
  
   /* Eigher neighbor list is empty or this neighbor is not in the list */
   /* A new neighbor is found and should be inserted to neighbor list   */  
   neighbor_ptr = pim_neighbor_create (intf_ptr, neighbor_addr, msg_ptr);
   op_prg_list_insert (intf_ptr->pim_neighbor_list, neighbor_ptr,
        OPC_LISTPOS_TAIL); 
   FOUT;    
}


PimDM_Neighbor *
pim_neighbor_create (PimDM_Interface* intf_ptr, IpT_Address neighbor_addr, Packet *message_ptr) 
{
   PimDM_Neighbor *neighbor_ptr;
   int             neighbor_hold_time;

   //printf ("inside neighbor create \n ");

   neighbor_ptr = (PimDM_Neighbor *) op_prg_mem_alloc (sizeof(PimDM_Neighbor));
   neighbor_ptr -> neighbor_address = ip_address_copy (neighbor_addr);
   neighbor_ptr -> parent_intf_ptr = intf_ptr;
   /*op_pk_nfd_get (message_ptr, "Hello_hold_time", &neighbor_hold_time); */
   neighbor_ptr->neighbor_time_out_handle = op_intrpt_schedule_call 
              (op_sim_time () + Pim_Hello_Holdtime, 0, neighbor_delete, neighbor_ptr);  
   return neighbor_ptr;
};


/* When hello is received from a neighbor, need to check if Mroute table need to be modified.    */
/* This will be very useful at the beginning of simulation and for dealing with network dynamics */                                                  
void
Mroute_update (PimDM_Interface* interface_ptr)
{
   int num_Mroute_entry;
   int Mroute_index;
   PimDM_Mroute_Entry * Mroute_entry;   
   OIF_Interface * oif_intf_ptr;
   List   *join_list;
   Boolean graft = OPC_FALSE;

   FIN (Mroute_update (PimDM_Interface* interface_ptr));
 
   num_Mroute_entry = op_prg_list_size (Mroute_table_ptr);
   if (num_Mroute_entry == 0)        
       FOUT; 
   for (Mroute_index = 0; Mroute_index < num_Mroute_entry; Mroute_index++) {     

       Mroute_entry = op_prg_list_access (Mroute_table_ptr, Mroute_index);   

       /* If a new neighbor comes up, need to put it in the oif list        */
       /* Note a router always forward packets if it has downstream routers */
       /* This is only applicable to PIM-DM, not PIM-SM. PIM-SM need to send*/
       /* Join for getting the packets. Check PIM-SM for possible           */
       /* modification                                                      */     
       if (find_intf_in_oifs (Mroute_entry->oif, interface_ptr->interface_address) == OPC_NIL) {       
          if ((op_prg_list_size (interface_ptr->pim_neighbor_list) != 0) 
                 && !ip_address_equal (interface_ptr->interface_address, Mroute_entry->iif)) {
              //printf ("create intf\n");
              oif_intf_ptr = (OIF_Interface *) op_prg_mem_alloc (sizeof (OIF_Interface));
              oif_intf_ptr->forward_status = Forward;
              oif_intf_ptr->interface_addr = ip_address_copy (interface_ptr->interface_address); 
              op_prg_list_insert (Mroute_entry->oif, oif_intf_ptr, OPC_LISTPOS_TAIL); 
              if (Mroute_entry->num_forward_intf == 0)
                 graft = OPC_TRUE; 
              Mroute_entry->num_forward_intf ++;     
              if (graft) {
                  Mroute_entry->graft_message_ptr = pim_graft_message_create (Mroute_entry);
                  pim_graft_message_send (Mroute_entry);
                  //printf ("%s GRAFT SEND +++++++++++++++++++++++++++++++++++++++\n", name);
              } 
            
            //printf ("entry get updated\n");                    
          }
       }       
    } 
    //printf ("Mroute_update\n");
    Mroute_print ();  
    FOUT;    
}        

void
negative_cache_entry_delete (SrcGrpPair *S_G, int intrpt_code)
{ 
   
   int entry_index;

   FIN (negative_cache_entry_delete (S_G, intrpt_code));

   Mroute_look_up (Mroute_table_ptr, S_G, &entry_index);
   if (entry_index != 0) 
      op_prg_list_remove (Mroute_table_ptr, entry_index);

   FOUT;
}


/*
void
negative_cache_entry_delete (PimDM_Mroute_Entry *Mroute_entry, int intrpt_code)
{   
   int entry_index;
   int num_oif, oif_index;
   OIF_Interface *oif_intf;   
  
   FIN (negative_cache_entry_delete (Mroute_entry->SrcGrp, intrpt_code));

   Mroute_look_up (Mroute_table_ptr, S_G, &entry_index);
   if (entry_index != 0) {
      if (op_ev_valid (Mroute_entry->entry_time))
         op_ev_cancel (Mroute_entry->entry_time);
      if (op_ev_valid (Mroute_entry->graft_resend_timer)) {
         op_ev_cancel (Mroute_entry->graft_resend_timer);
      DEBUG need to release the message here too  
      }            
      if (op_ev_valid (Mroute_entry->delay_join_timer))
          op_ev_cancel (Mroute_entry->delay_join_timer);
      ip_address_destroy (Mroute_entry->SrcGrp->source);
      ip_address_destroy (Mroute_entry->SrcGrp->group);
      op_prg_mem_free (Mroute_entry->SrcGrp);
      ip_address_destroy (Mroute_entry->iif); 
      num_oif = op_prg_list_size (Mroute_entry->oif);
      if (num_oif != 0)
         for (oif_index = 0; oif_index < num_oif; oif_index ++) {
             oif_intf = op_prg_list_access (Mroute_entry->oif, oif_index);
             if (op_ev_valid (oif_intf->rebroadcast_timer))  
                 op_ev_cancel (oif_intf->rebroadcast_timer);  
             if (op_ev_valid (oif_intf->delay_prune_timer))  
                 op_ev_cancel (oif_intf->delay_prune_timer);               
             ip_address_destroy (oif_intf->interface_addr); 
             op_prg_mem_free (oif_intf);
         }
      op_prg_list_remove (Mroute_table_ptr, entry_index);
   }
   FOUT;
}*/


void
neighbor_delete (PimDM_Neighbor *neighbor_ptr, int intrpt_code)
{ 
  PimDM_Interface* intf_ptr;
  int neighbor_index;
  int last_index;

  intf_ptr = neighbor_ptr->parent_intf_ptr;
  op_prg_list_elem_find (intf_ptr->pim_neighbor_list, strcmp, neighbor_ptr, &neighbor_index, &last_index);
  op_prg_list_remove (intf_ptr->pim_neighbor_list, neighbor_index);    
}


void 
pim_interface_rebroadcast(OIF_Interface *oif_intf_ptr, int intrpt_code)
{
   FIN (pim_interface_rebroadcast(oif_intf_ptr, intrpt_code));
    
   oif_intf_ptr->forward_status = Forward;
   if (oif_intf_ptr->parent_Mroute_entry_ptr->num_forward_intf == 0) {
      oif_intf_ptr->parent_Mroute_entry_ptr->graft_message_ptr = pim_graft_message_create (oif_intf_ptr->parent_Mroute_entry_ptr);
      pim_graft_message_send (oif_intf_ptr->parent_Mroute_entry_ptr); 
      //printf ("%s GRAFT SEND +++++++++++++++++++++++++++++++++++++++in rcvd\n", name);
   }    
   oif_intf_ptr->parent_Mroute_entry_ptr->num_forward_intf++;

   FOUT;

} 

/* void
pim_interface_rebroadcast (OIF_Interface *oif_intf_ptr, int intrpt_code)
{
   PimDM_Interface* interface_ptr;
  
   FIN (pim_interface_rebroadcast(oif_intf_ptr, intrpt_code));
 
   //printf ("inside rebroadcast\n");
   interface_ptr = pim_interface_find (oif_intf_ptr->interface_addr);

   if ((op_prg_list_size (interface_ptr->pim_neighbor_list) != 0) || 
         Mlocal_find (interface_ptr->interface_address, oif_intf_ptr->parent_Mroute_entry_ptr->SrcGrp->group)) {         
       oif_intf_ptr->forward_status = Forward;          
       oif_intf_ptr->parent_Mroute_entry_ptr->num_forward_intf ++;  
   }   
   if (oif_intf_ptr->parent_Mroute_entry_ptr->num_forward_intf == 0)   
      oif_intf_ptr->rebroadcast_timer = op_intrpt_schedule_call 
                   (op_sim_time () + RebroadcastInterval, 0, pim_interface_rebroadcast, oif_intf_ptr);
   if (oif_intf_ptr->parent_Mroute_entry_ptr->num_forward_intf == 0) {
       pim_prune_message_send (oif_intf_ptr->parent_Mroute_entry_ptr, oif_intf_ptr->parent_Mroute_entry_ptr->iif);
       oif_intf_ptr->parent_Mroute_entry_ptr->entry_timer = op_intrpt_schedule_call 
           (op_sim_time () + get_negatice_cache_entry (oif_intf_ptr->parent_Mroute_entry_ptr->oif), 0, negative_cache_entry_delete, oif_intf_ptr->parent_Mroute_entry_ptr->SrcGrp);
   }

   FOUT;
}
*/   






void
Mroute_print ()
{
   int num_Mroute_entry;
   int Mroute_index;
   PimDM_Mroute_Entry * Mroute_table_entry;
   char         group_addr_str [IPC_ADDR_STR_LEN];
   char         src_addr_str [IPC_ADDR_STR_LEN];
   char         RPF_str [IPC_ADDR_STR_LEN];
   char			iif_str [IPC_ADDR_STR_LEN];
   char         oif_intf_str [IPC_ADDR_STR_LEN];
   char         forwarder_str [IPC_ADDR_STR_LEN];
   char			msg [256], msg_str [256];
   int          num_oifs, index_oif;	
   OIF_Interface *oif_entry_ptr; 	

   FIN (Mroute_print ());
   num_Mroute_entry = op_prg_list_size (Mroute_table_ptr); 
   if (num_Mroute_entry == 0) {
       //printf ("entry is empty\n");
       FOUT;
   }  

   //printf("%s Mroute_table\n", name);
   for (Mroute_index = 0; Mroute_index < num_Mroute_entry; Mroute_index++) {
       Mroute_table_entry = op_prg_list_access (Mroute_table_ptr, Mroute_index);
       ip_address_print (group_addr_str, Mroute_table_entry->SrcGrp->group);
	   ip_address_print (src_addr_str, Mroute_table_entry->SrcGrp->source);
       ip_address_print (RPF_str, Mroute_table_entry->RPF_neighbor);
	   ip_address_print (iif_str, Mroute_table_entry->iif);
       ip_address_print (forwarder_str, Mroute_table_entry->forwarder);
       printf ("-----------------------------------------------------------------------------\n");
       printf ("index = %d, source = %s, group = %s\n", Mroute_index, src_addr_str, group_addr_str);
       printf ("num_forward = %d, RPF = %s,  iif = %s \n", Mroute_table_entry->num_forward_intf, RPF_str, iif_str);
       num_oifs = op_prg_list_size (Mroute_table_entry->oif);
       if (num_oifs != 0 ) {
          for (index_oif = 0; index_oif < num_oifs; index_oif ++) {        
              oif_entry_ptr = op_prg_list_access (Mroute_table_entry->oif, index_oif);                    
              ip_address_print (oif_intf_str, oif_entry_ptr->interface_addr);
              printf ("interface %d addr is %s, status is %d\n", index_oif, oif_intf_str, oif_entry_ptr->forward_status);
          }
          printf ("-----------------------------------------------------------------------------\n\n\n");
       }
       else {
          printf ("oif is null\n");
          printf ("-----------------------------------------------------------------------------\n\n\n");
       }
   }
   FOUT;
}

IpT_Address 
RPF_interface_get (IpT_Address next_hop_addr)
	{
	int						i;
	IpT_Interface_Info *	iface_elem_ptr;
	int						iface_table_size;
    List                *   iface_table_ptr;    
    char        RPF_addr_str [IPC_ADDR_STR_LEN];
    char        RPF_in_addr_str [IPC_ADDR_STR_LEN];  
     
	/** This procedure is used to determine the correct outstream index and mtu **/
	/** size based on the next hop address.                                     **/
	FIN (ip_outstrm_and_mtu_determine (iface_table_ptr, next_hop_addr, mtu_size_ptr));

    ip_address_print (RPF_addr_str, next_hop_addr);
              
    iface_table_ptr = ip_info_ptr->ip_iface_table_ptr;

	/* Loop through all interfaces and determine which interface */
	/* address range the next hop belongs to.                    */
	iface_table_size = op_prg_list_size (iface_table_ptr);

   
	for (i = 0; i < iface_table_size; i++)
		{

 		iface_elem_ptr = (IpT_Interface_Info *) op_prg_list_access (iface_table_ptr, i);
         
        ip_address_print (RPF_in_addr_str, iface_elem_ptr->addr_range_ptr->address);
               
        //printf ("RPF = %s RPF_intf_addr = %s\n", RPF_addr_str, RPF_in_addr_str); 

		/* Does the next hop exist within the address range this interface */
		/* connects to?                                                    */
		if (ip_address_range_check (next_hop_addr, iface_elem_ptr->addr_range_ptr))
			FRET (iface_elem_ptr->addr_range_ptr->address);
			
		}

	/* If no output stream is found, return an error flag and signal an error message.	*/
	FRET (OPC_NIL);
	}


Boolean
RPF_neighbor_get (IpT_Address *RPF_addr_ptr, IpT_Address *RPF_in_addr_ptr, IpT_Address dest_addr)
{
    IpT_Address dummy_fast_address = ip_address_create ("0.0.0.0");
    Compcode	route_status;
    int         num_in_assoc;    
    int         i;
    List *		proc_record_handle_list_ptr;    
    Objid       next_hop_router_id, strm_objid;
    OspfT_Interface *		interface_ptr;
	/*IpT_Info *				ip_info_ptr = OPC_NIL;
	IpT_Interface_Info *	ip_interface_ptr;*/
    int						num_intf, intf_index, RPF_instrm = 100;
	OmsT_Pr_Handle			ip_proc_record_handle;

    FIN (RPF_neighbor_get (RPF_addr_ptr, RPF_in_addr_ptr, dest_addr));

    proc_record_handle_list_ptr = op_prg_list_create ();
	/* invoke a routing table lookup to find the next hop.                       */
	if ((route_status = (*unicast_rte_select_func_ptr) (1000, unicast_dyn_route_table_ptr, dest_addr,
		RPF_addr_ptr)) == OPC_COMPCODE_FAILURE) {
        printf ("RPF is not available now\n");
        FRET (OPC_FALSE);
    }
    else {     
        num_in_assoc = op_topo_assoc_count (own_node_objid, OPC_TOPO_ASSOC_IN, OPC_OBJMTYPE_NODE);
        for (i = 0; i < 1; i++) {
             //printf ("num of assoc = %d\n", num_in_assoc);
             next_hop_router_id = op_topo_assoc (own_node_objid, OPC_TOPO_ASSOC_IN, OPC_OBJMTYPE_NODE, i);
             oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr,
  		     "protocol", 	OMSC_PR_STRING, 	"ip",
		     "node objid", 	OMSC_PR_OBJID, 		next_hop_router_id,
		     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 next hop 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);
             num_intf = op_prg_list_size (ip_info_ptr->ip_iface_table_ptr);
		     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);
		        if (ip_address_equal (ip_interface_ptr->addr_range_ptr->address , *RPF_addr_ptr)) {
                    strm_objid = op_topo_connect (next_hop_router_id, own_node_objid, OPC_OBJTYPE_STRM, 0);
                    op_ima_obj_attr_get (strm_objid, "dest_strm", &RPF_instrm);
                }
             }   
             //printf ("RPF_instrm = %d\n", RPF_instrm);    
             oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr,
  		       "protocol", 	OMSC_PR_STRING, 	"ip",
		       "node objid", 	OMSC_PR_OBJID, 		own_node_objid,
		        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);
             num_intf = op_prg_list_size (ip_info_ptr->ip_iface_table_ptr);
             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);
		     if (ip_interface_ptr-> port_num == RPF_instrm) 
                 *RPF_in_addr_ptr =  ip_address_copy (ip_interface_ptr->addr_range_ptr->address);
         }
	     /* Deallocate the temporary list pointer. */
	     op_prg_mem_free (proc_record_handle_list_ptr);
         FRET (OPC_TRUE);
      }    
   }		  
}


void
ospf_area_list_interfaces_print1 (List *area_list_ptr, Boolean detailed)
	{
	OspfT_Area *		area_ptr;
	int					num_areas, area_index;

	/** Print out the interfaces connected to the areas in the area list. **/
	/** This report is similar to the one described in D.2.2 in RFC 1247. **/
	FIN (ospf_area_list_interfaces_print (area_list_ptr, detailed));

	/* Loop through the areas and print out an interface table for each one. */
	num_areas = op_prg_list_size (area_list_ptr);
	
    //printf (" inside area list  num_area = %d************\n", num_areas);
	for (area_index = 0; area_index < num_areas; area_index++)
		{
         
		/* Get current area. */
		area_ptr = (OspfT_Area *) op_prg_list_access (area_list_ptr, area_index);

         //printf("interface list sie == %d\n", op_prg_list_size (area_ptr->interface_list_ptr));
		/* Print interface report for area. */
		ospf_interface_list_print1 (area_ptr->interface_list_ptr);
		
         
		}
	
	FOUT;
	}


void
ospf_interface_list_print1 (List *interface_list_ptr)
	{
	int					num_interfaces, interface_index;
	OspfT_Interface *	interface_ptr;
	FILE               *fp;

	/** Print out the interface list; useful for debugging. **/
	FIN (ospf_interface_list_print (interface_list_ptr));

	/* Loop through the interfaces and print out information on each one. */

    //printf ("inside interface list ----\n");
	num_interfaces = op_prg_list_size (interface_list_ptr);
    
    
    fp = fopen ("test", "w");
	for (interface_index = 0; interface_index < num_interfaces; interface_index++)
		{
		interface_ptr = (OspfT_Interface *) op_prg_list_access (interface_list_ptr, interface_index);
		ospf_interface_print (interface_ptr);
        fprintf (fp, " interface = %s\n", interface_ptr->interface_address); 
        getchar ();
		}

	FOUT;
	}


List *
ospf_area_list_get_in_pim ()
{
    List **             area_list_ptr;
    List *              proc_record_handle_list_ptr;
    OmsT_Pr_Handle      process_record_handle;

    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, "ospf",
       "node objid", 	OMSC_PR_OBJID, 	own_node_objid, 
	   OPC_NIL);
    
    if (op_prg_list_size (proc_record_handle_list_ptr) == 1) {
       process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, 
							OPC_LISTPOS_HEAD);
       
    	
       /*	Obtain the routing information structure pointer.	*/
       oms_pr_attr_get (process_record_handle, "area list", OMSC_PR_ADDRESS, &area_list_ptr);
     }

     /*	Deallocate the list pointer.							*/
     op_prg_mem_free (proc_record_handle_list_ptr);	
     return *area_list_ptr; 
}    


void
ospf_rte_table_handle_get (IpT_Rte_Table_Lookup_Proc *ospf_rte_select_func_ptr, IpT_Rte_Table_Handle *ospf_dyn_route_table_ptr) 
{
    List *              proc_record_handle_list_ptr;
    int                 record_handle_list_size;  
    OmsT_Pr_Handle      process_record_handle;
    IpT_Rte_Info*	    dyn_rte_info_ptr;
   
    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, "ospf",
				"node objid", 	OMSC_PR_OBJID, 	own_node_objid, 
				OPC_NIL);

	record_handle_list_size = op_prg_list_size (proc_record_handle_list_ptr);

	if (record_handle_list_size != 1)
		{
		/*	An error should be created if there are more than	*/
		/*	one dynamic routing process in the local node.		*/
		op_sim_end ("Error: either zero or several 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 routing information structure pointer.	*/
		oms_pr_attr_get (process_record_handle, "routing information", OMSC_PR_ADDRESS, &dyn_rte_info_ptr);
		}

	/* Deallocate the list pointer.							*/
	op_prg_mem_free (proc_record_handle_list_ptr);	
	
	/*	Obtain the function pointer to the routing table		*/
	/*	lookup function.										*/
	*ospf_rte_select_func_ptr = dyn_rte_info_ptr->lookup_proc;

	/*	Obtain a pointer to the dynamic routing table.			*/
	*ospf_dyn_route_table_ptr = dyn_rte_info_ptr->table_handle; 
	
}

int ip_address_is_pim_reserved (IpT_Address dest_addr)
{
 IpT_Address ALL_PIM_ROUTERS = ip_address_create ("224.0.0.13"); 
 return (ip_address_equal (dest_addr, ALL_PIM_ROUTERS));
}

PimDMT_Router *pim_dm_router_create (Prohandle proc_handle, OmsT_Pr_Handle proc_reg_handle)
	{
	PimDMT_Router *			router_ptr;

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

	/* Allocate memory for DS. */
	router_ptr = op_prg_mem_alloc (sizeof (PimDMT_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 ("PIM_DM 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);
	}

/* Xin Wang*/
/* Munaully create Mlocal now befroe IGMP work*/

/*void 
Mlocal_create ()
{
  FILE *fp;
  char tmp_group_addr[IPC_ADDR_STR_LEN]; 
  Mlocal_Entry * mlocal_entry_ptr;

    //printf ("inside Mlocal create\n");
  if (leaf_router) {
      //printf ("leaf_router \n");
     if ((fp = fopen(mlocal_table, "r")) == NULL)
        //printf ("can't find %s \n", mlocal_table);
     
     while (fgets (tmp_group_addr, 80, fp) != NULL) {
        //printf ("\n\n%s ^^^^^group add = %s ^^^^^^^\n\n\n", mlocal_table,tmp_group_addr);
        mlocal_entry_ptr = (Mlocal_Entry *)op_prg_mem_alloc (sizeof (Mlocal_Entry));
        mlocal_entry_ptr->group = ip_address_copy (ip_address_create (tmp_group_addr));
        op_prg_list_insert (Mlocal_table_ptr, mlocal_entry_ptr, OPC_LISTPOS_TAIL);
     }
  }
}           
  */   

/*void  
Mlocal_create ()
{
   char tmp_group_addr[IPC_ADDR_STR_LEN]; 
   Mlocal_Entry * mlocal_entry_ptr;
 
   FIN (Mlocal_create ());

   if (leaf_router) {       
        if((strcmp (name, "RT1") == 0) || (strcmp (name, "RT2") == 0)) {
            mlocal_entry_ptr = (Mlocal_Entry *) op_prg_mem_alloc (sizeof (Mlocal_Entry));
            mlocal_entry_ptr->group = ip_address_create ("239.255.0.1");
            op_prg_list_insert (Mlocal_table_ptr, mlocal_entry_ptr, OPC_LISTPOS_TAIL);
       }
    }
    FOUT;
}
*/


void
dummy_Mlocal_create ()
{
   char tmp_group_addr[IPC_ADDR_STR_LEN]; 
   Mlocal_Entry * mlocal_entry_ptr;
   int            pim_dim_intf_table_size, intf_index;
   PimDM_Interface  * intf_table_entry_ptr;
 
   FIN (dummy_Mlocal_create ());

   if ((strcmp (name, "RT1") == 0) || (strcmp (name, "RT2") == 0)) {
       pim_dim_intf_table_size = op_prg_list_size (pim_dm_interface_table_ptr);
       //printf (" pim_dim_intf_table_size = %d\n", pim_dim_intf_table_size );
       for (intf_index = 0; intf_index < pim_dim_intf_table_size; intf_index++) {
           intf_table_entry_ptr = (PimDM_Interface *) op_prg_list_access 
             (pim_dm_interface_table_ptr, intf_index);
           //printf ("here call Mlocal create ********\n");
           if (intf_table_entry_ptr->interface_type == OspfC_Interface_Type_Broadcast) {
              mlocal_entry_ptr = (Mlocal_Entry *) op_prg_mem_alloc (sizeof (Mlocal_Entry));
              mlocal_entry_ptr->intf_addr = ip_address_copy (intf_table_entry_ptr->interface_address);
              mlocal_entry_ptr->group_list = op_prg_list_create ();
              op_prg_list_insert (mlocal_entry_ptr->group_list, ip_address_create ("239.255.0.1"), OPC_LISTPOS_TAIL);
              op_prg_list_insert (Mlocal_table_ptr, mlocal_entry_ptr, OPC_LISTPOS_TAIL); 
           }
       }
   }
   FOUT;
}      


PimDMT_Rte_Table *PopulateMROUTE(char *rtable)
{
 FILE *fp;
 MROUTE_Entry *CurEntry, *PrevEntry, *MROUTE;
 char c = NOT_FILE_END;
 char Mode; 
 int FirstEntry = TRUE;
 int ListIndex;
 PimDMT_Rte_Table *Rte_Table;

 

 Rte_Table = (PimDMT_Rte_Table *)malloc(sizeof(PimDMT_Rte_Table));
 if(!Rte_Table) printf("\n\n ***** Rte_Table NULL !!! ******");
 fp = fopen(routing_table, "r");
 if(!fp) { /*printf("\n\n #### error opening file #####");*/
             return NULL; }
 
 PrevEntry = NULL;

 while(c != FILE_END) { 

       CurEntry = (MROUTE_Entry *)malloc(sizeof(MROUTE_Entry));
	   if(FirstEntry)  MROUTE = CurEntry;	         

       /* Get S of <S, G> Pair */
       fscanf(fp, "%d%c%d%c%d%c%d%c", &(CurEntry->SrcGrp.Source[0]),&c,
	                                  &(CurEntry->SrcGrp.Source[1]),&c,
									  &(CurEntry->SrcGrp.Source[2]),&c,
									  &(CurEntry->SrcGrp.Source[3]),&c);

	   /* Get G of <S, G> Pair */
	   fscanf(fp, "%d%c%d%c%d%c%d%c", &(CurEntry->SrcGrp.Group[0]),&c,
	                                  &(CurEntry->SrcGrp.Group[1]),&c,
									  &(CurEntry->SrcGrp.Group[2]),&c,
									  &(CurEntry->SrcGrp.Group[3]),&c);  

	   /* Get UpTime of the <S, G> Entry */
       fscanf(fp, "%d%c%d%c%d%c", &(CurEntry->Uptime.Hour), &c,
                                  &(CurEntry->Uptime.Min), &c,
								  &(CurEntry->Uptime.Sec), &c);

	   /* Get ExpireTime of the <S, G> Entry */
       fscanf(fp, "%d%c%d%c%d%c", &(CurEntry->ExpireTime.Hour), &c,
                                  &(CurEntry->ExpireTime.Min), &c,
								  &(CurEntry->ExpireTime.Sec), &c);

	   /* Get Incoming Interface */
	   fscanf(fp, "%d%c%d%c%d%c%d%c", &(CurEntry->IncInf[0]), &c,
		                              &(CurEntry->IncInf[1]), &c,
									  &(CurEntry->IncInf[2]), &c,
									  &(CurEntry->IncInf[3]), &c);

       /* Get RPF Neighbor */
       fscanf(fp, "%d%c%d%c%d%c%d%c", &(CurEntry->RPFNeighbor[0]),&c,
	                                  &(CurEntry->RPFNeighbor[1]),&c,
									  &(CurEntry->RPFNeighbor[2]),&c,
									  &(CurEntry->RPFNeighbor[3]),&c);

	   /* Fill up the interface list until record end marker is reached */

	   ListIndex = -1;

	   while (c != ENTRY_END && c != FILE_END) {

	     ListIndex++;

         /* Get Interface num and mode */
	     fscanf(fp, "%d%c%d%c%d%c%d%c%c%c", &(CurEntry->IntList[ListIndex].InfNum[0]), &c,
			                                &(CurEntry->IntList[ListIndex].InfNum[1]), &c,
										    &(CurEntry->IntList[ListIndex].InfNum[2]), &c,
										    &(CurEntry->IntList[ListIndex].InfNum[3]), &c,
										    &Mode, &c);

         if(Mode == 'F') CurEntry->IntList[ListIndex].Mode = FORWARD;
		 else  CurEntry->IntList[ListIndex].Mode = PRUNE;

	     /* Get Uptime for the Interface */
         fscanf(fp, "%d%c%d%c%d%c", &(CurEntry->IntList[ListIndex].Uptime.Hour), &c,
                                    &(CurEntry->IntList[ListIndex].Uptime.Min), &c,
		    			  		    &(CurEntry->IntList[ListIndex].Uptime.Sec), &c);

         /* Get ExpireTime for the Interface */
	     fscanf(fp, "%d%c%d%c%d%c", &(CurEntry->IntList[ListIndex].ExpireTime.Hour), &c,
  	                                &(CurEntry->IntList[ListIndex].ExpireTime.Min), &c,
								    &(CurEntry->IntList[ListIndex].ExpireTime.Sec), &c);

       } /* while */

	   CurEntry->IntList[++ListIndex].InfNum[0] = TERMINATE_LIST;
       CurEntry->NextEntry = NULL;

	   /* Set NextEntry of previous entry */
	   if(!FirstEntry)  
	                PrevEntry->NextEntry = CurEntry;
       else { PrevEntry = CurEntry;
	          FirstEntry = FALSE; 		
			}				 

   } /* main loop (eof file) */

   fclose(fp);
   Rte_Table->Mcast_Rte_Table =  MROUTE;
   /* Rte_Table->dirty_flag = */
   /* Rte_Table->last_calc_time = op_sim_time();*/
  
/*   printf("\n\n**** MROUTE in PIM module: %x ", MROUTE);*/

   return Rte_Table;
}

/***** Support for Dynamic Routing *****/
/*IpT_Rte_Info *
ip_dyn_rte_info_create (void)
	{
	IpT_Rte_Info *			info_ptr;

	Allocate and initialize a dynamic routing info structure. 
	FIN (ip_dyn_rte_info_create (void));

	info_ptr = op_prg_mem_alloc (sizeof (IpT_Rte_Info));
	info_ptr->table_handle = OPC_NIL;
	info_ptr->lookup_proc = OPC_NIL;
	info_ptr->fast_addr_proc = OPC_NIL;

	FRET (info_ptr);
	}*/

IpT_Address
find_bigger_address (IpT_Address addr1, IpT_Address addr2)
{
   int i;

   FIN (find_bigger_address (addr1, addr2));

   for (i = 0; i<4; i++) {
       if (addr1 [i] < addr2[i])
          FRET (addr2)
       else if (addr1 [i] > addr2[i])
          FRET (addr1)
   }
}
            
  
