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



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



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

/* added Xin Wang */
#include <stdio.h>
#include <malloc.h>
#include <string.h>

#include "ip3_addr.h"
#include "ip3_rte.h"
#include "ip3_rte_table.h"
#include "ip3_auto_addr_sup.h"
#include "ip_frag_sup2.h"
#include "nato.h"
#include "oms_pr.h"
#include "oms_tan.h"
#include "link_util_support.h"

/* Added by Xin Wang */
#include "pim_dm.h"
#include "debug.h"

/* Macros Used to Represent Transitions. */
#define QUEUE_EMPTY     	(op_q_empty ())
#define SVC_COMPLETION  	op_intrpt_type () == OPC_INTRPT_SELF
#define ARRIVAL         	op_intrpt_type () == OPC_INTRPT_STRM
#define	SELF_NOTIFICATION	op_intrpt_type () == OPC_INTRPT_SELF
#define END_SIM				op_intrpt_type () == OPC_INTRPT_ENDSIM

/* Added Xin Wang for mcast */
#define MCAST_REGISTRATION  op_intrpt_type () == OPC_INTRPT_REMOTE


/* Default value for the time-to-live counter.		*/
/* This value is assigned when a packet is received	*/
/* which does not already have a ttl field.			*/
#define IP_DEFAULT_TTL			32

/* Default value used to determine whether IP Auto-	*/
/* Addressing is used.								*/
#define	IPAA_USED				"Enabled"

Boolean	ip_error_message_printed = OPC_FALSE;

#define	IP_ADDR_INDEX_INVALID			-1
#define IP_SRC_ADDR_UNDEFINED			-1
#define IP_OUTSTRM_INVALID				-1

#define IP_NEIGHBOR_NOTIFICATION		-1

#define	LTRACE_GLOBAL_TABLE_ACTIVE (op_prg_odb_ltrace_active ("global_table"))

/***** Globals *****/
extern IpT_Address		IpI_Broadcast_Addr;
extern IpT_Address		IpI_Default_Addr;

/***** Procedure Declarations *****/
void					ip_interface_table_print (List *iface_table_ptr);
void					ip_rtab_init (void);
void					ip_rtab_local_addr_register (IpT_Address ip_addr);
void					ip_rtab_table_handle_register (void);
int						ip_rtab_num_addrs_registered (void);
void					ip_rtab_print (void);
Compcode				ip_dest_reached (IpT_Address dest_addr, List *iface_table_ptr, 
	                        int ttl_index);
void					ip_src_address_determine (IpT_Address dest_addr, IpT_Address *src_addr_ptr, 
			                List *iface_table_ptr);
int						ip_outstrm_and_mtu_determine (List *iface_table_ptr, 
			                IpT_Address next_hop_addr, int *mtu_size_ptr);
void					ip_rte_error (char *msg);
void					ip_rte_warn (char *msg);
Compcode		 		ip_rte_local_gateway_find (List *iface_table_ptr, IpT_Address *next_hop_addr_ptr);
Compcode				ip_rte_destination_local_network (List *iface_table_ptr, IpT_Address dest_addr);
void					ip_module_connectivity_check (Objid strm_id, Objid my_objid);
IpT_Interface_Info*		ip_interface_info_create (void);
void					ip_rte_next_hop_error (IpT_Address addr);
Compcode				ip_rte_datagram_dest_get (IpT_Address dest_addr, IpT_Address mcast_intf_addr, int ttl, 
							int fast_address, IpT_Address *next_addr_ptr, int *outstrm_ptr, int *mtu_ptr,
							Boolean *broadcast_ptr, Boolean *higher_layer_ptr, Boolean *destroy_pkt_ptr);
void					ip_rte_datagram_broadcast (IpT_Address dest_addr, Packet *pk_ptr);
void					ip_rte_datagram_higher_layer_forward (Packet *pk_ptr, IpT_Address dest_addr);
void					ip_rte_datagram_interface_forward (IpT_Address dest_addr, IpT_Address next_addr, 
	                        int outstrm, int mtu, Packet *pk_ptr);
void					ip_rte_pk_frag_send (Packet *pk_ptr, int outstrm, int mtu);
extern	void			lu_report ();
extern	void			lu_setup ();


/* Added Xin Wang  */
int                     address_is_mcast_reserved(IpT_Address dest_addr);

void                    ip_rte_check_and_get_mcast_interface_addr 
                           ( Packet *arg_pkptr, IpT_Address *ptr_to_mcast_intf_addr,
                        Boolean  *ptr_to_mcast_interface_is_specified);

/* By cjin for Mlocal table access or say IGMP table */
typedef struct grp_tab_node 
{
        IpT_Address grp_ip;
        int waiting_squery;  /* number of query still needed */
        float non_report_rec_timer; 
        float querytimer;
}grp_tab_node;
 
typedef struct IGMP_Interface{
        int inter_face;
		IpT_Address                 interface_address;
        int querier_flag; /* set if first */
        int waiting_gquery;
        float querytimer;
        float non_querier_rec_timer;
		List *grp_tab;
		float other_q_present_interval;
}IGMP_Interface;
#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
	int                     		sv_server_busy;
	double                  		sv_service_rate;
	Objid                   		sv_own_id;
	IpT_Rte_Table	          		sv_static_rte_table;
	Ici *	                  		sv_arp_iciptr;
	ip_dgram_list *	        		sv_dgram_list_ptr;
	int	                    		sv_default_ttl;
	List *	                 		sv_interface_table_ptr;
	IpT_Info *	             		sv_ip_info_ptr;
	Boolean	                		sv_dynamic_routing_enabled;
	IpT_Rte_Protocol	       		sv_routing_protocol_used;
	IpT_Rte_Table_Lookup_Proc			sv_ip_rte_select_func_ptr;
	IpT_Rte_Table_Handle	   		sv_ip_dyn_route_table_ptr;
	List*	                  		sv_link_iface_table_ptr;
	Objid	                  		sv_own_node_objid;
	Prohandle	              		sv_own_prohandle;
	OmsT_Pr_Handle	         		sv_own_process_record_handle;
	char	                   		sv_proc_model_name [20];
	int	                    		sv_instrm_from_ip_encap;
	int	                    		sv_outstrm_to_ip_encap;
	Objid	                  		sv_dyn_rte_objid;
	Boolean	                		sv_gateway_status;
	Boolean	                		sv_gateway_known;
	IpT_Address	            		sv_next_addr_gateway;
	Objid	                  		sv_comp_attr_objid;
	Objid	                  		sv_subnet_objid;
	int	                    		sv_interface_table_size;
	List *	                 		sv_mcast_addr_list_ptr;
	IpT_Address	            		sv_default_gtwy_addr;
	Boolean	                		sv_default_gtwy_available;
	char	                   		sv_ipaa_status [64];
	Boolean                 		sv_mcast_interface_is_specified;
	Boolean	                		sv_mcast_dynamic_routing_enabled;
	IpT_Rte_Protocol	       		sv_mcast_routing_protocol_used;
	IpT_Rte_Table_Lookup_Proc			sv_ip_mcast_rte_select_func_ptr;
	Objid	                  		sv_mcast_routing_entry_objid;
	Objid                   		sv_mcast_internal_rte_table_objid;
	PimDMT_Rte_Table *      		sv_ip_mcast_dyn_route_table_ptr;
	List *                  		sv_Mroute_table_ptr;
	List *                  		sv_Mlocal_table_ptr;
	char                    		sv_router_name[30];
	int                     		sv_num_oif;
	int                     		sv_MCAST_IP_RTE_IND;
	int                     		sv_mcast_data_from_higher_layer;
	MLOCAL_Entry *          		sv_MLOCAL;
	int                     		sv_test;
	int                     		sv_num_ind_create;
	int                     		sv_num_ind_destroy;
	int                     		sv_num_higher_layer_forward;
	int                     		sv_num_pk;
	IpT_Address	            		sv_DEMO_GRP_IP;
	IpT_Address	            		sv_IGMP_PROTOCOL_IP;
	} A1_ip3m_rte_state;

#define pr_state_ptr            		((A1_ip3m_rte_state*) SimI_Mod_State_Ptr)
#define server_busy             		pr_state_ptr->sv_server_busy
#define service_rate            		pr_state_ptr->sv_service_rate
#define own_id                  		pr_state_ptr->sv_own_id
#define static_rte_table        		pr_state_ptr->sv_static_rte_table
#define arp_iciptr              		pr_state_ptr->sv_arp_iciptr
#define dgram_list_ptr          		pr_state_ptr->sv_dgram_list_ptr
#define default_ttl             		pr_state_ptr->sv_default_ttl
#define interface_table_ptr     		pr_state_ptr->sv_interface_table_ptr
#define ip_info_ptr             		pr_state_ptr->sv_ip_info_ptr
#define dynamic_routing_enabled 		pr_state_ptr->sv_dynamic_routing_enabled
#define routing_protocol_used   		pr_state_ptr->sv_routing_protocol_used
#define ip_rte_select_func_ptr  		pr_state_ptr->sv_ip_rte_select_func_ptr
#define ip_dyn_route_table_ptr  		pr_state_ptr->sv_ip_dyn_route_table_ptr
#define link_iface_table_ptr    		pr_state_ptr->sv_link_iface_table_ptr
#define own_node_objid          		pr_state_ptr->sv_own_node_objid
#define own_prohandle           		pr_state_ptr->sv_own_prohandle
#define own_process_record_handle		pr_state_ptr->sv_own_process_record_handle
#define proc_model_name         		pr_state_ptr->sv_proc_model_name
#define instrm_from_ip_encap    		pr_state_ptr->sv_instrm_from_ip_encap
#define outstrm_to_ip_encap     		pr_state_ptr->sv_outstrm_to_ip_encap
#define dyn_rte_objid           		pr_state_ptr->sv_dyn_rte_objid
#define gateway_status          		pr_state_ptr->sv_gateway_status
#define gateway_known           		pr_state_ptr->sv_gateway_known
#define next_addr_gateway       		pr_state_ptr->sv_next_addr_gateway
#define comp_attr_objid         		pr_state_ptr->sv_comp_attr_objid
#define subnet_objid            		pr_state_ptr->sv_subnet_objid
#define interface_table_size    		pr_state_ptr->sv_interface_table_size
#define mcast_addr_list_ptr     		pr_state_ptr->sv_mcast_addr_list_ptr
#define default_gtwy_addr       		pr_state_ptr->sv_default_gtwy_addr
#define default_gtwy_available  		pr_state_ptr->sv_default_gtwy_available
#define ipaa_status             		pr_state_ptr->sv_ipaa_status
#define mcast_interface_is_specified		pr_state_ptr->sv_mcast_interface_is_specified
#define mcast_dynamic_routing_enabled		pr_state_ptr->sv_mcast_dynamic_routing_enabled
#define mcast_routing_protocol_used		pr_state_ptr->sv_mcast_routing_protocol_used
#define ip_mcast_rte_select_func_ptr		pr_state_ptr->sv_ip_mcast_rte_select_func_ptr
#define mcast_routing_entry_objid		pr_state_ptr->sv_mcast_routing_entry_objid
#define mcast_internal_rte_table_objid		pr_state_ptr->sv_mcast_internal_rte_table_objid
#define ip_mcast_dyn_route_table_ptr		pr_state_ptr->sv_ip_mcast_dyn_route_table_ptr
#define Mroute_table_ptr        		pr_state_ptr->sv_Mroute_table_ptr
#define Mlocal_table_ptr        		pr_state_ptr->sv_Mlocal_table_ptr
#define router_name             		pr_state_ptr->sv_router_name
#define num_oif                 		pr_state_ptr->sv_num_oif
#define MCAST_IP_RTE_IND        		pr_state_ptr->sv_MCAST_IP_RTE_IND
#define mcast_data_from_higher_layer		pr_state_ptr->sv_mcast_data_from_higher_layer
#define MLOCAL                  		pr_state_ptr->sv_MLOCAL
#define test                    		pr_state_ptr->sv_test
#define num_ind_create          		pr_state_ptr->sv_num_ind_create
#define num_ind_destroy         		pr_state_ptr->sv_num_ind_destroy
#define num_higher_layer_forward		pr_state_ptr->sv_num_higher_layer_forward
#define num_pk                  		pr_state_ptr->sv_num_pk
#define DEMO_GRP_IP             		pr_state_ptr->sv_DEMO_GRP_IP
#define IGMP_PROTOCOL_IP        		pr_state_ptr->sv_IGMP_PROTOCOL_IP

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




/* Process model interrupt handling procedure */



void
A1_ip3m_rte ()
	{
	double          		pk_svc_time;
	int             		insert_ok;
	int						instrm;
	int						outstrm = IP_OUTSTRM_INVALID;
	int						mtu = -1;
	int						i, frag, frag_accum, num_frags, k, instrm_index;
	int						frag_size, data_size, len;
	int						header_size, ttl;
	char					str0 [512], str1 [512];
	Packet					*pkptr, *ip_pkptr, *frag_ptr, *pkcopy;
	Packet					*ip_igmp_pkptr; // added by cjin
	Packet					*data_pkptr;
	int						num_output_streams, num_input_streams;
	int						instrm_ip_addr_index;
	Objid					strm_objid, ip_encap_objid, instrm_objid;
	IpT_Interface_Info*		iface_info_ptr;
	IpT_Interface_Info*		temp_iface_info_ptr;
	Ici*					rip_iciptr;
	IpT_Rte_Info*			ip_dyn_rte_info_ptr;
	int						ip_fast_addr = -1;
	Boolean					broadcast_mode, entry_included;
	List*					proc_record_handle_list_ptr;
	int						record_handle_list_size;
	OmsT_Pr_Handle			process_record_handle;
	Compcode				route_status;
	Evhandle				evh;
	Boolean					ip_dest_local_network;
	int						rip_rte_table_size;
	Objid					iface_description_objid;
	int						num_interfaces;
	Objid					line_objid;
	Objid					routing_data_objid;
	Objid					routing_data_subobjid;
	char					dyn_route [512];
	int						conn_class;
	int						proto_num;
	char					ip_packet_format [256];
	char					ip_addr_str [IPC_ADDR_STR_LEN];
	char					subnet_mask_str [IPC_ADDR_STR_LEN];
	IpT_Address				ip_addr;
	IpT_Address				subnet_mask_addr;
	IpT_Address				dest_addr;
	IpT_Address				next_addr = OPC_NIL;
	Boolean					broadcast = OPC_FALSE;
	Boolean					higher_layer = OPC_FALSE;
	Boolean					destroy_pkt = OPC_FALSE;
	Boolean					in_port_found;
	char					dest_addr_str [IPC_ADDR_STR_LEN];
	Objid					internal_rte_table_objid, routing_entry_objid;
	Ici*					intf_ici_ptr = OPC_NIL;
	Ici*					mcast_ici_ptr;
	IpT_Address				mcast_intf_addr = OPC_NIL;
	char					ici_format_str [128];
	char					trace_msg [128];
	char					intf_addr_str [IPC_ADDR_STR_LEN];
	int						intf_table_size;
	Boolean					broadcast_addr;
	int						intf_num;
	char					strm_name [128];
	int						iface_table_size;
	char					error_string [512];
	char					default_gtwy_str [IPC_ADDR_STR_LEN];
	IpT_Address				intf_addr;
	
	/* Added by Anindya Neogi */
	
	IpT_Address             source_addr;
	MCAST_Addr              group;
	int                     MCAST;
	int                     DONE;
	int                     MLOCALEntryIndex;
	int                     MROUTEEntryIndex;
	MROUTE_Entry           *MROUTEEntry = NULL;
	MLOCAL_Entry           *MLOCALEntry = NULL;
	int ROUTER = 0;
	
	
	/* Added by Xin Wang  3/3/98                     */
	IpT_Rte_Info*			ip_mcast_dyn_rte_info_ptr;
	Objid					mcast_routing_data_objid;
	Objid					mcast_routing_data_subobjid;
	char					mcast_dyn_route [512];
	IpT_Address             arrived_intf_addr;
	IpT_Address             src_addr;
	
	Ici*		            mcast_intf_ici_ptr = OPC_NIL;
	Ici*		            mcast_copy_intf_ici_ptr = OPC_NIL;
	Ici*                    ici_ptr; 
	char *                  format_name [256];
	char                    err_msg [256];
	int                     mcast_entry_index;
	PimDM_Mroute_Entry     *mroute_entry;
	int                     data_packet;
	OIF_Interface *         oif_intf;
	int                     intf_prune;
	Packet *                tmp_pkptr;
	Packet *                tmp_data;
	Packet	               *mcast_copy_pkptr, *mcast_pkptr;
	SrcGrpPair             *SourceGroup;
	IpT_Address				tmp_dest_addr;
	char *                  proto_str;
	char		            next_addr_str [IPC_ADDR_STR_LEN];
	char		            src_addr_str [IPC_ADDR_STR_LEN];
	char                    tmp_dest_addr_str [IPC_ADDR_STR_LEN];
	int                     num_Mroute_entry;
	PimDM_Mroute_Entry *    Mroute_table_entry;
	int                     Mroute_index;  
	int                     test_oif;
	/* end of change  */
	
	/* By cjin */
	int host_igmp;
	char             tmp_addr_str [IPC_ADDR_STR_LEN];
	
	Objid			igmprouter_objid;
	
	
	int _block_origin = 0;


	FSM_ENTER (A1_ip3m_rte)

	FSM_BLOCK_SWITCH
		{
		/*---------------------------------------------------------*/
		/** state (arrival) enter executives **/
		FSM_STATE_ENTER_FORCED (0, state0_enter_exec, "arrival", "A1_ip3m_rte () [arrival enter execs]")
			{
			
			/*	Acquire the arriving packet. Multiple arriving streams		*/
			/*	are supported.												*/
			instrm = op_intrpt_strm ();
			pkptr = op_pk_get (instrm);
			
			if (pkptr == OPC_NIL)
				ip_rte_error ("Unable to get packet from input stream.");
				
			/*	Obtain the format of the packet to verify that it is an		*/
			/*	IP datagram.												*/
			op_pk_format (pkptr, ip_packet_format);
			if (strcmp (ip_packet_format, "ip3_dgram") != 0)
				{
				/*	Print this error message only once.						*/
				if (ip_error_message_printed == OPC_FALSE)
					{
					sprintf (str0, "For packet ID (%d) Expecting packet format [ip3_dgram], but received format [%s]. Destroying packet.", op_pk_id (pkptr), ip_packet_format);
					ip_rte_warn (str0);
					
					ip_error_message_printed = OPC_TRUE;
					}
			
				op_pk_destroy (pkptr);
				insert_ok = 0;
				}
			else
				{
			    
				/*	If this packet came from the higher layer, check to		*/
				/*	see if multicast was specified.							*/
				if (instrm == instrm_from_ip_encap)
					{
					mcast_ici_ptr = op_intrpt_ici ();
					
					if (mcast_ici_ptr != OPC_NIL)
						{
						/*	The higher layer has specified a multicast, so	*/
						/*	get the interface we want to send the multicast	*/
						/*	packet to.                  			 		*/
						/*	The higher layer has specified a multicast.		*/
						/*	Save the ICI in the packet temporarily until	*/
						/*	the packet can be serviced by IP.				*/
						op_pk_ici_set (pkptr, mcast_ici_ptr);
						}
					}
				else
					{
					/*	If the packet didn't come from the higher layer,	*/
					/*	then check to see which interface it arrived on, so	*/
					/*	we can potentially forward this information to the	*/
					/*	higher layer (if the packet is eventually bound for	*/
					/*	the higher layer).									*/
					iface_table_size = op_prg_list_size (interface_table_ptr);
					for (i = 0; i < iface_table_size; i++)
						{
						/*	Get current interface.							*/
						iface_info_ptr = (IpT_Interface_Info *) op_prg_list_access (interface_table_ptr, i);
			
						/*	Does the input port match the port the packet	*/
						/*	was received on.								*/
						if (iface_info_ptr->in_port_num == instrm)
							{
							/* Create an ICI and associate it with the datagram.  The ICI will carry */
							/* the address of the received interface.                                */
							/* Comment out by Xin Wang intf_ici_ptr = op_ici_create ("ip3_rte_ind"); */
			                /* and added the following ici                                           */
			                intf_ici_ptr = op_ici_create ("ip3m_rte_ind");
							op_ici_attr_set (intf_ici_ptr, "interface_received", 
								ip_address_copy (iface_info_ptr->addr_range_ptr->address));
							op_pk_ici_set (pkptr, intf_ici_ptr);
			                num_ind_create ++;
							/*	Issue trace statement.						*/
							if ((op_prg_odb_ltrace_active ("ip_rte") &&
								op_prg_odb_pktrace_active (pkptr)))
								{
								ip_address_print (intf_addr_str, iface_info_ptr->addr_range_ptr->address);
								sprintf (trace_msg, "Packet ID (%d) arrived on interface %s",
									op_pk_id (pkptr), intf_addr_str);
								op_prg_odb_print_minor (trace_msg, OPC_NIL);
								}
			
							break;
							}
						}
					}
			
				/*	If the received packet does not yet have a time-to-live	*/
				/*	field assignment, place the default value in this field	*/
				/*	In this model, TTL is treated as a hop allowance		*/
				/*	decremented by one prior to each transmission. If the	*/
				/*	decremented counter reaches zero, the packet is not		*/
				/*	transmitted.											*/
				if (op_pk_nfd_is_set (pkptr, "ttl") == OPC_FALSE)
					{
					if (op_pk_nfd_set (pkptr, "ttl", IP_DEFAULT_TTL) == OPC_COMPCODE_FAILURE)
						ip_rte_error ("Unable to set TTL in packet.");
					}
			
				/*	Attempt to enqueue the packet at tail of subqueue 0.	*/
				if (op_subq_pk_insert (0, pkptr, OPC_QPOS_TAIL) != OPC_QINS_OK)
					{
					/*	The insertion failed (due to a full queue).		*/
					/*	Deallocate the packet.							*/
					op_pk_destroy (pkptr);
					if (intf_ici_ptr != OPC_NIL)
						{
						/* Obtain the interface address from this ICI.	*/
			            
						op_ici_attr_get (intf_ici_ptr, "interface_received", &intf_addr);
						ip_address_destroy (intf_addr);
						op_ici_destroy (intf_ici_ptr);
						}
			
					/*	Set flag indicating insertion fail. The flag is	*/
					/*	used to determine transition out of this state.	*/
					insert_ok = 0;
					}
				else
					{
					/*	Insertion was successful.						*/
					insert_ok = 1;
					}
				}
			}


		/** state (arrival) exit executives **/
		FSM_STATE_EXIT_FORCED (0, state0_exit_exec, "arrival", "A1_ip3m_rte () [arrival exit execs]")
			{
			
			
			}


		/** state (arrival) transition processing **/
		FSM_INIT_COND (!server_busy && insert_ok)
		FSM_DFLT_COND
		FSM_TEST_LOGIC ("arrival")

		FSM_TRANSIT_SWITCH
			{
			FSM_CASE_TRANSIT (0, 2, state2_enter_exec, ;)
			FSM_CASE_TRANSIT (1, 1, state1_enter_exec, ;)
			}
		/*---------------------------------------------------------*/



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


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


		/** state (idle) exit executives **/
		FSM_STATE_EXIT_UNFORCED (1, state1_exit_exec, "idle", "A1_ip3m_rte () [idle exit execs]")
			{
			/* Run final report for link utitization if specified.	*/
			/* The called function checks whether reporting is		*/
			/* specified.											*/
			if (END_SIM)
				lu_report ();
			}


		/** state (idle) transition processing **/
		FSM_INIT_COND (ARRIVAL)
		FSM_TEST_COND (SVC_COMPLETION)
		FSM_TEST_COND (END_SIM)
		FSM_TEST_COND (MCAST_REGISTRATION)
		FSM_TEST_LOGIC ("idle")

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



		/** state (svc_start) enter executives **/
		FSM_STATE_ENTER_FORCED (2, state2_enter_exec, "svc_start", "A1_ip3m_rte () [svc_start enter execs]")
			{
			/*	Determine the time required to complete service of the		*/
			/*	enqueued packet.											*/
			pk_svc_time = 1.0 / service_rate;
			
			/*	Schedule an interrupt for this process at the time where	*/
			/*	service ends.												*/
			evh = op_intrpt_schedule_self (op_sim_time () + pk_svc_time, 0);
			if (op_ev_valid (evh) == OPC_FALSE)
				{
				ip_rte_warn ("Unable to schedule service completion.");
				}
			else
				{
				/*	Mark the server to be busy now.							*/
				server_busy = 1;
			    }
			}


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


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



		/** state (svc_compl) enter executives **/
		FSM_STATE_ENTER_FORCED (3, state3_enter_exec, "svc_compl", "A1_ip3m_rte () [svc_compl enter execs]")
			{
			
			/*	Extract packet at head of queue - this is the packet just	*/
			/*	finishing service.											*/
			pkptr = op_subq_pk_remove (0, OPC_QPOS_HEAD);
			if (pkptr == OPC_NIL)
				ip_rte_error ("Unable to get packet from head of queue.");
			
			
			/* added by Anindya Neogi */
			/* IP forwarding is modified only for IP on Routers */
			if (gateway_status) ROUTER = TRUE;
			else ROUTER = FALSE; 
			
			/* end if change */
				
			/*	An IP packet has completed service. Obtain the destination	*/
			/*	IP address information.										*/
			if (op_pk_nfd_get (pkptr, "dest_addr", &dest_addr) == OPC_COMPCODE_FAILURE)
				ip_rte_error ("Unable to get address from serviced packet.");
			
			
			/*DEBUG 
			ip_address_print (dest_addr_str, dest_addr); 
			printf("%s enter coml dest = %s\n", router_name, dest_addr_str);*/
			
			/*added by Anindya Neogi */
			
			if(ip_address_is_multicast(dest_addr) && !address_is_mcast_reserved(dest_addr))
			     MCAST = TRUE;
			else MCAST = FALSE;
			
			/* Tried by cjin */
			/* A host would pick up a mcast data packet */
			mcast_data_from_higher_layer = 1;
			if (  MCAST && ip_address_equal ( dest_addr, DEMO_GRP_IP ) && ( !ROUTER ) ) {
			
				if ((ici_ptr = op_pk_ici_get (pkptr)) != OPC_NIL) { 		
			       op_ici_format (ici_ptr, format_name);  
			       if ( ( strcmp (format_name, "ip3m_rte_ind") == 0 ) ) {
						mcast_data_from_higher_layer = 0;
					}
				}
			
				if ( mcast_data_from_higher_layer == 0 ) {		
			
					ip_igmp_pkptr = op_pk_copy(pkptr);
					op_pk_nfd_set (ip_igmp_pkptr, "dest_addr", dest_addr, op_prg_mem_copy_create,
						op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
			      
			           op_pk_nfd_set (ip_igmp_pkptr, "protocol", "mcast_gen");
			
						intf_ici_ptr = op_ici_create ("ip3m_rte_ind");
						op_ici_attr_set (intf_ici_ptr, "interface_received",
							ip_address_copy (IpI_Default_Addr));
						op_ici_attr_set (intf_ici_ptr, "Mroute_hit", 1);
						op_pk_ici_set (ip_igmp_pkptr, intf_ici_ptr);
			
						if (!op_pk_nfd_is_set (ip_igmp_pkptr, "src_addr"))
							op_pk_nfd_set (ip_igmp_pkptr, "src_addr", ip_address_copy (dest_addr), op_prg_mem_copy_create,
								op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
			
					
					op_pk_send (ip_igmp_pkptr, outstrm_to_ip_encap);
			
					op_pk_destroy( pkptr );
				}
			}
			/* End of try by cjin I add if following */
			if ( mcast_data_from_higher_layer == 1 ) {
			
			host_igmp = 0;
			/* Tried by cjin */
			/* I try to catch igmp message received by host ip and send it up to higher layer */
			if ( ( address_is_mcast_reserved(dest_addr) ) && ( !ROUTER ) &&
					( ip_address_equal( dest_addr, IGMP_PROTOCOL_IP ) ) ) {
				mcast_data_from_higher_layer = 0;
			
				if ((ici_ptr = op_pk_ici_get (pkptr)) != OPC_NIL) { 		
			       op_ici_format (ici_ptr, format_name);  
			       if ( (strcmp (format_name, "ip3m_rte_req") == 0) || (strcmp (format_name, "ip3_rte_req") == 0) )
						mcast_data_from_higher_layer = 1;
						host_igmp = 1;
				}
			
				if ( mcast_data_from_higher_layer != 1 ) {
			
					/* I copy the packet instead of using it to send up to higher layer */
					ip_igmp_pkptr = op_pk_copy(pkptr);
					op_pk_nfd_set (ip_igmp_pkptr, "dest_addr", dest_addr, op_prg_mem_copy_create,
						op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
			      
			           op_pk_nfd_set (ip_igmp_pkptr, "protocol", "igmprouter");
					/* Check the 'interface received' ICI.  If the ICI isn't set in the packet,  */
					/* set it with IP default address (0.0.0.0) as the interface received.  This */
					/* corresponds to the situation where the higher layer sends a packet to     */
					/* itself (except in the case of broadcast).                                 */
			
						/* By cjin I use ici format ip3m_rte_ind instead of ip3_rte_ind to have Mroute_hit*/
						intf_ici_ptr = op_ici_create ("ip3m_rte_ind");
						op_ici_attr_set (intf_ici_ptr, "interface_received",
							ip_address_copy (IpI_Default_Addr));
						op_ici_attr_set (intf_ici_ptr, "Mroute_hit", 1);
						op_pk_ici_set (ip_igmp_pkptr, intf_ici_ptr);
			
						/* The source address is unlikely to be set, simply set to the destination address. */
						if (!op_pk_nfd_is_set (ip_igmp_pkptr, "src_addr"))
							op_pk_nfd_set (ip_igmp_pkptr, "src_addr", ip_address_copy (dest_addr), op_prg_mem_copy_create,
								op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
			
					
					/* Forward the complete packet to the higher layer. */  
					op_pk_send (ip_igmp_pkptr, outstrm_to_ip_encap);
				}
			}
			
			//		( mcast_data_from_higher_layer == 1 ) ) { 
			/* Added by Xin Wang                                                                          */
			/* A copy of a multicast packet is always sent to PIM-DM for                                  */
			/* 1. Triggering mcast tree building is no (S, G) entry exist in Mroute                       */
			/* 2. ASSERT  if a data packet is receiving from an oif interface                             */
			/* mcast_data_from_higher layer is set to true if PIM-DM create (S, G) entry and forward the  */
			/* original packet back. IP does not need to forward the packet again to PIM                  */
			MCAST_IP_RTE_IND = 0;
			mcast_data_from_higher_layer = 0; 
			intf_prune = FALSE;
			if (MCAST) {
			
			   if ((ici_ptr = op_pk_ici_get (pkptr)) != OPC_NIL) { 
					
			       op_ici_format (ici_ptr, format_name);
			   
			       if (strcmp (format_name, "ip3m_rte_ind") == 0) {
			          MCAST_IP_RTE_IND = 1;
					
			          mcast_pkptr = op_pk_copy (pkptr);
				
			          mcast_intf_ici_ptr = op_ici_create ("ip3m_rte_ind");
			          num_ind_create ++;
			          /* Retrieve the address of the interface. */
			          if (op_ici_attr_get (ici_ptr, "interface_received", &arrived_intf_addr) == 
				         OPC_COMPCODE_FAILURE)
				         ip_encap_error ("Unable to retrieve interface received information from network layer indication");
			          //printf ("intf1\n"); 
			          op_ici_attr_set (ici_ptr, "interface_received", 
								ip_address_copy (arrived_intf_addr));
			          op_ici_attr_set (mcast_intf_ici_ptr, "interface_received", 
				 				ip_address_copy (arrived_intf_addr));
			          ip_address_destroy (arrived_intf_addr);  
			          op_pk_ici_set (pkptr, ici_ptr);
			       }
			       else if ((strcmp (format_name, "ip3m_rte_req") == 0) && ROUTER) {      
			          op_ici_attr_get (ici_ptr, "data_packet", &data_packet);
			          if (data_packet)
			             mcast_data_from_higher_layer = 1;
			          op_ici_destroy (ici_ptr);
			          op_pk_ici_set (pkptr, OPC_NIL);
			       } 
			       
			   }
			   
			   ip_address_print (dest_addr_str, dest_addr);  
			   if (!address_is_mcast_reserved (dest_addr)) {        
			       sprintf (err_msg, " BEGIN: ----%s data dest_addr = %s , gateway_status = %d MCAST = %d data = %d num_ind_create = %d\n", 
			           router_name, dest_addr_str, gateway_status, MCAST, data_packet, num_ind_create);
			       printf (err_msg);
			   }
			} /* end Xin Wang*/
			
			/* BEGIN_JOE_SUN */
			/* Check one more condition for MCAST */
			/* MCAST really means MCAST table lookup should be performed */
			/* So, if mcast interface is specified, MCAST should set to false */
			/* Also, the packet ici is set to OPCI_NIL */
			/* The following condition  " if (!mcast_data_from_higher_layer) { "is added by Xin Wang */
			if (!mcast_data_from_higher_layer) {
			   ip_rte_check_and_get_mcast_interface_addr(pkptr,&mcast_intf_addr, &mcast_interface_is_specified);
			   /*ip_address_print (tmp_dest_addr_str, mcast_intf_addr);
			   //printf ("forward addr2 = %s\n", tmp_dest_addr_str);*/
			   if (mcast_interface_is_specified) {
				  /*printf("mcast_interface is specified as %d.%d.%d.%d\n", mcast_intf_addr[0], mcast_intf_addr[1],mcast_intf_addr[2],mcast_intf_addr[3]);*/ /*debug */
				  op_pk_ici_set(pkptr, OPC_NIL);
				  MCAST = FALSE ;
			   } 
			}
			/* END_JOE_SUN */
			
			
			
			/*	Obtain the TTL value to determine whether the packet was	*/
			/*	sent by this node.											*/
			op_pk_nfd_get (pkptr, "ttl", &ttl);
			/*	Obtain the fast address from the packet if it is set. This	*/
			/*	should only be done if no broadcast addresses are involved,	*/
			/*	because fast addresses are assigned only to addresses whose	*/
			/*	IP addresses are not broadcast addresses.					*/
			
			/*  We need to check against every connected interface to see   */
			/*  the destination is a broadcast address (assuming it's not   */
			/*  IpI_Broadcast_Addr).                                        */
			broadcast_addr = OPC_FALSE;
			if (ip_address_equal (dest_addr, IpI_Broadcast_Addr))
				broadcast_addr = OPC_TRUE;
			else
				{
			    if (!MCAST && !mcast_interface_is_specified) 
			       {   /* check inserted by Anindya Neogi and JOE_SUN */ 
			       if(DEBUG) printf("\n\n %s: packet is not mcast: range check with mask", router_name);
				
				   intf_table_size = op_prg_list_size (interface_table_ptr);
				   for (i = 0; i < intf_table_size; i++) 
			          {
					  iface_info_ptr = op_prg_list_access (interface_table_ptr, i);
					  if ((ip_address_range_check (dest_addr, iface_info_ptr->addr_range_ptr)) &&
						  (ip_address_is_broadcast (dest_addr, iface_info_ptr->addr_range_ptr->subnet_mask)))
						  {
						  broadcast_addr = OPC_TRUE;
						  break;
						  }
				      }
			       }
			   }
			if (!ip_address_is_multicast (dest_addr) && !broadcast_addr)
				{
				if (op_pk_nfd_is_set (pkptr, "dest_internal_addr"))
					op_pk_nfd_get (pkptr, "dest_internal_addr", &ip_fast_addr);
				else
					{
					/*	Obtain the fast address from the global table.		*/
					ip_fast_addr = ip_rtab_addr_convert (dest_addr);
				
					/*	Set the corresponding field in the ip datagram.		*/
					op_pk_nfd_set (pkptr, "dest_internal_addr", ip_fast_addr);
					}
			     }
			
			/* BEGIN_JOE_SUN */
			/* This entire block is commented out */
			/* because it is performed earlier in */
			/* ip_rte_check_and_set_mcast_interface_addr() */
			/* Also note that the packet ici has been destroyed if the mcast_interface_addr is set */
			
			/* the second condition in the else-if part is inserted by Anindya Neogi */
			/* mil3 code does multicast only for control traffic on a reserved address */
			/* so take the interface from the higher layer iff address is reserved */
			/*
			else if(ip_address_is_multicast (dest_addr) && address_is_mcast_reserved(dest_addr))
				{*/
			
				/*	The interface to send the multicast is specified in		*/
				/*	the ICI associated with the packet.						*/
			/*	mcast_ici_ptr = op_pk_ici_get (pkptr);
				op_ici_format (mcast_ici_ptr, ici_format_str);
			
				if (strcmp (ici_format_str, "ip3_rte_req") == 0)
					{
					op_ici_attr_get (mcast_ici_ptr, "multicast_interface_address", &mcast_intf_addr);
					op_ici_destroy (mcast_ici_ptr);
					op_pk_ici_set (pkptr, OPC_NIL);
					}
				} */
			/* END_JOE_SUN */
			
			/*	Determine the interface the datagram should be forwarded to.	*/
			/*	Also determine whether the datagram should be broadcast, and/or	*/
			/*	the datagram should be forwarded to the higher layer.			*/
			
			/*  Added Xin Wang                                              */
			/*	Determine the next address for the packet based on the type	*/
			/*	of destination addresses. Three valid cases are considered:	*/
			/*
				1) Neither net nor node addresses are broadcast addresses. In this case the dynamic routing API
				is used to determine the next net and node numbers based on the dynamic routing routing table, if a
				dynamic routing protocol is used for routing. In the case of static routing tables, the next and node
				addresses are obtained from the static IP routing tables.
			
				2) Net number is broadcast, meaning that the packet should be broadcast on all of the local ip
				module's interfaces.  The next net number is set to broadcast as well, even though packets can
				only be broadcast to directly connected neighbors.  The next node number is set to broadcast as
			    well.
			
				3) The destination network number is an actual network number and the node number is a broadcast
				address.  As broadcasts can only occur to the neighboring nodes, the next net number is set to
				the destination net number, which should be a directly connected neighbor, and the next node
				number is set to the broadcast address.
			
			Note that packets can only be broadcast to the directly connected neighbors, and broadcast packets
			received from other neighbors are not to be forwarded to other nodes.  The case in which the destination
			net number is a broadcast address and the node number is an actual node number is not valid. */ 
			
			/* CHANGE MADE BY: Anindya Neogi */
			/* DATE : 07-18-97 */
			
			
			  if(MCAST && ROUTER) { 
			  /*  At this point access the 'pim' and 'igmp' modules to get pointers to the   */
			  /*  MROUTE and MLOCAL tables.                     						     */
			  /*  process registry or op_ima_obj_svar_get() may be used to get the pointer   */  
			  /*  7-24-97: currently process registry is implemented and the init phase gets */
			  /*  the pointers to MROUTE and MLOCAL                                          */ 
			
			  
			  
			   /* currently MLOCAL in IGMP is not being accessed  */
			   MLOCAL = NULL;
			
			   if(MLOCAL)
			     MLOCALEntry = SearchMLOCAL(MLOCAL, group);
			
			   /* Added by Xin Wang. Search Mrouter to find match entry    */
			   tmp_pkptr = op_pk_copy (pkptr);
			
			   if (op_pk_nfd_is_set (tmp_pkptr, "src_addr")) {
			       if (op_pk_nfd_get(tmp_pkptr, "src_addr", &src_addr) == OPC_COMPCODE_FAILURE)
			           ip_rte_error ("unable to get source address from serviced packet");
			       ip_address_print (src_addr_str, src_addr);
			       //printf ("%s src_addr = %s \n", router_name, src_addr_str);  
			   }           
			   /*Added by Xin Wang */
			
			   
			   SourceGroup = (SrcGrpPair *) op_prg_mem_alloc (sizeof (SrcGrpPair));
			   SourceGroup->source = ip_address_copy (src_addr);
			   SourceGroup->group = ip_address_copy (dest_addr);
			 
			   op_pk_nfd_get (tmp_pkptr, "data", &tmp_data);
			   op_pk_destroy (tmp_data); 
			   op_pk_destroy (tmp_pkptr);
			   ip_address_destroy (src_addr);
			   
			   if (Mroute_table_ptr != OPC_NIL) 
			      if ((mroute_entry = Mroute_look_up (Mroute_table_ptr, SourceGroup, &mcast_entry_index)) != OPC_NIL)   
			         num_oif = op_prg_list_size (mroute_entry->oif); 
			   /* Set flag before sending the mcast data copy to PIM */
			   if (MCAST_IP_RTE_IND) {
			      if (mroute_entry != OPC_NIL)   
			         op_ici_attr_set (mcast_intf_ici_ptr, "Mroute_hit", 1);
			      else {
			         op_ici_attr_set (mcast_intf_ici_ptr, "Mroute_hit", 0);
			         intf_prune = TRUE;
			      }
			      op_pk_ici_set (mcast_pkptr, mcast_intf_ici_ptr);
			      ip_rte_datagram_higher_layer_forward (mcast_pkptr, ip_address_copy (dest_addr));
			   } /* end Xin Wang */
			  
			   }
			
			   
			  DONE = FALSE;
			  MLOCALEntryIndex = 0; /* to go thru the list of interfaces returned */
			  MROUTEEntryIndex = 0;
			  while (!DONE) {   /* main loop for multiple interface forwarding */
			    /* go thru all the local interfaces specified by the MLOCAL table and then */
			    /* thru the MROUTE table */
				//printf("In %s\n", router_name);
			    if (MCAST && ROUTER) {  
			         /*specify the mcast_intf_addr */
			       if (MLOCALEntry) {
			          /* send through LOCAL interface  */
			          address_copy(mcast_intf_addr, MLOCALEntry->IntList[MLOCALEntryIndex++]);  
			          if (MLOCALEntry->IntList[MLOCALEntryIndex][0] == -1) 
			             MLOCALEntry = NULL;
			       }  
			       else if (mroute_entry) {  /* Added by Xin Wang */       
			          intf_prune = FALSE;
			          oif_intf = (OIF_Interface *) op_prg_list_access (mroute_entry->oif, MROUTEEntryIndex++);
			          if (oif_intf -> forward_status == Prune)
			             intf_prune = TRUE; 
			
					/* Tried by cjin to forward mcast data packet according to Mlocal table */
					if ( Mlocal_look_up( oif_intf->interface_addr, dest_addr ) ) {
						ip_address_print(tmp_addr_str, oif_intf->interface_addr );
						ip_address_print(tmp_dest_addr_str, dest_addr );
						printf("%s has a interface = %s, which has a group = %s join, so should forward\n", 
							router_name, tmp_addr_str, tmp_dest_addr_str );
						intf_prune = FALSE;
					}
			
			          /* If the oif is not pruned, need to forward out to corresponding interface.    */
			          /* Make a copy of the packet before forwarding                                  */
			          /* Dummy ici is created for later function to destroy                           */
			          if (!intf_prune) {
			              mcast_copy_pkptr = op_pk_copy (pkptr); 
			              op_pk_ici_set (pkptr, OPC_NIL);  
			              mcast_intf_addr = ip_address_copy (oif_intf->interface_addr);
			          }
			          if (MROUTEEntryIndex >= num_oif) 
			              DONE = TRUE;           
			       } 
			       if ((mroute_entry == NULL) || (MROUTEEntryIndex >= num_oif)) {
			          if (op_pk_nfd_is_set (pkptr, "src_addr")) {
			              if (op_pk_nfd_get(pkptr, "src_addr", &src_addr) == OPC_COMPCODE_FAILURE)
			                     ip_rte_error ("unable to get source address from serviced packet");
			              ip_address_destroy (src_addr);
			           }  
			           if ((ici_ptr = op_pk_ici_get (pkptr)) != OPC_NIL) { 
			              if (op_ici_attr_exists (ici_ptr, "interface_received")) 
			                 if (op_ici_attr_get (ici_ptr, "interface_received", &arrived_intf_addr) != 
			                    OPC_COMPCODE_FAILURE)
			                    ip_address_destroy (arrived_intf_addr);
			              op_ici_destroy (ici_ptr);
			              num_ind_destroy ++;
			                   
			           }           
			           if (op_pk_nfd_is_set (pkptr, "src_addr")) {
			              if (op_pk_nfd_get (pkptr, "src_addr", &src_addr) == OPC_COMPCODE_FAILURE)
				              ip_encap_error ("Unable to get source address from IP datagram.");
			              ip_address_destroy (src_addr);
			           }
			           if (op_pk_nfd_is_set (pkptr, "dest_addr")) {
			              if (op_pk_nfd_get (pkptr, "dest_addr", &tmp_dest_addr) == OPC_COMPCODE_FAILURE)
				              ip_encap_error ("Unable to get destination address from IP datagram.");
				          ip_address_destroy (tmp_dest_addr);
			           } 
			           op_pk_nfd_get (pkptr, "protocol", &proto_str);
			           op_prg_mem_free (proto_str);
			           op_pk_nfd_get (pkptr, "data", &tmp_data);
			           op_pk_destroy (tmp_data);
			           op_pk_destroy (pkptr); 
			           if (mroute_entry == NULL)
			               break; 
			       }
			       /*else break;   drop packet */
			    } /*if MCAST & ROUTER*/
				
			    else if(MCAST && !ROUTER) { 
			       if (DEBUG)
			           printf("\n\n%s: setting fixed mcast interface address(in host)\n\n", router_name);
			       mcast_intf_addr = ip_address_create ("128.2.1.7");
			       DONE = TRUE;
			    }
			
			    else DONE = TRUE;
			
			    if (MCAST && DEBUG)
			      printf("\n\n%s: Forward thru mcast_intf_addr : %d.%d.%d.%d", router_name, mcast_intf_addr[0], 
			                                        mcast_intf_addr[1], mcast_intf_addr[2], mcast_intf_addr[3]);
			
			
			   
			     /* end of changes made */
			        
			/*	First try to get the interface the datagram should be		*/
			/*	forwarded to.												*/
			
			   if (MCAST && DEBUG)
			     printf("\n\n %s: CALLING THE dest_get FUNCTION", router_name);
			
			   /* BEGIN_JOE_SUN */
			   /* If by now, the mcast_intf_addr is not set */
			   /* either by the upper layer or the MCAST table lookup */
			   /* then set mcast_intf_addr to OPC_NIL */
			   /* so that the JOE_SUN modified ip_rte_datagram_dest_get() */
			   /* will look it up */
			   if ( ! (mcast_interface_is_specified || MCAST) )     
				  mcast_intf_addr = OPC_NIL;
			   /* END_JOE_SUN */
			
				/* Condition if (&& !host_igmp) is added by cjin */
			   /* Condition "if (!intf_prune) {" is added by Xin Wang. Not need to do anything following if a interface is set to prune */
			   if ( (!intf_prune || !MCAST || !ROUTER) ) {
			      if (ip_rte_datagram_dest_get (dest_addr, mcast_intf_addr, ttl, ip_fast_addr, &next_addr, &outstrm, &mtu,
				   &broadcast, &higher_layer, &destroy_pkt) == OPC_COMPCODE_FAILURE) {
				     /*	Issue a trace message and destroy the packet.				*/
				     if (op_prg_odb_ltrace_active ("ip_rte"))
					    {
					    ip_address_print (dest_addr_str, dest_addr);
					    sprintf (str0, "Routing error: Unable to route packet destined for (%s)", dest_addr_str);
					    op_prg_odb_print_major (str0, OPC_NIL);
					    }
			
				     /*	Obtain the interface ICI from the packet. The ICI contains	*/
				     /*	information on the interface this IP datagram arrived.		*/
				     intf_ici_ptr = op_pk_ici_get (pkptr);
			
				     /*	Check if the ICI associated with the packet is valid.		*/
				     if (intf_ici_ptr != OPC_NIL)
					    {
					    /* Deallocate the memory associated with this ICI as it is	*/
					    /* no longer required.										*/
			            if (op_ici_attr_exists (intf_ici_ptr, "interface_received")) {
			    		   op_ici_attr_get (intf_ici_ptr, "interface_received", &intf_addr);
					       ip_address_destroy (intf_addr);
			            }
					    op_ici_destroy (intf_ici_ptr);
					    }
			
				     /*	Destroy the packet since cannot be routed.					*/
			         
				     op_pk_destroy (pkptr);
				  }
			      else {
			         /*	If the datagram has reached its destination, forward it to	*/
			         /*	the higher layer.											*/
				    if (!broadcast && higher_layer && !destroy_pkt)
					   ip_rte_datagram_higher_layer_forward (pkptr, dest_addr);
				
			        /*	If the datagram should be broadcast, send it out on all		*/
			        /*	applicable interfaces.										*/
				    if  (broadcast && !higher_layer && !destroy_pkt)
					    ip_rte_datagram_broadcast (dest_addr, pkptr);
			
			        /*	Otherwise, forward the datagram on the appropriate			*/
			        /*	interface.													*/
				    if (!broadcast && !higher_layer && !destroy_pkt) {
			           /* Following condition check is addred by Xin Wang for mcast  IP forwarding */ 
			           if (MCAST && ROUTER && !intf_prune)     
			              { ip_rte_datagram_interface_forward (dest_addr, next_addr, outstrm, mtu, mcast_copy_pkptr);
						  }
			           else 
					      ip_rte_datagram_interface_forward (dest_addr, next_addr, outstrm, mtu, pkptr);
			
			         }
			         /*	The packet should be destroyed, because it's a multicast	*/
			         /*	that this IP host is not configured to handle.				*/
			       
				    if (destroy_pkt)           
					   op_pk_destroy (pkptr);
			        
			      }
			   }
			} /* main loop while (!DONE)to forward through multiple interfaces */
			/*	De-allocate memory allocated for storing the next hop address.	*/
			if (next_addr != OPC_NIL)
				ip_address_destroy (next_addr);
			
			/*	The IP routing server becomes available again.					*/
			server_busy = 0;
			
			/*printf (" -- %s, num_ind_Create = %d , num_ind_destroy = %d \n", router_name, num_ind_create, num_ind_destroy);*/
			//printf ("%s exit IP\n", router_name);
			//}
				// This is to enbrace all process except igmp message sening up to higher layer in "if". 
				//	Added by cjin 
			} // Added by cjin
			
			server_busy = 0;
			}


		/** state (svc_compl) exit executives **/
		FSM_STATE_EXIT_FORCED (3, state3_exit_exec, "svc_compl", "A1_ip3m_rte () [svc_compl exit execs]")
			{
			
			}


		/** state (svc_compl) transition processing **/
		FSM_INIT_COND (!QUEUE_EMPTY)
		FSM_DFLT_COND
		FSM_TEST_LOGIC ("svc_compl")

		FSM_TRANSIT_SWITCH
			{
			FSM_CASE_TRANSIT (0, 2, state2_enter_exec, ;)
			FSM_CASE_TRANSIT (1, 1, state1_enter_exec, ;)
			}
		/*---------------------------------------------------------*/



		/** state (init) enter executives **/
		FSM_STATE_ENTER_UNFORCED (4, state4_enter_exec, "init", "A1_ip3m_rte () [init enter execs]")
			{
			/* Link Utilization setup	*/
			lu_setup ();
			
			/*	Initialize the IP address package.							*/
			ip_address_pkg_init ();
			
			/*	Initialize the global address table.						*/
			ip_rtab_init ();
			                
			/*	Create the interface table.									*/
			interface_table_ptr = op_prg_list_create ();
			
			/*	The interface table pointer is made available to other		*/
			/*	processes by registering it in the model-wide registry.		*/
			/*	Allocate memory for the data structure used to store this	*/
			/*	information and set the interface table information.		*/
			ip_info_ptr = (IpT_Info *) op_prg_mem_alloc (sizeof (IpT_Info));
			ip_info_ptr->ip_iface_table_ptr = interface_table_ptr;
			
			/*	Initially the server is idle.								*/
			server_busy = 0;
			
			/*	Initially the local gateway is not known.					*/
			gateway_known = OPC_FALSE;
			
			/*	Get queue module's own object identifier.					*/
			own_id = op_id_self ();
			  						
			/*	Get assigned value of server processing rate.				*/
			if (op_ima_obj_attr_get (own_id, "service_rate", &service_rate) == OPC_COMPCODE_FAILURE)
				ip_rte_error ("Unable to get server processing rate from attribute.");
			
			/*	Determine whether this IP node is a gateway.				*/
			if (op_ima_obj_attr_get (own_id, "gateway", &gateway_status) == OPC_COMPCODE_FAILURE)
				ip_rte_error ("Unable to get gateway status from attribute.");
			
			/*	Obtain the node and subnet's objid.							*/
			own_node_objid = op_topo_parent (own_id);
			subnet_objid = op_topo_parent (own_node_objid);
			
			/*	Obtain the ip_rte process's prohandle.						*/
			own_prohandle = op_pro_self ();
			
			/*	Obtain the name of the process. It is the "process model"	*/
			/*	attribute of the module.									*/
			op_ima_obj_attr_get (own_id, "process model", proc_model_name);
			
			/*	Register the process in the model-wide registry.			*/
			own_process_record_handle = (OmsT_Pr_Handle) oms_pr_process_register 
							(own_node_objid, 
							own_id, 
							own_prohandle, 
							proc_model_name);
			
			/*	Register the protocol attribute in the registry.			*/
			oms_pr_attr_set (own_process_record_handle, 
							"protocol", OMSC_PR_STRING, "ip", 
							OPC_NIL);
			
			/* Make this process TIP compliant by registering the address	*/
			/* information in the process registry.							*/
			oms_pr_attr_set (own_process_record_handle,
							"address", OMSC_PR_ADDRESS, &interface_table_ptr,
							OPC_NIL);
			
			/* Register the address information in the process registry.	*/
			oms_pr_attr_set (own_process_record_handle,
							"interface information", OMSC_PR_ADDRESS, ip_info_ptr,
							OPC_NIL);
			
			/*	If this IP node is a gateway, register it as a gateway.		*/
			if (gateway_status == OPC_TRUE)
				oms_pr_attr_set (own_process_record_handle, 
							"gateway node", OMSC_PR_STRING, "gateway", 
							OPC_NIL);
			
			/*	Register the subnet objid so that gateways in this local	*/
			/*	subnet can be found.										*/
			oms_pr_attr_set (own_process_record_handle, 
							"subnet", OMSC_PR_OBJID, subnet_objid, 
							OPC_NIL);
			
			/*	Create the list of multicast addresses this node can		*/
			/*	handle, and publish that list through the process registry.	*/
			mcast_addr_list_ptr = op_prg_list_create ();
			oms_pr_attr_set (own_process_record_handle, 
							"multicast address list", OMSC_PR_ADDRESS, mcast_addr_list_ptr,
							OPC_NIL);
			
			/*	A global address table is used to associate IP addresses	*/
			/*	with the corresponding lower layer address. This table is	*/
			/*	is managed by the NATO sub-package and optimizes address	*/
			/*	conversion table lookup. The IP routing process registers	*/
			/*	addresses in this table and makes it available (e.g., to 	*/
			/*	the IP ARP process) via process registry.					*/
			ip_rtab_table_handle_register ();
			
			/*	Build a table relating the process' interfaces to the links	*/
			/*	attached to the node.  This is used, among other potential	*/
			/*	purposes, for the auto-addressing capability.  Since mobile	*/
			/*	and satellite nodes do not support point-to-point or bus	*/
			/*	link connections, such a table is not build for those type	*/
			/*	of nodes. Build the table only if this is a fixed node.		*/
			link_iface_table_ptr = op_prg_list_create ();
			if (op_id_to_type (own_node_objid) == OPC_OBJTYPE_NDFIX)
				{
				ip3_link_iface_table_build (own_node_objid, link_iface_table_ptr);
				}
			
			/*	Schedule a self interrupt at the current time to let all	*/
			/*	the ip modules resolve their IP addresses. This involves	*/
			/*	assigning unique addresses and making sure that these are	*/
			/*	unique. Note that dynamically assigned addresses may change	*/
			/*	until all the statically assigned addresses have been		*/
			/*	checked for uniqueness.										*/
			op_intrpt_schedule_self (op_sim_time (), 0);
			
			/*	Create an ICI for communicating destination addresses to	*/
			/*	the address resultion protocol (ARP)						*/
			arp_iciptr = op_ici_create ("ip3_arp_req");
			if (arp_iciptr == OPC_NIL)
				ip_rte_error ("Unable to create ICI for communication with ARP.");
			
			/*	Create a datagram list to store datagram fragments until	*/
			/*	the complete datagram can be constructed.					*/
			dgram_list_ptr = ip_frag_sup_setup ();
			
			/*	Obtain the specified "Default Gateway". This specifies an 	*/
			/*	IP interface address to which a datagram needs to be 		*/
			/*	forwarded as its next hop. The attribute gets its name from	*/
			/*	the fact that the next hop would, typically, reside in a	*/
			/*	node acting as a gateway.									*/
			op_ima_obj_attr_get (own_id, "default gateway", default_gtwy_str);
			
			/*	Determine whether the default gateway has been specified 	*/
			/*	a value other than its default value.						*/
			if (strcmp (default_gtwy_str, IPC_AUTO_ADDRESS) == 0)
				{
				/*	No value has been specified to the "default gateway"	*/
				/*	model attribute. This means that an IP datagram may be	*/
				/*	dropped if a possible next hop cannot be determined		*/
				/*	using the routing tables. Store the fact that a default	*/
				/*	gateway information is not available.					*/
				default_gtwy_available = OPC_FALSE;
				}
			else
				{
				/*	Create the IP address of the default gateway from the	*/
				/*	value of the specified attribute.						*/
				default_gtwy_addr = ip_address_create (default_gtwy_str);
			
				/*	Set the variable that contains information on whether	*/
				/*	a default gateway is available.							*/
				default_gtwy_available = OPC_TRUE;
				}
			
			/* Initialize the variable used to determine whether IP auto-	*/
			/* addressing feature used for assigning and verifying IP		*/
			/* interface addresses.											*/
			strcpy (ipaa_status, "Enabled");
			
			/* Obtain the value specified to the "IP Auto Addressing"			*/
			/* simulation attribute. This is used to determine whether IP	*/
			/* address resolution feature is used.							*/
			op_ima_sim_attr_get (OPC_IMA_STRING, "IP Auto Addressing", ipaa_status);
			
			/* The IP auto-address resolution feature (used in the exit		*/
			/* excutives of the "init" state) is called depending upon the	*/
			/* value specified to this simulation attribute. If IPAA is not	*/
			/* used, then a default gateway must be specified. Generate an	*/
			/* error message if this condition is not met.					*/
			if ((strcmp (ipaa_status, IPAA_USED) != 0) && (default_gtwy_available == OPC_FALSE))
				{
				/* Terminate the simulation with an error message.			*/
				if (op_prg_odb_ltrace_active ("ip_errs"))
					{
					op_prg_odb_print_major ("Note:",
						" If \"IP Auto Addressing\" (a simulation attribute) is \"Disabled\", then",
						" \"Default IP Gateway\" may need to have an assignment. Use the interface",
						" address of a gateway to provide an assignment to this attribute.", OPC_NIL);
					}
				}
			
			/* Xin Wang added for DEBUG_MEM */
			num_ind_create = 0;
			num_ind_destroy = 0;
			num_higher_layer_forward = 0;
			
			num_pk=0;
			
			/* By cjin to host demo group ip 239.255.0.1 */
			DEMO_GRP_IP = ip_address_create( "239.255.0.1" );
			
			/* By cjin to host igmp protocol ip 224.0.0.2 */
			IGMP_PROTOCOL_IP = ip_address_create( "224.0.0.2" );
			}


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


		/** state (init) exit executives **/
		FSM_STATE_EXIT_UNFORCED (4, state4_exit_exec, "init", "A1_ip3m_rte () [init exit execs]")
			{
			/* At this point, the enter executives of the 'init' state are	*/
			/* guaranteed to have completed for all IP modules across the 	*/
			/* entire network model. This allows us to know that certain 	*/
			/* initializations have been performed on a global level. In 	*/
			/* particular, all IP modules have registered their primary 	*/
			/* process registry attributes. In the exit executives of init	*/
			/* we can now perform a second wave of initializations that 	*/
			/* rely on this first wave having completed. These activities 	*/
			/* include: 													*/
			/* 1) verifying consistency of interface addreess and subnet 	*/
			/*    mask assignments (consistency with other nodes as well). 	*/
			/* 2) choosing values for all "auto-assigned" inteface			*/
			/*    addresses and subnet masks. 								*/
			/* 3) checking for certain construcion problems in the node		*/
			/*	  model. 													*/
			/* 4) Construction of an interface table in the form of a state	*/
			/*    variable that reflects the assignments to the interface 	*/
			/*    attributes of the IP object. 								*/
			/* 5) Registration of each IP interface address into a global 	*/
			/*    address table to support a simulation efficiency method.	*/
			/* 6) Discovery of dynamic vs. static routing configuration and	*/
			/*    notification of dynamic routing processes that IP is 		*/
			/*    "ready" (i.e., fully configured.)							*/
			
			/* In case this node supports radio communication, verify that	*/
			/* all interfaces used to access radio channels are properly	*/
			/* configured. Radio interfaces are subject to different rules	*/
			/* for address assignment than are cable-connected interfaces. 	*/
			ip3_radio_addresses_verify (own_id);
			
			/*	Obtain a handle on the interface table. This attribute is 	*/
			/* a compound attribute (i.e., an  object) whose sub-objects	*/
			/* are individual interfaces. 									*/
			op_ima_obj_attr_get (own_id, "ip addr info", &comp_attr_objid);
			
			/*	Determine the number of interfaces in the table.			*/
			num_interfaces = op_topo_child_count (comp_attr_objid, OPC_OBJTYPE_GENERIC);
			
			/* Examine each interface in turn to: 							*/
			/* 1) verify that it is configured appropriately.				*/
			/* 2) complete its specifcation if necessary.					*/
			/* 3) extract information from it to construct an interface		*/
			/*    table in a state variable for faster access.				*/
			for (i = 0; i < num_interfaces; i++)
				{
				/* Obtain the object representing the i_th interface in the table. */
				iface_description_objid = op_topo_child (comp_attr_objid, OPC_OBJTYPE_GENERIC, i);
			
				/* Verify that at least one stream exists to support this interface. If not, issue	*/
				/* a warning. The system can still function, but it is probably not what the node	*/
				/* model developer intended. 														*/
				ip_stream_from_iface_index (i, &instrm, &outstrm);
			
				/*	The existencce of an outstream to support an interface is required. Otherwise, 	*/
				/*	IP might try to send packet on an attached stream, which will generate errors.	*/
				if (outstrm == -1)
					{
					sprintf (error_string, "IP cannot find output stream to support interface (%d)", i);
					ip_rte_error (error_string);
					}
				else
					{
					/* Resolve the interface's address and subnet mask attributes. This information		*/
					/* may already be specified directly in the attributes' assignments; However, it 	*/
					/* may be 'auto-assigned', meaning that the network model developer has requested	*/
					/* that the ip model choose appropriate values. This is done in a complex process	*/
					/* that examines and may also implicitly configure interfaces of other IP-capable	*/
					/* nodes in the network. This process may also generate errors for inconsistent 	*/
					/* interface assignments (i.e., inconsistent with other interfaces in the network). */
			
					/* The address resolution feature is used depending upon the value specified to the	*/
					/* simulation attribute - "IP Auto Addressing Usage".								*/
					if (strcmp (ipaa_status, IPAA_USED) == 0)
						{
						ip3_addr_resolve (iface_description_objid, i, link_iface_table_ptr);
						}
			
					/* Build an internal representation of the interface table. */
					/* This provides faster access and allows the table to be	*/
					/* transparently shared by other processes in the IP procss	*/
					/* group (for example, ARP). 								*/
					op_ima_obj_attr_get (iface_description_objid, "address",		ip_addr_str);
					op_ima_obj_attr_get (iface_description_objid, "subnet mask",	subnet_mask_str);
					op_ima_obj_attr_get (iface_description_objid, "mtu",			&mtu);
			
					/* If the address and or subnet mask are still auto assigned, 	*/
					/* then it must mean that the interface is not connected to any	*/
					/* links. Rather than leave an unconfigured interface in the 	*/
					/* the interface table, simply do not create an interface entry.*/
					if (!strcmp (ip_addr_str, IPC_AUTO_ADDRESS) ||
						!strcmp (subnet_mask_str, IPC_AUTO_ADDRESS))
						{
						continue;
						}
						
					/* Convert the string representations of the address/mask	*/
					/* to IP internal form. These will be stored as part of the	*/
					/* interface's 'address range' created below.				*/
					ip_addr = ip_address_create (ip_addr_str);
					subnet_mask_addr = ip_address_create (subnet_mask_str);
			
					/* A global address table is used to associate IP addresses	*/
					/* with the corresponding lower layer address. This table	*/
					/* is managed by the NATO sub-package. The IP routing		*/
					/* process registers addresses in this table and makes it	*/
					/* available to other processes (e.g., IP ARP) via process	*/
					/* registry.												*/
					ip_rtab_local_addr_register (ip_addr);
			
					/*	Create and initialzie a new cell to hold the interface	*/
					/*	information.											*/
					iface_info_ptr = ip_interface_info_create ();
					iface_info_ptr->addr_index = i;
					iface_info_ptr->addr_range_ptr = ip_address_range_create (ip_addr, subnet_mask_addr);
					iface_info_ptr->mtu	= mtu;
					iface_info_ptr->port_num = outstrm;
					iface_info_ptr->in_port_num = instrm;
			
					/*	Insert the cell into the interface table. */
			        op_prg_list_insert (interface_table_ptr, iface_info_ptr, OPC_LISTPOS_TAIL);
					}
				}
			
			/*	Obtain the object identifier of the "routing data" compound	*/
			/*	attribute. This attribute determines the type of routing	*/
			/*	used. If dynamic routing (RIP or OSPF) is specified, it is	*/
			/*	used instead of the user-created tables.					*/
			op_ima_obj_attr_get (own_id, "routing data", &routing_data_objid);
			routing_data_subobjid = op_topo_child (routing_data_objid, OPC_OBJTYPE_GENERIC, 0);
			
			/*	Obtain the type of dynamic routing to be used by this IP	*/
			/*	module. The possible options are RIP, OSPF or NONE (which	*/
			/*	indicates that no dynamic routing protocol is to be used.)	*/
			if (op_ima_obj_attr_get (routing_data_subobjid, "Dynamic Routing", dyn_route) == OPC_COMPCODE_FAILURE)
				ip_rte_error ("Unable to get name of IP Dynamic Routing from attribute.");
			
			/*	Determine whether dynamic routing is used for routing.		*/
			if ((strcmp (dyn_route, "rip") == 0) ||
				(strcmp (dyn_route, "ospf") == 0))
				{
				/*	Set a flag to indicate that dynamic routing is used.	*/
				dynamic_routing_enabled = OPC_TRUE;
			
				/*	Search through the process registry to find the			*/
				/*	appropriate process.									*/
				proc_record_handle_list_ptr = op_prg_list_create ();
			
				/* Discover type dynamic routing module being used.			*/
				if (strcmp (dyn_route, "rip") == 0)
					{
					oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr, 
							"protocol",		OMSC_PR_STRING,	"rip", 
							"node objid",	OMSC_PR_OBJID,	own_node_objid, 
							OPC_NIL);
					routing_protocol_used = IpC_Rte_Rip;
					}
				else
					{
					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);
					routing_protocol_used = IpC_Rte_Ospf;
					}
			
				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 an		*/
					/*	one dynamic routing process in the local node.		*/
					op_sim_end ("Error: either zero or several dynamic routing processes in the local node", "", "", "");
					}
				else
					{
					process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, 
											OPC_LISTPOS_HEAD);
			
			    	/*	Obtain the object id of the module performing the	*/
					/*	dynamic routing.									*/
					oms_pr_attr_get (process_record_handle, "module objid", OMSC_PR_OBJID, &dyn_rte_objid);
					}
			
				/*	Deallocate no longer needed process registry information*/
				while (op_prg_list_size (proc_record_handle_list_ptr))
					op_prg_list_remove (proc_record_handle_list_ptr, OPC_LISTPOS_HEAD);
				op_prg_mem_free (proc_record_handle_list_ptr);
			
				/*	Schedule a remote interrupt to the local dynamic		*/
				/*	routing protocol process to notify it that the IP		*/
				/*	interface table can now be accessed.					*/
				op_intrpt_schedule_remote (op_sim_time (), 0, dyn_rte_objid);
				}
			else
				{
				/*	Obtain the static routing entries specified in the		*/
				/*	"Internal routing Table" attribute.						*/
				routing_entry_objid = op_topo_child (routing_data_objid, OPC_OBJTYPE_GENERIC, 0);
				op_ima_obj_attr_get (routing_entry_objid, "Internal Routing Table", &internal_rte_table_objid);
			
				/*	Create the routing table from the object ID.			*/
				static_rte_table = ip_rte_table_parse (internal_rte_table_objid);
			
				/*	Set status flags to indicate use of static routing.		*/
				dynamic_routing_enabled = OPC_FALSE;
				routing_protocol_used = IpC_Rte_Static;
			
				/*	Fill in the necessary components for IP routing API.	*/
				ip_rte_select_func_ptr = (IpT_Rte_Table_Lookup_Proc) ip_rte_table_route_select;
				ip_dyn_route_table_ptr = static_rte_table;
				}
			
			/* Following remote schedule is added by cjin to invoke igmphost to register protocol number 224.0.0.2 */
			if(!gateway_status) {
			     proc_record_handle_list_ptr = op_prg_list_create();
			
			
			    oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr, 
							"protocol", 	OMSC_PR_STRING, "igmprouter",
							"node objid", 	OMSC_PR_OBJID, 	op_topo_parent (own_id), 
				             OPC_NIL);
			
				record_handle_list_size = op_prg_list_size (proc_record_handle_list_ptr);
				if (record_handle_list_size == 0)
					{
					/*	An error should be created if there are more than	*/
					/*	one igmp in the local node */
					op_sim_end ("Error: either zero or several igmprouter", "", "", "");
					}
				else
					{
					process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, 
										OPC_LISTPOS_HEAD);
			
			         /*	Obtain the object id of the module performing the	*/
					/*	 igmp							    */
					oms_pr_attr_get (process_record_handle, "module objid", OMSC_PR_OBJID, &igmprouter_objid);
					}
			
			    	 
				/*	Deallocate the list pointer.							*/
				op_prg_mem_free (proc_record_handle_list_ptr);	
			
				/*	Schedule a remote interrupt to the local  igmprouter, actually host    */
				op_intrpt_schedule_remote (op_sim_time (), 0, igmprouter_objid);
			}
			}


		/** state (init) transition processing **/
		FSM_TRANSIT_FORCE (7, state7_enter_exec, ;)
		/*---------------------------------------------------------*/



		/** state (init_too) enter executives **/
		FSM_STATE_ENTER_UNFORCED (5, state5_enter_exec, "init_too", "A1_ip3m_rte () [init_too enter execs]")
			{
			
			}


		/** blocking after enter executives of unforced state. **/
		FSM_EXIT (11,A1_ip3m_rte)


		/** state (init_too) exit executives **/
		FSM_STATE_EXIT_UNFORCED (5, state5_exit_exec, "init_too", "A1_ip3m_rte () [init_too exit execs]")
			{
			/*	The dynamic routing table pointer and the pointer to route	*/
			/*	select function are now obtained from the model-wide		*/
			/*	registry, if a dynamic routing protocol is used. The		*/
			/*	output stream index to the Encap module is also obtained.	*/
			if (dynamic_routing_enabled && gateway_status)
				{
				/*	Obtain the process record handle of the dynamic routing	*/
				/*	process that resides in the local node. This should		*/
				/*	only be done if IP is using dynamic routing.			*/
				proc_record_handle_list_ptr = op_prg_list_create();
			
				if (routing_protocol_used == IpC_Rte_Rip)
					oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr, 
							"protocol", 	OMSC_PR_STRING, "rip",
							"node objid", 	OMSC_PR_OBJID, 	own_node_objid, 
							OPC_NIL);
				else if (routing_protocol_used == IpC_Rte_Ospf)
					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);
				else
					{
					/*	Dynamic routing shouldn't be enabled without the	*/
					/*	proper protocol. This situation indicates an error.	*/
					ip_rte_error ("Dynamic routing enabled without dynamic routing protocol.");
					}
			
				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, &ip_dyn_rte_info_ptr);
					}
			
				/* Deallocate no longer needed process registry information	*/
				while (op_prg_list_size (proc_record_handle_list_ptr))
					op_prg_list_remove (proc_record_handle_list_ptr, OPC_LISTPOS_HEAD);
				op_prg_mem_free (proc_record_handle_list_ptr);
				
				/*	Obtain the function pointer to the routing table		*/
				/*	lookup function.										*/
				ip_rte_select_func_ptr = ip_dyn_rte_info_ptr->lookup_proc;
			
				/*	Obtain a pointer to the dynamic routing table.			*/
				ip_dyn_route_table_ptr = ip_dyn_rte_info_ptr->table_handle; 
				}
			
			/*	Obtain the outstream index to ip_encap module.				*/
			proc_record_handle_list_ptr = op_prg_list_create ();
			
			oms_pr_process_discover (own_id, proc_record_handle_list_ptr, 
							"protocol", OMSC_PR_STRING, "ip_encap", 
							OPC_NIL);
			
			record_handle_list_size = op_prg_list_size (proc_record_handle_list_ptr);
			if (record_handle_list_size != 1)
				{
				if (dynamic_routing_enabled)
					{
					/*	An error should be created if there are zero or		*/
					/*	more than one ip_encap process in the local node.	*/
					op_sim_end ("Error: either zero or several ip_encap processes connected to ip", "", "", "");
					}
				}
			else
				{
				process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, 
										OPC_LISTPOS_HEAD);
				}
			
			/*	Obtain the ip_encap module objid, and the output stream		*/
			/*	index from IP to IP Encap module.							*/
			oms_pr_attr_get (process_record_handle, "module objid", OMSC_PR_OBJID, &ip_encap_objid);
			oms_tan_neighbor_streams_find (own_id, ip_encap_objid, &instrm_from_ip_encap, &outstrm_to_ip_encap);
			
			/*	Deallocate no longer needed process registry information.	*/
			while (op_prg_list_size (proc_record_handle_list_ptr))
				op_prg_list_remove (proc_record_handle_list_ptr, OPC_LISTPOS_HEAD);
			op_prg_mem_free (proc_record_handle_list_ptr);
			
			/* Run final report for link utitization if specified.	*/
			/* The called function checks whether reporting is		*/
			/* specified.											*/
			if (END_SIM)
				lu_report ();
			
			
			}


		/** state (init_too) transition processing **/
		FSM_INIT_COND (ARRIVAL)
		FSM_TEST_COND (END_SIM)
		FSM_TEST_COND (MCAST_REGISTRATION)
		FSM_DFLT_COND
		FSM_TEST_LOGIC ("init_too")

		FSM_TRANSIT_SWITCH
			{
			FSM_CASE_TRANSIT (0, 0, state0_enter_exec, ;)
			FSM_CASE_TRANSIT (1, 5, state5_enter_exec, ;)
			FSM_CASE_TRANSIT (2, 8, state8_enter_exec, ;)
			FSM_CASE_TRANSIT (3, 1, state1_enter_exec, ;)
			}
		/*---------------------------------------------------------*/



		/** state (wait) enter executives **/
		FSM_STATE_ENTER_UNFORCED (6, state6_enter_exec, "wait", "A1_ip3m_rte () [wait enter execs]")
			{
			/*	Schedule a self interrupt to allow other stations to register	*/
			/*	their IP addresses.											*/
			op_intrpt_schedule_self (op_sim_time (), 0);
			}


		/** blocking after enter executives of unforced state. **/
		FSM_EXIT (13,A1_ip3m_rte)


		/** state (wait) exit executives **/
		FSM_STATE_EXIT_UNFORCED (6, state6_exit_exec, "wait", "A1_ip3m_rte () [wait exit execs]")
			{
			/*	The process will enter this state when all IP interfaces	*/
			/*	have been treated via the "ip3_addr_resolve ()" service.	*/
			/*	Perform cleanup operations (e.g., memory deallocation) for	*/
			/*	the state built while using this service.					*/
			ip3_auto_addr_cleanup ();
			
			/* Run final report for link utitization if specified.	*/
			/* The called function checks whether reporting is		*/
			/* specified.											*/
			if (END_SIM)
				lu_report ();
			}


		/** state (wait) transition processing **/
		FSM_INIT_COND (SELF_NOTIFICATION)
		FSM_TEST_COND (END_SIM)
		FSM_TEST_LOGIC ("wait")

		FSM_TRANSIT_SWITCH
			{
			FSM_CASE_TRANSIT (0, 5, state5_enter_exec, ;)
			FSM_CASE_TRANSIT (1, 6, state6_enter_exec, ;)
			}
		/*---------------------------------------------------------*/



		/** state (mcast_register) enter executives **/
		FSM_STATE_ENTER_FORCED (7, state7_enter_exec, "mcast_register", "A1_ip3m_rte () [mcast_register enter execs]")
			{
			/* Added by Xin Wang      */
			/* Modified date: 3/3/97  */
			
			op_ima_obj_attr_get (own_node_objid, "name", router_name);
			
			if (gateway_status) { /* this node is a router */
			
			/*	Obtain the object identifier of the "routing data" compound	*/
			/*	attribute. This attribute determines the type of routing	*/
			/*	used. If dynamic routing (pim_dm) or (pim_sm) is specified, */
			/*  it is used instead of the user-created tables.				*/
			op_ima_obj_attr_get (own_id, "mcast routing data", &mcast_routing_data_objid);
			mcast_routing_data_subobjid = op_topo_child (mcast_routing_data_objid, OPC_OBJTYPE_GENERIC, 0);
			
			/*	Obtain the type of dynamic routing to be used by this IP	*/
			/*	module. The possible options are pim_dm, pim_sm or NONE     */
			/*  (which dicates that no dynamic routing protocol is to be    */
			/*  used.)	                                                    */
			if (op_ima_obj_attr_get (mcast_routing_data_subobjid, "Mcast Dynamic Routing", mcast_dyn_route) == OPC_COMPCODE_FAILURE)
				ip_rte_error ("Unable to get name of IP Dynamic Routing from attribute.");
			
			/*	Determine whether mcast dynamic routing is used for routing.		*/
			if ((strcmp (mcast_dyn_route, "pim_dm") == 0) || (strcmp (mcast_dyn_route, "pim_sm") == 0))
				{
				/*	Set a flag to indicate that dynamic routing is used.	*/
				mcast_dynamic_routing_enabled = OPC_TRUE;
			
				/*	Search through the process registry to find the			*/
				/*	appropriate process.									*/
			    /*  Note, we nned to add IpC_Rte_PimDM into ip3_rte.h       */
			    proc_record_handle_list_ptr = op_prg_list_create ();
			 
			    if (strcmp (mcast_dyn_route, "pim_dm") == 0)
			       {
			  	   oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr, 
							"protocol",		OMSC_PR_STRING,	"pim_dm", 
							"node objid",	OMSC_PR_OBJID,	own_node_objid, 
							OPC_NIL);
			       mcast_routing_protocol_used = IpC_Rte_PimDM;
			       }
			    else
			       {
			       oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr, 
							"protocol",		OMSC_PR_STRING,	"pim_sm", 
							"node objid",	OMSC_PR_OBJID,	own_node_objid, 
							OPC_NIL);
			       mcast_routing_protocol_used = IpC_Rte_PimSM;
				   }
				}
			else
				{
				/*	Obtain the static routing entries specified in the		*/
				/*	"Internal routing Table" attribute.						*/
				routing_entry_objid = op_topo_child (mcast_routing_data_objid, OPC_OBJTYPE_GENERIC, 0);
				op_ima_obj_attr_get (mcast_routing_entry_objid, "Mcast Internal Routing Table", &mcast_internal_rte_table_objid);
			
				/*	Create the routing table from the object ID.			*/
				static_rte_table = ip_rte_table_parse (mcast_internal_rte_table_objid);
			
				/*	Set status flags to indicate use of static routing.		*/
				mcast_dynamic_routing_enabled = OPC_FALSE;
				mcast_routing_protocol_used = IpC_Rte_Static;
			    ip_mcast_dyn_route_table_ptr = static_rte_table;
				}
			
			}
			
			 
			}


		/** state (mcast_register) exit executives **/
		FSM_STATE_EXIT_FORCED (7, state7_exit_exec, "mcast_register", "A1_ip3m_rte () [mcast_register exit execs]")
			{
			}


		/** state (mcast_register) transition processing **/
		FSM_TRANSIT_FORCE (6, state6_enter_exec, ;)
		/*---------------------------------------------------------*/



		/** state (init_mcast) enter executives **/
		FSM_STATE_ENTER_FORCED (8, state8_enter_exec, "init_mcast", "A1_ip3m_rte () [init_mcast enter execs]")
			{
			/* Added by Xin Wang      */
			/* Modified date: 3/3/98 */
			/* gets handle to mcast routing data etc. : equivalent of exit execs (unicast) */
			 
			/*	The dynamic mcast routing table pointer and the pointer to  */
			/*  route select function are now obtained from the model-wide  */
			/*	registry, if a mcast dynamic routing protocol is used. The	*/
			/*	output stream index to the Encap module is also obtained.	*/
			if (mcast_dynamic_routing_enabled && gateway_status)
				{
				/*	Obtain the process record handle of the dynamic mcast   */
			    /*  routing process that resides in the local node. This    */ 
			    /*  should only be done if IP is using dynamic routing.	    */
				proc_record_handle_list_ptr = op_prg_list_create();
			
				if (mcast_routing_protocol_used == IpC_Rte_PimDM)
					oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr, 
							"protocol", 	OMSC_PR_STRING, "pim_dm",
							"node objid", 	OMSC_PR_OBJID, 	own_node_objid, 
							OPC_NIL);
			    else if (mcast_routing_protocol_used == IpC_Rte_PimSM)
					oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr, 
							"protocol", 	OMSC_PR_STRING, "pim_sm",
							"node objid", 	OMSC_PR_OBJID, 	own_node_objid, 
							OPC_NIL);
				else
					{
					/*	Dynamic routing shouldn't be enabled without the	*/
					/*	proper protocol. This situation indicates an error.	*/
					ip_rte_error ("Dynamic mcast routing enabled without dynamic routing protocol.");
					}
			
				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 mcast routing process in the local node.*/
					op_sim_end ("Error: either zero or several mcast dynamic routing proceses in the local node", "", "", "");
					}
				else
					{
					process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, 
										OPC_LISTPOS_HEAD);
			
			    	/*	Obtain the routing information structure pointer.	*/
					oms_pr_attr_get (process_record_handle, "routing information", OMSC_PR_ADDRESS, &ip_mcast_dyn_rte_info_ptr);
					}
			
				/*	Deallocate the list pointer.							*/
				op_prg_mem_free (proc_record_handle_list_ptr);	
				
				
				/*	Obtain a pointer to the dynamic routing table.			*/
			    Mroute_table_ptr = (List *) ip_mcast_dyn_rte_info_ptr->table_handle;
			   
			    /* Following code is for getting IGMP table pointer */
			    proc_record_handle_list_ptr = op_prg_list_create();
			    oms_pr_process_discover (OPC_OBJID_INVALID, proc_record_handle_list_ptr, 
							"protocol", 	OMSC_PR_STRING, "igmprouter",
							"node objid", 	OMSC_PR_OBJID, 	own_node_objid, 
							OPC_NIL);   
			 
			
				if (record_handle_list_size != 1)
					{
					/*	An error should be created if there are more than	*/
					/*	one dynamic mcast routing process in the local node.*/
					op_sim_end ("Error: either zero or several igmprouter 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, "Mlocal table", OMSC_PR_ADDRESS, &Mlocal_table_ptr);
					}
			
			    /*	Deallocate the list pointer.							*/
				op_prg_mem_free (proc_record_handle_list_ptr);	
				
			    //printf ("%s Mlocal_table_ptr = %p\n", router_name, Mlocal_table_ptr);	
			    //op_sim_end ("get IGMP table pointer successful", OPC_NIL, OPC_NIL, OPC_NIL);
			
			   }
			
			 
			
			}


		/** state (init_mcast) exit executives **/
		FSM_STATE_EXIT_FORCED (8, state8_exit_exec, "init_mcast", "A1_ip3m_rte () [init_mcast exit execs]")
			{
			}


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



		}


	FSM_EXIT (4,A1_ip3m_rte)
	}




void
A1_ip3m_rte_svar (prs_ptr,var_name,var_p_ptr)
	A1_ip3m_rte_state		*prs_ptr;
	char			*var_name, **var_p_ptr;
	{

	FIN (A1_ip3m_rte_svar (prs_ptr))

	*var_p_ptr = VOS_NIL;
	if (Vos_String_Equal ("server_busy" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_server_busy);
	if (Vos_String_Equal ("service_rate" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_service_rate);
	if (Vos_String_Equal ("own_id" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_own_id);
	if (Vos_String_Equal ("static_rte_table" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_static_rte_table);
	if (Vos_String_Equal ("arp_iciptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_arp_iciptr);
	if (Vos_String_Equal ("dgram_list_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_dgram_list_ptr);
	if (Vos_String_Equal ("default_ttl" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_default_ttl);
	if (Vos_String_Equal ("interface_table_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_interface_table_ptr);
	if (Vos_String_Equal ("ip_info_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ip_info_ptr);
	if (Vos_String_Equal ("dynamic_routing_enabled" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_dynamic_routing_enabled);
	if (Vos_String_Equal ("routing_protocol_used" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_routing_protocol_used);
	if (Vos_String_Equal ("ip_rte_select_func_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ip_rte_select_func_ptr);
	if (Vos_String_Equal ("ip_dyn_route_table_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ip_dyn_route_table_ptr);
	if (Vos_String_Equal ("link_iface_table_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_link_iface_table_ptr);
	if (Vos_String_Equal ("own_node_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_own_node_objid);
	if (Vos_String_Equal ("own_prohandle" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_own_prohandle);
	if (Vos_String_Equal ("own_process_record_handle" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_own_process_record_handle);
	if (Vos_String_Equal ("proc_model_name" , var_name))
		*var_p_ptr = (char *) (prs_ptr->sv_proc_model_name);
	if (Vos_String_Equal ("instrm_from_ip_encap" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_instrm_from_ip_encap);
	if (Vos_String_Equal ("outstrm_to_ip_encap" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_outstrm_to_ip_encap);
	if (Vos_String_Equal ("dyn_rte_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_dyn_rte_objid);
	if (Vos_String_Equal ("gateway_status" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_gateway_status);
	if (Vos_String_Equal ("gateway_known" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_gateway_known);
	if (Vos_String_Equal ("next_addr_gateway" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_next_addr_gateway);
	if (Vos_String_Equal ("comp_attr_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_comp_attr_objid);
	if (Vos_String_Equal ("subnet_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_subnet_objid);
	if (Vos_String_Equal ("interface_table_size" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_interface_table_size);
	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 ("default_gtwy_addr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_default_gtwy_addr);
	if (Vos_String_Equal ("default_gtwy_available" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_default_gtwy_available);
	if (Vos_String_Equal ("ipaa_status" , var_name))
		*var_p_ptr = (char *) (prs_ptr->sv_ipaa_status);
	if (Vos_String_Equal ("mcast_interface_is_specified" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_mcast_interface_is_specified);
	if (Vos_String_Equal ("mcast_dynamic_routing_enabled" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_mcast_dynamic_routing_enabled);
	if (Vos_String_Equal ("mcast_routing_protocol_used" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_mcast_routing_protocol_used);
	if (Vos_String_Equal ("ip_mcast_rte_select_func_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ip_mcast_rte_select_func_ptr);
	if (Vos_String_Equal ("mcast_routing_entry_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_mcast_routing_entry_objid);
	if (Vos_String_Equal ("mcast_internal_rte_table_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_mcast_internal_rte_table_objid);
	if (Vos_String_Equal ("ip_mcast_dyn_route_table_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_ip_mcast_dyn_route_table_ptr);
	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 ("router_name" , var_name))
		*var_p_ptr = (char *) (prs_ptr->sv_router_name);
	if (Vos_String_Equal ("num_oif" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_num_oif);
	if (Vos_String_Equal ("MCAST_IP_RTE_IND" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_MCAST_IP_RTE_IND);
	if (Vos_String_Equal ("mcast_data_from_higher_layer" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_mcast_data_from_higher_layer);
	if (Vos_String_Equal ("MLOCAL" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_MLOCAL);
	if (Vos_String_Equal ("test" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_test);
	if (Vos_String_Equal ("num_ind_create" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_num_ind_create);
	if (Vos_String_Equal ("num_ind_destroy" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_num_ind_destroy);
	if (Vos_String_Equal ("num_higher_layer_forward" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_num_higher_layer_forward);
	if (Vos_String_Equal ("num_pk" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_num_pk);
	if (Vos_String_Equal ("DEMO_GRP_IP" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_DEMO_GRP_IP);
	if (Vos_String_Equal ("IGMP_PROTOCOL_IP" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_IGMP_PROTOCOL_IP);

	FOUT;
	}




void
A1_ip3m_rte_diag ()
	{
	double          		pk_svc_time;
	int             		insert_ok;
	int						instrm;
	int						outstrm = IP_OUTSTRM_INVALID;
	int						mtu = -1;
	int						i, frag, frag_accum, num_frags, k, instrm_index;
	int						frag_size, data_size, len;
	int						header_size, ttl;
	char					str0 [512], str1 [512];
	Packet					*pkptr, *ip_pkptr, *frag_ptr, *pkcopy;
	Packet					*ip_igmp_pkptr; // added by cjin
	Packet					*data_pkptr;
	int						num_output_streams, num_input_streams;
	int						instrm_ip_addr_index;
	Objid					strm_objid, ip_encap_objid, instrm_objid;
	IpT_Interface_Info*		iface_info_ptr;
	IpT_Interface_Info*		temp_iface_info_ptr;
	Ici*					rip_iciptr;
	IpT_Rte_Info*			ip_dyn_rte_info_ptr;
	int						ip_fast_addr = -1;
	Boolean					broadcast_mode, entry_included;
	List*					proc_record_handle_list_ptr;
	int						record_handle_list_size;
	OmsT_Pr_Handle			process_record_handle;
	Compcode				route_status;
	Evhandle				evh;
	Boolean					ip_dest_local_network;
	int						rip_rte_table_size;
	Objid					iface_description_objid;
	int						num_interfaces;
	Objid					line_objid;
	Objid					routing_data_objid;
	Objid					routing_data_subobjid;
	char					dyn_route [512];
	int						conn_class;
	int						proto_num;
	char					ip_packet_format [256];
	char					ip_addr_str [IPC_ADDR_STR_LEN];
	char					subnet_mask_str [IPC_ADDR_STR_LEN];
	IpT_Address				ip_addr;
	IpT_Address				subnet_mask_addr;
	IpT_Address				dest_addr;
	IpT_Address				next_addr = OPC_NIL;
	Boolean					broadcast = OPC_FALSE;
	Boolean					higher_layer = OPC_FALSE;
	Boolean					destroy_pkt = OPC_FALSE;
	Boolean					in_port_found;
	char					dest_addr_str [IPC_ADDR_STR_LEN];
	Objid					internal_rte_table_objid, routing_entry_objid;
	Ici*					intf_ici_ptr = OPC_NIL;
	Ici*					mcast_ici_ptr;
	IpT_Address				mcast_intf_addr = OPC_NIL;
	char					ici_format_str [128];
	char					trace_msg [128];
	char					intf_addr_str [IPC_ADDR_STR_LEN];
	int						intf_table_size;
	Boolean					broadcast_addr;
	int						intf_num;
	char					strm_name [128];
	int						iface_table_size;
	char					error_string [512];
	char					default_gtwy_str [IPC_ADDR_STR_LEN];
	IpT_Address				intf_addr;
	
	/* Added by Anindya Neogi */
	
	IpT_Address             source_addr;
	MCAST_Addr              group;
	int                     MCAST;
	int                     DONE;
	int                     MLOCALEntryIndex;
	int                     MROUTEEntryIndex;
	MROUTE_Entry           *MROUTEEntry = NULL;
	MLOCAL_Entry           *MLOCALEntry = NULL;
	int ROUTER = 0;
	
	
	/* Added by Xin Wang  3/3/98                     */
	IpT_Rte_Info*			ip_mcast_dyn_rte_info_ptr;
	Objid					mcast_routing_data_objid;
	Objid					mcast_routing_data_subobjid;
	char					mcast_dyn_route [512];
	IpT_Address             arrived_intf_addr;
	IpT_Address             src_addr;
	
	Ici*		            mcast_intf_ici_ptr = OPC_NIL;
	Ici*		            mcast_copy_intf_ici_ptr = OPC_NIL;
	Ici*                    ici_ptr; 
	char *                  format_name [256];
	char                    err_msg [256];
	int                     mcast_entry_index;
	PimDM_Mroute_Entry     *mroute_entry;
	int                     data_packet;
	OIF_Interface *         oif_intf;
	int                     intf_prune;
	Packet *                tmp_pkptr;
	Packet *                tmp_data;
	Packet	               *mcast_copy_pkptr, *mcast_pkptr;
	SrcGrpPair             *SourceGroup;
	IpT_Address				tmp_dest_addr;
	char *                  proto_str;
	char		            next_addr_str [IPC_ADDR_STR_LEN];
	char		            src_addr_str [IPC_ADDR_STR_LEN];
	char                    tmp_dest_addr_str [IPC_ADDR_STR_LEN];
	int                     num_Mroute_entry;
	PimDM_Mroute_Entry *    Mroute_table_entry;
	int                     Mroute_index;  
	int                     test_oif;
	/* end of change  */
	
	/* By cjin */
	int host_igmp;
	char             tmp_addr_str [IPC_ADDR_STR_LEN];
	
	Objid			igmprouter_objid;
	
	
	int _block_origin;

	FIN (A1_ip3m_rte_diag ())

	BINIT
	if (op_prg_odb_ltrace_active ("ip_frag"))
		{
		ip_frag_sup_print (dgram_list_ptr);
		}
	
	if (LTRACE_GLOBAL_TABLE_ACTIVE)
		{
		ip_rtab_print ();
		}
	
	
	if (op_prg_odb_ltrace_active ("ip_interfaces"))
		{
		ip_interface_table_print (interface_table_ptr);
		}
	
	if (op_prg_odb_ltrace_active ("ip_static_rte"))
		{
		ip_rte_table_print (static_rte_table);
		}

	FOUT;
	}




void
A1_ip3m_rte_terminate ()
	{
	double          		pk_svc_time;
	int             		insert_ok;
	int						instrm;
	int						outstrm = IP_OUTSTRM_INVALID;
	int						mtu = -1;
	int						i, frag, frag_accum, num_frags, k, instrm_index;
	int						frag_size, data_size, len;
	int						header_size, ttl;
	char					str0 [512], str1 [512];
	Packet					*pkptr, *ip_pkptr, *frag_ptr, *pkcopy;
	Packet					*ip_igmp_pkptr; // added by cjin
	Packet					*data_pkptr;
	int						num_output_streams, num_input_streams;
	int						instrm_ip_addr_index;
	Objid					strm_objid, ip_encap_objid, instrm_objid;
	IpT_Interface_Info*		iface_info_ptr;
	IpT_Interface_Info*		temp_iface_info_ptr;
	Ici*					rip_iciptr;
	IpT_Rte_Info*			ip_dyn_rte_info_ptr;
	int						ip_fast_addr = -1;
	Boolean					broadcast_mode, entry_included;
	List*					proc_record_handle_list_ptr;
	int						record_handle_list_size;
	OmsT_Pr_Handle			process_record_handle;
	Compcode				route_status;
	Evhandle				evh;
	Boolean					ip_dest_local_network;
	int						rip_rte_table_size;
	Objid					iface_description_objid;
	int						num_interfaces;
	Objid					line_objid;
	Objid					routing_data_objid;
	Objid					routing_data_subobjid;
	char					dyn_route [512];
	int						conn_class;
	int						proto_num;
	char					ip_packet_format [256];
	char					ip_addr_str [IPC_ADDR_STR_LEN];
	char					subnet_mask_str [IPC_ADDR_STR_LEN];
	IpT_Address				ip_addr;
	IpT_Address				subnet_mask_addr;
	IpT_Address				dest_addr;
	IpT_Address				next_addr = OPC_NIL;
	Boolean					broadcast = OPC_FALSE;
	Boolean					higher_layer = OPC_FALSE;
	Boolean					destroy_pkt = OPC_FALSE;
	Boolean					in_port_found;
	char					dest_addr_str [IPC_ADDR_STR_LEN];
	Objid					internal_rte_table_objid, routing_entry_objid;
	Ici*					intf_ici_ptr = OPC_NIL;
	Ici*					mcast_ici_ptr;
	IpT_Address				mcast_intf_addr = OPC_NIL;
	char					ici_format_str [128];
	char					trace_msg [128];
	char					intf_addr_str [IPC_ADDR_STR_LEN];
	int						intf_table_size;
	Boolean					broadcast_addr;
	int						intf_num;
	char					strm_name [128];
	int						iface_table_size;
	char					error_string [512];
	char					default_gtwy_str [IPC_ADDR_STR_LEN];
	IpT_Address				intf_addr;
	
	/* Added by Anindya Neogi */
	
	IpT_Address             source_addr;
	MCAST_Addr              group;
	int                     MCAST;
	int                     DONE;
	int                     MLOCALEntryIndex;
	int                     MROUTEEntryIndex;
	MROUTE_Entry           *MROUTEEntry = NULL;
	MLOCAL_Entry           *MLOCALEntry = NULL;
	int ROUTER = 0;
	
	
	/* Added by Xin Wang  3/3/98                     */
	IpT_Rte_Info*			ip_mcast_dyn_rte_info_ptr;
	Objid					mcast_routing_data_objid;
	Objid					mcast_routing_data_subobjid;
	char					mcast_dyn_route [512];
	IpT_Address             arrived_intf_addr;
	IpT_Address             src_addr;
	
	Ici*		            mcast_intf_ici_ptr = OPC_NIL;
	Ici*		            mcast_copy_intf_ici_ptr = OPC_NIL;
	Ici*                    ici_ptr; 
	char *                  format_name [256];
	char                    err_msg [256];
	int                     mcast_entry_index;
	PimDM_Mroute_Entry     *mroute_entry;
	int                     data_packet;
	OIF_Interface *         oif_intf;
	int                     intf_prune;
	Packet *                tmp_pkptr;
	Packet *                tmp_data;
	Packet	               *mcast_copy_pkptr, *mcast_pkptr;
	SrcGrpPair             *SourceGroup;
	IpT_Address				tmp_dest_addr;
	char *                  proto_str;
	char		            next_addr_str [IPC_ADDR_STR_LEN];
	char		            src_addr_str [IPC_ADDR_STR_LEN];
	char                    tmp_dest_addr_str [IPC_ADDR_STR_LEN];
	int                     num_Mroute_entry;
	PimDM_Mroute_Entry *    Mroute_table_entry;
	int                     Mroute_index;  
	int                     test_oif;
	/* end of change  */
	
	/* By cjin */
	int host_igmp;
	char             tmp_addr_str [IPC_ADDR_STR_LEN];
	
	Objid			igmprouter_objid;
	
	
	int _block_origin;

	FIN (A1_ip3m_rte_terminate ())

	BINIT

	FOUT;
	}



Compcode
A1_ip3m_rte_init (pr_state_pptr)
	A1_ip3m_rte_state			**pr_state_pptr;
	{
	int _block_origin = 0;
	static VosT_Cm_Obtype	obtype = OPC_NIL;

	extern int				Vos_Nop ();

	FIN (A1_ip3m_rte_init (pr_state_pptr))

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

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



enum { _block_origin = __LINE__ };
/* Functions for IP routing support */

/* BEGIN_JOE_SUN */

/* if the mcast interface is specified by the upper layer protocols, */
/* then 1) set mcast_intf_addr to the it */
/* 		2) set the Boolean mcast_interace_is_specified to TRUE, */
/* else 	*/
/* 		1) set mcast_intf_addr to OPC_NIL */
/* 		2) set mcast_interface_is_specified to FALSE */

void 
ip_rte_check_and_get_mcast_interface_addr (Packet *arg_pkptr, IpT_Address *ptr_to_mcast_intf_addr, Boolean *ptr_to_mcast_interface_is_specified)
{
	Ici *mcast_ici_ptr;
	char ici_format_str [128];

    FIN (ip_rte_check_and_get_mcast_interface_addr ( Packet *arg_pkptr, IpT_Address *ptr_to_mcast_intf_addr, Boolean *ptr_to_mcast_interface_is_specified)); 
	/* By default, the interface is assumed to be unspecified*/
	*ptr_to_mcast_intf_addr = OPC_NIL;
	*ptr_to_mcast_interface_is_specified = FALSE;

	/* If no ici is set, just return */
	mcast_ici_ptr = op_pk_ici_get(arg_pkptr);
	if (mcast_ici_ptr == OPC_NIL ) 
	   FOUT; 

	/* If not the right format, just return, */
	op_ici_format(mcast_ici_ptr, ici_format_str);
	if (strcmp(ici_format_str, "ip3_rte_req") != 0)
	   FOUT; 
	
	op_ici_attr_get(mcast_ici_ptr, "multicast_interface_address", ptr_to_mcast_intf_addr);
	
	if (*ptr_to_mcast_intf_addr != OPC_NIL)
	{
		/* Looks like mcast_addr interface is specified */
        
		*ptr_to_mcast_interface_is_specified = TRUE;
	}

    /* Added by Xin Wang */
    if (mcast_ici_ptr != OPC_NIL) {
       op_pk_ici_set (arg_pkptr, OPC_NIL);
	   op_ici_destroy (mcast_ici_ptr);
    }
    FOUT; 
}	           	
/* END_JOE_SUN */

ip_stream_from_iface_index (int iface_index, int* in_stream_ptr, int* out_stream_ptr)
	{
	int				num_dirs, assoc_dir;
	int				s, num_streams;
	int				assigned_iface_index;
	Objid			stream_objid;

	/* Find a stream connected to the IP-layer module that supports 	*/
	/* the specified interface. The interface is specified by its 		*/
	/* index in the interface table. 									*/
	FIN (ip_stream_from_iface_index (int iface_index, int* out_stream_ptr, int* in_stream_ptr))

	/*	Initialize the stream index output arguments to indicate that	*/
	/*	no streams were found. These values will be overwritten if the	*/
	/*	streams are found.												*/
	*in_stream_ptr = *out_stream_ptr = -1;

	/* Loop through all input and output streams to check for the 		*/
	/* presence and value of the "ip addr index" attribute. This 		*/
	/* extended attribute specifies the interface supported by the 		*/
	/* stream, if any. 													*/
	for (num_dirs = 0, assoc_dir = OPC_TOPO_ASSOC_IN; num_dirs < 2; 
		assoc_dir = OPC_TOPO_ASSOC_OUT, num_dirs++)
		{
		/* For each direction (in and out), now loop through the streams of that type. */
		num_streams = op_topo_assoc_count (own_id, assoc_dir, OPC_OBJTYPE_STRM);
		for (s = 0; s < num_streams; s++)
			{
			/* Obtain the object ID of the s_th stream. */
			stream_objid = op_topo_assoc (own_id, assoc_dir, OPC_OBJTYPE_STRM, s);
			
			/* The extended attribute might not exist on all connected streams, 	*/
			/* so check first for its presence. This avoids generating an error 	*/
			/* when getting the attribute value. 									*/
			if (op_ima_obj_attr_exists (stream_objid, "ip addr index") == OPC_TRUE)
				{
				/* Obtain the index of the interface assigned to the stream and 	*/
				/* compare to the sought interface index. 							*/
				op_ima_obj_attr_get (stream_objid, "ip addr index", &assigned_iface_index);
				if (assigned_iface_index == iface_index)
					{
					/*	Based on the direction of the packet stream (incoming to IP	*/
					/*	module or outgoing from IP module), determine the stream 	*/
					/*	index.														*/
					if (assoc_dir == OPC_TOPO_ASSOC_IN)
						op_ima_obj_attr_get (stream_objid, "dest stream", in_stream_ptr);
					else
						op_ima_obj_attr_get (stream_objid, "src stream", out_stream_ptr);
					}
				}
			}	
		}

	FOUT	
	}


void
ip_interface_table_print (List *iface_table_ptr)
	{
    int						i;
	char					str0 [512];
	int						num_entries;
	IpT_Interface_Info *	ip_interface_info_ptr;
	char					addr_str [IPC_ADDR_STR_LEN];
	char					subnet_mask_str [IPC_ADDR_STR_LEN];

	/** This function is used to print out the contents of the ip interface table. **/
	FIN (ip_interface_table_print (iface_table_ptr));

	/* Print out the route table information only if the route table exists. */
	if (iface_table_ptr != OPC_NIL)
		{
		op_prg_odb_print_major ("IP Interface Table Contents\n", OPC_NIL);
		op_prg_odb_print_minor ("Interface\tAddress\t\tSubnet Mask\tOutput\tInput", OPC_NIL);
		op_prg_odb_print_minor ("---------\t-------\t\t-----------\t------\t-----\n", OPC_NIL);
	
		num_entries = op_prg_list_size (iface_table_ptr);

		for (i = 0; i < num_entries; i++)
			{
			ip_interface_info_ptr = (IpT_Interface_Info *) op_prg_list_access (iface_table_ptr, i);

			/* Get string representations of the address and subnet mask. */
			ip_address_print (addr_str, ip_interface_info_ptr->addr_range_ptr->address);
			ip_address_print (subnet_mask_str, ip_interface_info_ptr->addr_range_ptr->subnet_mask);
			
			sprintf (str0, "  [%d]\t\t%s\t%s\t  %d\t  %d\n",
				ip_interface_info_ptr->addr_index, addr_str, subnet_mask_str, 
				ip_interface_info_ptr->port_num, ip_interface_info_ptr->in_port_num);

			op_prg_odb_print_minor (str0, OPC_NIL);
			}
		}
	else
		{
		/* Print out an error message saying that the route table does not exist. */
		op_prg_odb_print_major ("IP Interface Table does not exist\n", OPC_NIL);
		}

    FOUT;
	}

void	
ip_rtab_init (void)
	{
	/** Create the underlying optimized address lookup structures **/	
	/** if this has not been already done.                        **/
    FIN (ip_rtab_init (void));

	nato_table_build_start ("ip_table", NATOC_ONE_COMPONENT_ADDR);

	FOUT;
	}

void
ip_rtab_local_addr_register (IpT_Address ip_addr)
	{
	NatoT_Table_Handle	ip_table_handle;
	int					status;
	int					int_ip_addr;

	/** Register local IP address within the network wide IP **/
	/** optimized address conversion table.                  **/
	FIN (ip_rtab_local_addrs_register (ip_addr));
	
	ip_table_handle = nato_table_handle_get ("ip_table");

	/* An error message is printed out if the table handle is OPC_NIL. */
	if (ip_table_handle == OPC_NIL)
		op_sim_end ("Error at function ip_rtab_local_addrs_register: global ip_table_handle is invalid",
			OPC_NIL, OPC_NIL, OPC_NIL);

	/* Convert IP address to a integer representation, so that */
	/* it may be stored in the NATO table.                     */
	int_ip_addr = ip_address_to_int (ip_addr);

	/* Store the integer representation of the IP address in the table. */
    status = nato_table_address_register (ip_table_handle, int_ip_addr);

    /* If the registration was not successful, return an error. */
	if (status == NATOC_TABLE_FORM_INVALID)
		op_sim_end ("Error at function ip_rtab_local_addrs_register: global ip table",
			"is in usage mode and no registrations can be performed", OPC_NIL, OPC_NIL);

	FOUT;
	}

void
ip_rtab_table_handle_register (void)
	{
	NatoT_Table_Handle		ip_table_handle;

	/** This function is used to register the global IP address table handle in the model-wide registry. **/
	FIN (ip_rtab_table_handle_register (void));

	/* First obtain the global IP table handle and then register it. */
	ip_table_handle = nato_table_handle_get ("ip_table");

	if (ip_table_handle == OPC_NIL)
		op_sim_end ("Error at function ip_rtab_table_handle_register: global ip_table_handle",
			"is invalid.", OPC_NIL, OPC_NIL);

	oms_pr_attr_set (own_process_record_handle, 
		"ip table handle", OMSC_PR_ADDRESS, ip_table_handle, 
		OPC_NIL);

	FOUT;
	}

int
ip_rtab_num_addrs_registered (void)
	{
	NatoT_Table_Handle	ip_table_handle;
	int					num_addrs_registered;

	/** Determine the number of addresses registered in the global address table. **/
	FIN (ip_rtab_num_addrs_registered (void));
	
	ip_table_handle = nato_table_handle_get ("ip_table");

	/* An error message is printed out if the table handle is OPC_NIL. */
	if (ip_table_handle == OPC_NIL)
		op_sim_end ("Error at function ip_rtab_num_addrs_registered: global ip_table_handle",
			"is invalid", OPC_NIL, OPC_NIL);

	num_addrs_registered = nato_table_size (ip_table_handle);

	FRET (num_addrs_registered);
	}

void
ip_rtab_print (void)
	{
	NatoT_Table_Handle	ip_table_handle;
	int					status;
	int					table_size, table_index;
	NatoT_Table_Entry *	table_entry_ptr;
	char				header_str [128], header2_str [128];
	char				table_entry_str [512], title_str [512], blank_str [512] = "";
	int					int_ip_addr, phys_addr;
	IpT_Address 		ip_addr;
	char				ip_addr_str [IPC_ADDR_STR_LEN];
	int					lower_layer_addr;
	int					lower_layer_index;
	Boolean				phys_addr_found;

	/** This function is used to print the global address table. **/
	FIN (ip_rtab_print (void));
	
	ip_table_handle = nato_table_handle_get ("ip_table");

	/* An error message is printed out if the table handle is OPC_NIL. */
	if (ip_table_handle == OPC_NIL)
		op_sim_end ("Error at function ip_rtab_print: global ip_table_handle is invalid", 
			OPC_NIL, OPC_NIL, OPC_NIL);

	/* Print the table header. */
	sprintf (title_str, "Global IP Address Mapping Table:");
	sprintf (header_str, "IP Address\t\tLower Layer Address");
	sprintf (header2_str, "----------\t\t-------------------");
	op_prg_odb_print_minor (blank_str, title_str, blank_str, header_str, header2_str, OPC_NIL);

	/* We do not use the standard nato table print function, so */
	/* can print out IP addresses in a readable form.           */
	table_size = nato_table_size (ip_table_handle);
	
	for (table_index = 0; table_index < table_size; table_index++)
		{
		/* Get the current table entry. */
		table_entry_ptr = nato_table_entry_get (ip_table_handle, table_index);

		/* Get the major address, which is the IP address. */
		int_ip_addr = nato_table_entry_major_addr_get (table_entry_ptr);

		/* Convert the integer representation to an internal representation. */
		ip_addr = ip_address_from_int (int_ip_addr);

		/* Get string representation of address. */
		ip_address_print (ip_addr_str, ip_addr);

		/* Destroy the IP address, since we no longer need it for this iteration. */
		ip_address_destroy (ip_addr);

		/* Loop through the lower layer addresses, and print out the */
		/* address that's defined.                                   */
		phys_addr_found = OPC_FALSE;
		for (lower_layer_index = 0; lower_layer_index < NATOC_NUM_LOWER_LAYER_ADDRS; lower_layer_index++)
			{
			if ((lower_layer_addr = nato_table_entry_phys_addr_get (table_entry_ptr, lower_layer_index)) !=
				NATOC_LOWER_LAYER_ADDR_UNDEFINED)
				{
				sprintf (table_entry_str, "(%s)\t\t%d (Type %d)",
					ip_addr_str, lower_layer_addr, lower_layer_index);
				phys_addr_found = OPC_TRUE;
				}

			if (!phys_addr_found)
				sprintf (table_entry_str, "(%s)\t\t(No physical address)",
					ip_addr_str);
			}

		/* Print out table entry. */
		op_prg_odb_print_minor (table_entry_str, OPC_NIL);
		}

	FOUT;
	}

Compcode
ip_dest_reached (IpT_Address dest_addr, List *iface_table_ptr, int ttl_index)
    {
	int						i, interface_list_size;
    IpT_Interface_Info *	iface_elem_ptr;

	/** This procedure is used to determine whether the IP datagram has reached its destination. **/
	FIN (ip_dest_reached (dest_addr, iface_table_ptr, ttl_index));

	/* Loop through all IP interfaces to see if the destination address matches anyone of them. */
	interface_list_size = op_prg_list_size (iface_table_ptr);

	for (i = 0; i < interface_list_size; i++)
		{
		iface_elem_ptr = (IpT_Interface_Info *) op_prg_list_access (iface_table_ptr, i);

		/* An IP datagram has reached its destination under the following conditions: */
		/*   1) The destination address matches the interface address.                */
		/*   2) The destination address is broadcast to the net the interface is      */
		/*      connected to, and the datagram was not originated by the local node.  */
		/*   3) The destination address is broadcast to all nets and nodes, and the   */
		/*      datagram was not originated by the local node.                        */
		/*   4) The destination address is multicast, and this IP module has been     */	
		/*      added to the multicast group, and the datagram was not originated     */
		/*      by the local node.   (reserved clause added by Anindya Neogi)         */

		/* This checks condition 1. */
		if (ip_address_equal (iface_elem_ptr->addr_range_ptr->address, dest_addr))
			FRET (OPC_COMPCODE_SUCCESS);

		/* This checks conditions 2 and 3. */
		if ((ip_address_is_broadcast (dest_addr, iface_elem_ptr->addr_range_ptr->subnet_mask)) &&
			(ttl_index != IP_DEFAULT_TTL))
			FRET (OPC_COMPCODE_SUCCESS);

		/* This checks condition 4. */
		if ((ip_address_is_multicast (dest_addr)) && 
            (address_is_mcast_reserved (dest_addr)) &&
			(ip_address_multicast_accept (dest_addr)) && 
			(ttl_index != IP_DEFAULT_TTL))
			FRET (OPC_COMPCODE_SUCCESS);
		}

	/* None of the three conditions matched for any of the interfaces, */
	/* so the datagram has not reached its destination.                */
	FRET (OPC_COMPCODE_FAILURE);
	}

void
ip_src_address_determine (IpT_Address dest_addr, IpT_Address *src_addr_ptr, List *iface_table_ptr)
	{
	int						i;
	IpT_Interface_Info *	iface_elem_ptr;
	char					error_string [512];
	char					dest_addr_str [IPC_ADDR_STR_LEN];
	int						iface_table_size;

	/** This procedure is used to determine the correct src address based **/
	/** on the next hop address.                                          **/
	FIN (ip_src_address_determine (dest_addr, src_addr_ptr, iface_table_ptr));

	/* Loop through the interfaces and see if the destination address falls within the */
	/* range of any of the interfaces.  If so, then return the address of the          */
	/* interface as the source address.                                                */
	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);

		/* Does the destination address exist within the range of */
		/* addresses the interface connects to?                   */
		if (ip_address_range_check (dest_addr, iface_elem_ptr->addr_range_ptr))
			{
			/* Found the proper interface, so fill in the source address. */
			*src_addr_ptr = ip_address_copy (iface_elem_ptr->addr_range_ptr->address);

			FOUT;
			}
		}

	/* An error message is signaled, as no matching source address can be */
	/* found for the destination address.                                 */
	ip_address_print (dest_addr_str, dest_addr);
	sprintf (error_string, "No valid source address for next hop (%s)", dest_addr_str);
	ip_rte_error (error_string);

	FOUT;
	}

int
ip_outstrm_and_mtu_determine (List *iface_table_ptr, IpT_Address next_hop_addr, int *mtu_size_ptr)
	{
	int						i;
	IpT_Interface_Info *	iface_elem_ptr;
	int						iface_table_size;

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

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

		/* 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))
		    {
			*mtu_size_ptr = iface_elem_ptr->mtu;

			FRET (iface_elem_ptr->port_num);
			}
		}

	/* If no output stream is found, return an error flag and signal an error message.	*/
	FRET (IP_OUTSTRM_INVALID);
	}
	
void
ip_rte_error (char *msg)
	{
	/** Print an error message and exit the simulation. **/
	FIN (ip_rte_error (msg));

	op_sim_end ("Error in IP routing process model (ip3_rte):",	msg, OPC_NIL, OPC_NIL);

	FOUT;
	}

void
ip_rte_warn (char *msg)
	{
	/** Print a warning message and resume. **/
	FIN (ip_rte_warn (msg));

	op_sim_message ("Warning from IP routing process model (ip3_rte):", msg);

	FOUT;
	}
	
Compcode
ip_rte_local_gateway_find (List *iface_table_ptr, IpT_Address *next_hop_addr_ptr)
	{
	int						iface_table_size, i;
	IpT_Interface_Info *	iface_ptr;
	IpT_Address				gateway_iface_address;
	char					address_string [IPC_ADDR_STR_LEN];
	char					mask_string [IPC_ADDR_STR_LEN];
	char					error_string [512];

	/* Used to locate a gateway that can be contacted by this node via any of its	*/
	/* interfaces. If such a gateway is found, then its interface address is 		*/
	/* returned. The returned address uses memory managed by the underlying IP 		*/
	/* packages, so it should not be deallocated by the client of this function.	*/
	FIN (ip_rte_local_gateway_find (iface_table_ptr, next_hop_addr_ptr));
	
	/* We are interested in finding any interface that can contact a gateway; try 	*/
	/* each of the interfaces in turn until one such interface is found. 			*/
    iface_table_size = op_prg_list_size (iface_table_ptr);
	for (i = 0; i < iface_table_size; i++)
		{
		/* Obtain the i_th interface in the interface table. */
		iface_ptr = (IpT_Interface_Info *) op_prg_list_access (iface_table_ptr, i);
		
		/* Look for any interface belonging to a gateway, provided that the interface	*/
		/* also belongs to the same subnet as the i_th interface in this table. 		*/
		gateway_iface_address = ip3_known_gateway_iface_any (iface_ptr->addr_range_ptr->address,
			iface_ptr->addr_range_ptr->subnet_mask);

		/* If an interface for a gateway has been located, stop searching. */
		if (gateway_iface_address != OPC_NIL)
			break;
		}

	/* An error should be printed out if there are no available gateway interfaces. */
	if (gateway_iface_address == OPC_NIL)
		{
		ip_address_print (address_string, iface_ptr->addr_range_ptr->address);
		ip_address_print (mask_string, iface_ptr->addr_range_ptr->subnet_mask);
		sprintf (error_string, "Unable to locate a gateway for ADDR (%s), MASK (%s)");
		ip_rte_error (error_string);
		*next_hop_addr_ptr = OPC_NIL;
		FRET (OPC_COMPCODE_FAILURE)
		}
	else
		{			
		*next_hop_addr_ptr = gateway_iface_address;			
		FRET (OPC_COMPCODE_SUCCESS);
		}
	}

Compcode
ip_rte_destination_local_network (List *iface_table_ptr, IpT_Address dest_addr)
    {
	int						i, iface_table_size;
    IpT_Interface_Info *	iface_elem_ptr;

	/** This procedure is used to determine whether the IP datagram is **/
	/** destined for a direct neighbor of this node.                   **/
	FIN (ip_rte_destination_local_network (iface_table_ptr, dest_addr));
	
	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);

		if (ip_address_range_check (dest_addr, iface_elem_ptr->addr_range_ptr))
			FRET (OPC_COMPCODE_SUCCESS);
		}

	FRET (OPC_COMPCODE_FAILURE);
	}


IpT_Interface_Info *
ip_interface_info_create (void)
	{
	IpT_Interface_Info *		iface_info_ptr;

	/** Create an interface information structure. **/
	FIN (ip_interface_info_create (void));

	/* Allocate memory for the structure. */
	iface_info_ptr = (IpT_Interface_Info *) op_prg_mem_alloc (sizeof (IpT_Interface_Info));

	/* Initialize fields of the structure. */
	iface_info_ptr->addr_index = 0;
	iface_info_ptr->addr_range_ptr = OPC_NIL;
	iface_info_ptr->mtu = 0;
	iface_info_ptr->port_num = 0;
	iface_info_ptr->in_port_num = 0;

	FRET (iface_info_ptr);
	}

void
ip_rte_next_hop_error (IpT_Address addr)
	{
	char			addr_str [IPC_ADDR_STR_LEN];
	char			err_str [512];

	/** Print out an error indicating an invalid next hop. **/
	FIN (ip_rte_next_hop_error (addr));

	ip_address_print (addr_str, addr);
	sprintf (err_str, "Unable to find interface for next hop (%s)", addr_str);
	ip_rte_error (err_str);

	FOUT;
	}

Compcode
ip_rte_datagram_dest_get (IpT_Address dest_addr, IpT_Address mcast_intf_addr, int ttl, int fast_address,
	IpT_Address *next_addr_ptr, int *outstrm_ptr, int *mtu_ptr, Boolean *broadcast_ptr, 
	Boolean *higher_layer_ptr, Boolean *destroy_pkt_ptr)
	{
	char					dest_addr_str [IPC_ADDR_STR_LEN];
	char					next_addr_str [IPC_ADDR_STR_LEN];
	char					interface_addr_str [IPC_ADDR_STR_LEN];
	char					error_str [512];
	IpT_Interface_Info *	interface_ptr;
	Compcode				route_status;
    char                    mcast_intf_addr_str [IPC_ADDR_STR_LEN];

	/** Retrieve and return the outstream and mtu to which we should send the **/
	/** datagram.  This function may also return status flags indicating that **/
	/** the datagram should be broadcast, and/or the datagram should be       **/
	/** forwarded to the higher layer.  If OPC_COMPCODE_SUCCESS is returned,  **/
	/** the datagram can be dispatched by the client of this procedure; if    **/
	/** OPC_COMPCODE_FAILURE is returned, a routing message error should be   **/
	/** generated, and the datagram should be destroyed.                      **/
	FIN (ip_rte_datagram_dest_get (dest_addr, mcast_intf_addr, ttl, fast_address, *next_addr_ptr, 
		*outstrm_ptr, *mtu_ptr, *broadcast_ptr, *higher_layer_ptr, *destroy_pkt_ptr));

    
	/* By default the packet should not be destroyed. */
	*destroy_pkt_ptr = OPC_FALSE;

	/* Check first to see if datagram has reached its destination. */
	if (ip_dest_reached (dest_addr, interface_table_ptr, ttl) == OPC_COMPCODE_SUCCESS)
		{
		/* The destination address matches one of the IP addresses */
		/* on this node, and therefore has reached its final       */
		/* destination.  The datagram should be forwarded to the   */
		/* higher layer.                                           */
		*broadcast_ptr = OPC_FALSE;
		*higher_layer_ptr = OPC_TRUE;

		FRET (OPC_COMPCODE_SUCCESS);
		}

	/* Begin Suggested by Paul Stirpe, tried cjin */
	/*if ( ip_address_equal(dest_addr, ip_address_create("239.255.0.1") ) ) {
		*higher_layer_ptr = OPC_TRUE;
	} */
	/* End Suggested by Paul Stirpe, tried cjin */

	/* Next thing to check is to see if this is a broadcast address. */
	if (ip_address_equal (dest_addr, IpI_Broadcast_Addr))
		{
		/* The destination address indicates a 'limited broadcast', meaning that */	
		/* the datagram should be forwarded on all interfaces to the next IP     */
		/* hop, but no further.  At this point, we know that the current IP node */
		/* originated the datagram (otherwise, it would have reached its         */
		/* ultimate destination).                                                */
		*broadcast_ptr = OPC_TRUE;
		*higher_layer_ptr = OPC_FALSE;

		FRET (OPC_COMPCODE_SUCCESS);
		}

	/* The destination is neither a broadcast nor matches an IP interface on this node. */
	*broadcast_ptr = OPC_FALSE;
	*higher_layer_ptr = OPC_FALSE;

	/* Final thing to check is to see if this is a multicast address. */
	/* If so, then the interface on which to send the datagram has    */
	/* already been specified by the higher layer.                    */

    /* comment changed to add the last cluse by Anindya Neogi */
	/* Final thing to check is to see if this is a multicast address. */
	/* If so, then the interface on which to send the datagram has    */
	/* already been specified by the higher layer or has been looked up in the */
    /* routing tables                                                 */

    /* BEGIN_JOE_SUN */
	/* mcast_intf_addr != OPC_NIL clause added by JOE_SUN */
	/* This clause corresponds to the case when mcast_intf_addr is set before */
	/* we reach here */
	/* END_JOE_SUN */
	if (ip_address_is_multicast (dest_addr) || mcast_intf_addr!=OPC_NIL) /* JOE_SUN */
		{
        if ((ttl != IP_DEFAULT_TTL) && (dest_addr[0] != 239)) {/*Xin Wang added the second condition*/ 
			/* The multicast packet did not originate from this node */
			/* and the node was not configured to accept it.  The    */
			/* packet should be destroyed.                           */
			*destroy_pkt_ptr = OPC_TRUE;			

			FRET (OPC_COMPCODE_SUCCESS);
		}
		
		/* To forward igmp report from a hsot added by cjin */
       if ( ip_address_equal(dest_addr, ip_address_create("224.0.0.2") ) ) {/*cjin added*/ 
			*broadcast_ptr = OPC_TRUE;
			*higher_layer_ptr = OPC_FALSE;
			*destroy_pkt_ptr = OPC_FALSE;			

			FRET (OPC_COMPCODE_SUCCESS);
		}


		/* Determine the parameters for the next hop. */

       
		*outstrm_ptr = ip_outstrm_and_mtu_determine (interface_table_ptr, mcast_intf_addr,
			mtu_ptr);
	
		/* Is the stream valid? */
		if (*outstrm_ptr == IP_OUTSTRM_INVALID)
			ip_rte_next_hop_error (mcast_intf_addr);

		/* Copy the specified interface to the next hop. */
		*next_addr_ptr = ip_address_copy (mcast_intf_addr);

        /* added by Anindya Neogi */
        if(!address_is_mcast_reserved (dest_addr)) FRET (OPC_COMPCODE_SUCCESS);

		/* We have enough information for the client to dispatch to the next hop. */
		FRET (OPC_COMPCODE_SUCCESS);
		}

	/* Check first to see if the destination is local. */
	if (ip_rte_destination_local_network (interface_table_ptr, dest_addr) == OPC_COMPCODE_SUCCESS)
		{
		/* If the destination is local, then we simply look at the interfaces       */
		/* and return the outstrm and mtu corresponding to the destination address. */
		*outstrm_ptr = ip_outstrm_and_mtu_determine (interface_table_ptr, dest_addr,
			mtu_ptr);

		/* If we got an invalid outstrm, then give an error message. */
		if (*outstrm_ptr == IP_OUTSTRM_INVALID)
			ip_rte_next_hop_error (dest_addr);

		*next_addr_ptr = ip_address_copy (dest_addr);

		/* We have enough information for the client to dispatch to the next hop. */
		FRET (OPC_COMPCODE_SUCCESS);
		}

	/* The node does not know how to route this datagram; forward it to a:		*/
	/*   1) Deafult Gateway, if specified using the "default gateway" process	*/
	/*      model attribute, or													*/
	/*	 2) Local Gateway, which can be determined if not known.				*/
	/* The default gateway attribute can be used both by the end stations as	*/
	/* well as gateways to forward the datagram to the next hop, whereas, a 	*/
	/* local gateway is used only by the end stations. Determine if this node	*/
	/* functions like a gateway.												*/
	if (gateway_status == OPC_FALSE)
		{
		/* This node is not a gateway. Determine if a next hop gateway is		*/
		/* already known or not.												*/
		if (gateway_known == OPC_FALSE)
			{
			/* The gateway is not yet known. Determine if a defaut gateway has	*/
			/* been specified. If specified, use this as the next hop; else		*/
			/* find a local gateway to which the datagram should be forwarded	*/
			/* as its next hop.													*/
			if (default_gtwy_available == OPC_TRUE)
				{
				/* A defaut gateway has been specified. Update the state		*/
				/* information to reflect this									*/
				next_addr_gateway = ip_address_copy (default_gtwy_addr); 
				gateway_known = OPC_TRUE;

				/* Deallocate memory allocated for the original variable used	*/
				/* to store the "default gateway" address.  For subsequent		*/
				/* access, "next_addr_gateway" will be used as the next hop.	*/
				ip_address_destroy (default_gtwy_addr);
				}
			else
				{
				/* A default gateway is not available and we need to forward	*/
				/* the datagram to a gateway, but the gateway is not yet known.	*/
				/* Determine the local gateway.                 				*/
				if (ip_rte_local_gateway_find (interface_table_ptr, next_addr_ptr) == OPC_COMPCODE_SUCCESS)
					{
					/* Found the local gateway. Update the state information	*/
					/* to reflect this. 										*/
					next_addr_gateway = ip_address_copy (*next_addr_ptr);
					gateway_known = OPC_TRUE;
					}
				else
					{
					/* If we can't find a gateway, then report an error.		*/
					ip_address_print (dest_addr_str, dest_addr);
				
					/* Get IP address for an interface on this node.			*/
					interface_ptr = (IpT_Interface_Info *) op_prg_list_access (interface_table_ptr, 0);
					ip_address_print (interface_addr_str, interface_ptr->addr_range_ptr->address);

					/* Construct error string.									*/
					sprintf (error_str, "Unable to find local gateway for (%s) while routing datagram destined for (%s)",
						interface_addr_str, dest_addr_str);
					ip_rte_error (error_str);
					}
				}
			}

		/* Determine interface parameters for next hop. */
		*outstrm_ptr = ip_outstrm_and_mtu_determine (interface_table_ptr, next_addr_gateway, mtu_ptr);

		/* Is the stream valid?							*/
		if (*outstrm_ptr == IP_OUTSTRM_INVALID)
			ip_rte_next_hop_error (next_addr_gateway);

		/* Copy the gateway address to the next hop.	*/
		*next_addr_ptr = ip_address_copy (next_addr_gateway);

		/* We have enough information for the client to dispatch to the next hop. */
		FRET (OPC_COMPCODE_SUCCESS);
		}
	else
		{
		/* The IP node is a gateway, and the final destination is not on a		*/
		/* network the gateway connects to.  At this point, we need to find 	*/
		/* a next hop. This can be found using:									*/
		/*  1) Performing a routing table lookup, or 							*/
		/*  2) Using the "default gateway" specification, if the routing 		*/
		/*	   table lookup fails.     											*/

		/* Invoke routing table lookup.											*/
		route_status = (*ip_rte_select_func_ptr) (fast_address, ip_dyn_route_table_ptr,
			dest_addr, next_addr_ptr);

		/* If the routing table lookup fails, look for a default gateway.		*/
		if (route_status == OPC_COMPCODE_FAILURE)
			{
			if (default_gtwy_available == OPC_TRUE)
				{
				/* A default gateway has been specified.						*/
				*next_addr_ptr = ip_address_copy (default_gtwy_addr);

				/* Indicate that a valid next hop route is available.			*/
				route_status = OPC_COMPCODE_SUCCESS;
				}
			else
				{
				/* A route to the destination address cannot be found. Report	*/
				/* an error. Construct error string.							*/
				ip_address_print (dest_addr_str, dest_addr);
				sprintf (error_str, "Unable to find a gateway while routing datagram destined for (%s)", dest_addr_str);
				ip_rte_error (error_str);
				}
			}

		/* We have a valid route (and a valid next hop). Next find the		*/
		/* interface this hop corresponds to.								*/
		*outstrm_ptr = ip_outstrm_and_mtu_determine (interface_table_ptr, *next_addr_ptr, mtu_ptr);
		
		/* Is this a valid outstream?										*/
		if (*outstrm_ptr == IP_OUTSTRM_INVALID)
			ip_rte_next_hop_error (*next_addr_ptr);
		}

	FRET (route_status);
	}


void
ip_rte_datagram_broadcast (IpT_Address dest_addr, Packet *pk_ptr)
	{
	int						num_interfaces, interface_index;
	IpT_Interface_Info *	iface_ptr;
	Packet *				pk_copy_ptr;
	
	/** Broadcast a datagram on all applicable interfaces. **/
	FIN (ip_rte_datagram_broadcast (dest_addr, pk_ptr));

	num_interfaces = op_prg_list_size (interface_table_ptr);

	/* Loop through all interfaces, and send a copy of the packet on each applicable interface. */
	for (interface_index = 0; interface_index < num_interfaces; interface_index++)
		{
		iface_ptr = (IpT_Interface_Info *) op_prg_list_access (interface_table_ptr,
			interface_index);

		if (interface_index < (num_interfaces - 1))
			pk_copy_ptr = op_pk_copy (pk_ptr);
		else
			pk_copy_ptr = pk_ptr;

		/* Set the source address before forwarding the packet. */
		op_pk_nfd_set (pk_copy_ptr, "src_addr", ip_address_copy (iface_ptr->addr_range_ptr->address),
			ip_address_copy_create, ip_address_destroy, IPC_ADDR_SIZE * sizeof (unsigned char));
		op_pk_nfd_set (pk_copy_ptr, "src_internal_addr", 
			ip_rtab_addr_convert (iface_ptr->addr_range_ptr->address));

		ip_rte_datagram_interface_forward (dest_addr, dest_addr, iface_ptr->port_num,
			iface_ptr->mtu, pk_copy_ptr);
		}

	FOUT;
	}

void
ip_rte_datagram_higher_layer_forward (Packet *pk_ptr, IpT_Address dest_addr)
	{
	Packet *		ip_pkptr;
	char			str0 [512];
	char			dest_addr_str [IPC_ADDR_STR_LEN];
	Ici *			intf_ici_ptr;
    char *                  format_name [256];
	/** Forward a datagram to the higher layer. **/
	FIN (ip_rte_datagram_higher_layer_forward (pk_ptr, dest_addr));

	/* Call the fragment reassembly procedure to determine if    */
	/* a complete packet has arrived.  Note that the packet      */
	/* will be destroyed in this procedure call unless it is     */
	/* itself the complete datagram (i.e., it isn't a fragment). */

    /* Xin Wang MEM*/
    /*num_higher_layer_forward ++;
    printf ("%s num_higher_layer_forward  = %d\n",   router_name, num_higher_layer_forward);
*/
     /* Added by Xin Wang for sending a mcast data packet copy to higher layer */                         
    if (ip_address_is_multicast (dest_addr) && !address_is_mcast_reserved (dest_addr)) { 
		/* Set the destination address before sending the packet. */
        ip_pkptr = pk_ptr;
		op_pk_nfd_set (pk_ptr, "dest_addr", dest_addr, op_prg_mem_copy_create,
			op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
        if (mcast_routing_protocol_used == IpC_Rte_PimDM)
           op_pk_nfd_set (pk_ptr, "protocol", "pim_dm");
        else if (mcast_routing_protocol_used == IpC_Rte_PimSM)
           op_pk_nfd_set (pk_ptr, "protocol", "pim_sm");
		/* Check the 'interface received' ICI.  If the ICI isn't set in the packet,  */
		/* set it with IP default address (0.0.0.0) as the interface received.  This */
		/* corresponds to the situation where the higher layer sends a packet to     */
		/* itself (except in the case of broadcast).                                 */
		if (op_pk_ici_get (ip_pkptr) == OPC_NIL)
			{
			intf_ici_ptr = op_ici_create ("ip3_rte_ind");
			op_ici_attr_set (intf_ici_ptr, "interface_received",
				ip_address_copy (IpI_Default_Addr));
			op_pk_ici_set (ip_pkptr, intf_ici_ptr);

			/* The source address is unlikely to be set, simply set to the destination address. */
			if (!op_pk_nfd_is_set (ip_pkptr, "src_addr"))
				op_pk_nfd_set (ip_pkptr, "src_addr", ip_address_copy (dest_addr), op_prg_mem_copy_create,
					op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char));
			}
		
		/* Forward the complete packet to the higher layer. */  
		op_pk_send (ip_pkptr, outstrm_to_ip_encap);
		
    } /* ended Xin Wang */
	else if ((ip_pkptr = ip_frag_sup_insert (dgram_list_ptr, pk_ptr)) != OPC_NIL)
		{
		/* In debug mode, issue a trace statement. */
		if (op_prg_odb_ltrace_active ("ip_frag"))
			{
			ip_address_print (dest_addr_str, dest_addr);
			sprintf (str0, "Complete Datagram Received at (%s)", dest_addr_str);
			op_prg_odb_print_major (str0, OPC_NIL);
			}

		/* Set the destination address before sending the packet. */
		/* Only set the destination address if the field is not set. */
		if (op_pk_nfd_is_set (ip_pkptr, "dest_addr") == OPC_FALSE)
		op_pk_nfd_set (ip_pkptr, "dest_addr", dest_addr, ip_address_copy_create,
			ip_address_destroy, IPC_ADDR_SIZE * sizeof (unsigned char));

		/* Check the 'interface received' ICI.  If the ICI isn't set in the packet,  */
		/* set it with IP default address (0.0.0.0) as the interface received.  This */
		/* corresponds to the situation where the higher layer sends a packet to     */
		/* itself (except in the case of broadcast).                                 */
		if (op_pk_ici_get (ip_pkptr) == OPC_NIL)
			{
			intf_ici_ptr = op_ici_create ("ip3_rte_ind");
			op_ici_attr_set (intf_ici_ptr, "interface_received", ip_address_copy (IpI_Default_Addr));
			op_pk_ici_set (ip_pkptr, intf_ici_ptr);

			/* The source address is unlikely to be set, simply set to the destination address. */
			if (!op_pk_nfd_is_set (ip_pkptr, "src_addr"))
				op_pk_nfd_set (ip_pkptr, "src_addr", ip_address_copy (dest_addr), ip_address_copy_create,
					ip_address_destroy, IPC_ADDR_SIZE * sizeof (unsigned char));
			}
		
		/* Forward the complete packet to the higher layer. */
		op_pk_send (ip_pkptr, outstrm_to_ip_encap);
		}

	FOUT;
	}

void
ip_rte_datagram_interface_forward (IpT_Address dest_addr, IpT_Address next_addr, 
	int outstrm, int mtu, Packet *pk_ptr)
	{
	int					ttl;
	char				str0 [512], str1 [512];
	char				dest_addr_str [IPC_ADDR_STR_LEN];
	char				next_addr_str [IPC_ADDR_STR_LEN];
	int					conn_class;
	IpT_Address			src_addr;
	IpT_Address			intf_addr;
	int 				ip_fast_addr;
	Ici*				intf_ici_ptr;


	/** Forward a datagram to a particular IP interface (specified by a stream and MTU). **/
	FIN (ip_rte_datagram_interface_forward (dest_addr, next_addr, outstrm, mtu, pk_ptr));

	/* Create string references to the destination and next addresses. */
	ip_address_print (dest_addr_str, dest_addr);
	ip_address_print (next_addr_str, next_addr);

	/* Decrement packet's time-to-live field.  If this becomes 0, discard the packet. */
	if (op_pk_nfd_get (pk_ptr, "ttl", &ttl) == OPC_COMPCODE_FAILURE)
		ip_rte_error ("Unable to get TTL from packet.");

	/* Check the higher layer indication ICI.  If set, then remove it, since we	*/
	/* are not forwarding the packet to the higher layer. Also, deallocate the	*/
	/* contents of the ICI (interface address on which the dgram was received.)	*/
	intf_ici_ptr = op_pk_ici_get (pk_ptr);
    if (intf_ici_ptr != OPC_NIL)
		{
		op_ici_attr_get (intf_ici_ptr, "interface_received", &intf_addr);
		ip_address_destroy (intf_addr);
		op_ici_destroy (intf_ici_ptr);
		op_pk_ici_set (pk_ptr, OPC_NIL);
        /* Added Xin Wang */ 
        num_ind_destroy ++; 
		}
    /* Added Xin Wang */ 
    /*printf ("%s interface num_ind_destroy = %d\n", router_name, num_ind_destroy);  
  */
	ttl--;
	if (ttl == 0)
		{
		/* In debug mode, indicate that a packet is destroyed due to expired ttl. */
		if (op_prg_odb_ltrace_active ("ip_errs"))
			{
			sprintf (str0, "Discarding packet (%d) with expired TTL", op_pk_id (pk_ptr));
			sprintf (str1, "Destination: (%s)", dest_addr_str);
			op_prg_odb_print_major (str0, str1, OPC_NIL);
			}

		op_pk_destroy (pk_ptr);
		FOUT;
		}

	if (op_pk_nfd_set (pk_ptr, "ttl", ttl) == OPC_COMPCODE_FAILURE)
		ip_rte_error ("Unable to set TTL in packet.");

	/*	In debug mode, trace the routing action.					*/
	if (op_prg_odb_ltrace_active ("ip_rte"))
		{
		sprintf (str0, "Routing packet ID (%d) towards (%s)", op_pk_id (pk_ptr), dest_addr_str);
		sprintf (str1, "Next hop (%s), output stream (%d)", next_addr_str, outstrm);
		op_prg_odb_print_major (str0, str1, OPC_NIL);
		}

	/* For multicast addresses, the next address should be the actual */
	/* destination, not the next hop.  The reason for this is we want */
	/* ARP to treat this packet as a one-hop broadcast, so as to find */
	/* all IP hosts on the network that are members of the multicast  */
	/* group indicated by the address.                                */
	if (ip_address_is_multicast (dest_addr))
		{
		/* Install an ICI indicating to the lower layer what the next IP hop is. */
		/* Following if statement is modified by cjin, suggested by Stirpe to route mcast data packet to LAN */
		/* if (op_ici_attr_set (arp_iciptr, "next_addr", dest_addr) == OPC_COMPCODE_FAILURE) */
		if (op_ici_attr_set (arp_iciptr, "next_addr", IpI_Broadcast_Addr) == OPC_COMPCODE_FAILURE)
			ip_rte_error ("Unable to set next hop address in ICI.");
		}
	else
		{
		/* Install an ICI indicating to the lower layer what the next IP hop is. */
		if (op_ici_attr_set (arp_iciptr, "next_addr", next_addr) == OPC_COMPCODE_FAILURE)
			ip_rte_error ("Unable to set next hop address in ICI.");
		}

	/* Obtain the connection class from the packet. */
	op_pk_nfd_get (pk_ptr, "connection_class", &conn_class);

	/* Set the corresponding connection class in the ICI. */
	if (op_ici_attr_set (arp_iciptr, "connection_class", conn_class) 
		== OPC_COMPCODE_FAILURE)
		ip_rte_error ("Unable to set connection class in ICI.");

	op_ici_install (arp_iciptr);

	/* If the source address is not set, set it here. */
	if (!op_pk_nfd_is_set (pk_ptr, "src_addr"))
		{
		ip_src_address_determine (next_addr, &src_addr, interface_table_ptr);
		ip_fast_addr = ip_rtab_addr_convert (src_addr);

		if ((op_pk_nfd_set (pk_ptr, "src_internal_addr", ip_fast_addr) == OPC_COMPCODE_FAILURE) ||
			(op_pk_nfd_set (pk_ptr, "src_addr", src_addr, ip_address_copy_create,
				ip_address_destroy, IPC_ADDR_SIZE * sizeof (unsigned char)) == OPC_COMPCODE_FAILURE))
			ip_rte_error ("Unable to set source addresses in packet.");
		}

	/* Set the destination address before sending the packet. */
	op_pk_nfd_set (pk_ptr, "dest_addr", ip_address_copy (dest_addr), ip_address_copy_create, 
		ip_address_destroy, IPC_ADDR_SIZE * sizeof (unsigned char));

	/* Then send the IP packet in fragments. */
	ip_rte_pk_frag_send (pk_ptr, outstrm, mtu);

	FOUT;
	}

void
ip_rte_pk_frag_send (Packet *pk_ptr, int outstrm, int mtu)
	{
	int				frag_size, data_size, header_size, len;
	int				num_frags, frag_index, frag_accum;
	char			str0 [512];
	Packet *		ip_pkptr;
	Packet *		data_pkptr;
	Packet *		frag_ptr;
	int				frag;

	/** Fragment the IP packet and send the fragments over the given stream. **/
	FIN (ip_rte_pk_frag_send (pk_ptr, outstrm, mtu));

	/* Obtain the size in bytes of the packet. */
	frag_size = op_pk_total_size_get (pk_ptr) / 8;

	/* Obtain the number of bytes of data carried in this fragment. */
	if (op_pk_nfd_get (pk_ptr, "frag_len", &data_size) == OPC_COMPCODE_FAILURE)
		ip_rte_error ("Unable to get fragment size from packet.");

	/* Also obtain the difference between the packet size and */
	/* the length field; this is the size of the header.      */
	header_size = frag_size - data_size;

	/* If the fragment size is smaller than the MTU, send as is. */
	if (frag_size <= mtu)
		{
		op_pk_send_forced (pk_ptr, outstrm);
		FOUT;
		}

	/* The size is greater than the MTU, so break packet up into fragments. */
	num_frags = (data_size + mtu - header_size - 1) / (mtu - header_size);

	/* In debug mode indicate the fragmentation. */
	if (op_prg_odb_ltrace_active ("ip_frag"))
		{
		sprintf (str0, "Breaking datagram into (%d) fragments", num_frags);
		op_prg_odb_print_major (str0, OPC_NIL);
		}

	/* If the fragment is carrying the original datagram given to IP,  */
	/* extract it before copies are made.  Only one fragment can carry */
	/* the original packet for the reassembly model to work properly.  */
	if (op_pk_nfd_is_set (pk_ptr, "ip_dgram"))
		{
		if (op_pk_nfd_get (pk_ptr, "ip_dgram", &ip_pkptr) == OPC_COMPCODE_FAILURE)
			ip_rte_error ("Unable to get datagram from packet.");
		}
	else
		ip_pkptr = OPC_NIL;

	/* If the packet is carrying any encapsulated data (normally this */
	/* would happen only for a packet fragmented for the first time), */
	/* extract this data packet so that it will not appear in each    */
	/* fragment generated by copying.                                 */
	if (op_pk_nfd_is_set (pk_ptr, "data"))
		{
		if (op_pk_nfd_get (pk_ptr, "data", &data_pkptr) == OPC_COMPCODE_FAILURE)
			ip_rte_error ("Unable to get data from packet.");
		}
	else
		data_pkptr = OPC_NIL;

	for (frag_accum = 0, frag_index = 0; frag_index < num_frags; frag_index++)
		{
		/* Make a copy of the original packet. */
		frag_ptr = op_pk_copy (pk_ptr);
		if (frag_ptr == OPC_NIL)
			ip_rte_error ("Unable to copy packet to be fragmented.");

		/* Indicate that the copy is a fragment. */
		if (op_pk_nfd_set (frag_ptr, "frag", 1) == OPC_COMPCODE_FAILURE)	
			ip_rte_error ("Unable to set fragment field in packet.");

		/* For all but the last fragment, the size is the MTU */
		/* and the encapsulated IP packet is not included.    */
		if (frag_index < num_frags - 1)
			{
			if ((op_pk_nfd_set (frag_ptr, "frag_len", mtu - header_size) == OPC_COMPCODE_FAILURE) ||
				(op_pk_total_size_set (frag_ptr, 8 * mtu) == OPC_COMPCODE_FAILURE))
				ip_rte_error ("Unable to set size of fragment.");
			
			frag_accum += (mtu - header_size);
			}
		else
			{
			/* For the last fragment, set the fragment size to be */
			/* whatever packet size remains plus the header size. */
			len = data_size - frag_accum;
			if ((op_pk_nfd_set (frag_ptr, "frag_len", len) == OPC_COMPCODE_FAILURE) ||
				(op_pk_total_size_set (frag_ptr, 8 * (header_size + len)) == OPC_COMPCODE_FAILURE))
				ip_rte_error ("Unable to set size of final fragment.");

			/* If the original packet was not a fragment, encapsulate it */
			/* into the last fragment created here.                      */
			if (op_pk_nfd_get (pk_ptr, "frag", &frag) == OPC_COMPCODE_FAILURE)
				ip_rte_error ("Unable to get fragment field from packet.");

			if (!frag)
				{
				/* If the packet contained encapsulated data (i.e., from the */
				/* transport), that data will have been removed to avoid its */
				/* duplication in the fragments.  The data should now be     */
				/* reinserted into the original packet.                      */
				if (data_pkptr != OPC_NIL)
					{
					if (op_pk_nfd_set (pk_ptr, "data", data_pkptr) == OPC_COMPCODE_FAILURE)
						ip_rte_error ("Unable to set data in final fragment.");
					}

				/* In either case the original packet is encapsualted in the fragment. */	
				if (op_pk_nfd_set (frag_ptr, "ip_dgram", pk_ptr) == OPC_COMPCODE_FAILURE)
					ip_rte_error ("Unable to set datagram in final fragment.");
				}
			else
				{
				/* Otherwise the packet can be discarded. */
				op_pk_destroy (pk_ptr);

				/* Also, if the packet included the original datagram   */
				/* from which it was generated, transfer that datagram  */
				/* into the last fragment created here.  Note that it   */
				/* is possible, in the case where a fragment is itself  */
				/* being fragmented, that none of the created fragments */
				/* will contain the original datagram.                  */
				if (ip_pkptr != OPC_NIL)
					if (op_pk_nfd_set (frag_ptr, "ip_dgram", ip_pkptr) == OPC_COMPCODE_FAILURE)
						ip_rte_error ("Unable to set datagram in packet.");
				}
			}
		
		/* Forward the datagram fragment. */
		op_pk_send_forced (frag_ptr, outstrm);
		}

	FOUT;
	}

Boolean
ip_address_multicast_accept (IpT_Address addr)
	{
	int				num_addrs, addr_index;	
	IpT_Address		cur_addr;

	/** Returns OPC_TRUE if this IP module is configured to accept the given address. **/
	FIN (ip_adddress_multicast_accept (addr));

	/* Loop through the accepted multicast addresses and see if the addresses matches one of those. */
	num_addrs = op_prg_list_size (mcast_addr_list_ptr);

	for (addr_index = 0; addr_index < num_addrs; addr_index++)
		{
		cur_addr = (IpT_Address) op_prg_list_access (mcast_addr_list_ptr, addr_index);
		
		if (ip_address_equal (addr, cur_addr))
			FRET (OPC_TRUE);
		}

	FRET (OPC_FALSE);
	}

Boolean
ip_interface_table_verify (List *intf_list_ptr)
	{
	int						num_intfs, intf_index, check_index;
	IpT_Address				best_subnet_mask, check_net_addr, net_addr;
	IpT_Interface_Info *	intf_ptr;
	IpT_Interface_Info *	check_intf_ptr;
	IpT_Address_Range *		addr_range_ptr;

	/** Return OPC_TRUE if the list of interfaces given does not overlap **/
	/** address ranges.  Return OPC_FALSE if two or more interfaces      **/
	/** contain overlapping address ranges.                              **/
	FIN (ip_interface_table_verify (intf_list_ptr));

	/* Loop through all interfaces and check for address range conflicts. */
	num_intfs = op_prg_list_size (intf_list_ptr);

	for (intf_index = 0; intf_index < num_intfs; intf_index++)
		{
		intf_ptr = op_prg_list_access (intf_list_ptr, intf_index);
		addr_range_ptr = intf_ptr->addr_range_ptr;

		/* If this is the first interface, don't check against the others. */
		if (intf_index == 0)
			{
			best_subnet_mask = addr_range_ptr->subnet_mask;
			continue;
			}

		/* Is the subnet mask on this interface more general than the best so far? */
		if (ip_address_to_int (addr_range_ptr->subnet_mask) < 
			ip_address_to_int (best_subnet_mask))
			best_subnet_mask = addr_range_ptr->subnet_mask;

		/* Construct an address to check against other ranges. */
		net_addr = ip_address_mask (addr_range_ptr->address, best_subnet_mask);

		/* Check this interface's address with others previously seen. */
		for (check_index = 0; check_index < intf_index; check_index++)
			{
			check_intf_ptr = op_prg_list_access (intf_list_ptr, check_index);
			check_net_addr = ip_address_mask (check_intf_ptr->addr_range_ptr->address,
				best_subnet_mask);

			if (ip_address_equal (net_addr, check_net_addr))
				{
				/* We've detected an address overlap.  Return OPC_FALSE. */
				FRET (OPC_FALSE);
				}

			/* Destroy the address so it can be re-used. */
			ip_address_destroy (check_net_addr);
			}

		/* Destroy the network address so it can be re-used. */
		ip_address_destroy (net_addr);
		}

	/* If we get here, no conflicts have been detected. */
	FRET (OPC_TRUE);
	}


/* Added by Xin Wang */
int 
address_is_mcast_reserved(IpT_Address addr)
	{
	char			addr_str [IPC_ADDR_STR_LEN];
    char			mcast_addr_str [IPC_ADDR_STR_LEN];
	char			err_msg [512];
	int				num_addrs, addr_index;
	IpT_Address		mcast_addr;

	/** Register a node to accept the given multicast address. **/
	FIN (address_is_mcast_register (addr));
    
    

	/* Loop through the list to make sure the address is not already registered. */
   
    if (mcast_addr_list_ptr == OPC_NIL) {
        printf ("mcast_addr_list is empty\n");
        FRET (0)
    }
	num_addrs = op_prg_list_size (mcast_addr_list_ptr);

	for (addr_index = 0; addr_index < num_addrs; addr_index++)
		{
		mcast_addr = op_prg_list_access (mcast_addr_list_ptr, addr_index);
        mcast_addr = op_prg_list_access (mcast_addr_list_ptr, addr_index);
        ip_address_print (mcast_addr_str, mcast_addr);
        //printf ("mcast addr = %s \n", mcast_addr_str);
		if (ip_address_equal (mcast_addr, addr))
			{
			/* This address is a reserved address */
			FRET (1);
			}
		}

	FRET (0);
	}	

/* This function is added by cjin, determine forward a data packet or not based on igmp table */
/* if the group is on the interface,reutrn 1 */
/* otherwise, return 0 */
int Mlocal_look_up( IpT_Address interface_addr, IpT_Address grp_ip )
{
int interface_index, i, group_index, j;
IGMP_Interface * igmp_interface_ptr;
grp_tab_node * igmp_group_ptr;

FIN(Mlocal_look_up( IpT_Address interface_addr, IpT_Address grp_ip ));

interface_index = op_prg_list_size(Mlocal_table_ptr);

for (i = 0; i < interface_index; i++) {
	igmp_interface_ptr = (IGMP_Interface *) op_prg_list_access (Mlocal_table_ptr, i);
	if ( ip_address_equal( igmp_interface_ptr->interface_address, interface_addr ) && 
		igmp_interface_ptr->querier_flag ) {

		group_index = op_prg_list_size(igmp_interface_ptr->grp_tab);
		for ( j = 0; j < group_index; j++) {
			igmp_group_ptr = (grp_tab_node *) op_prg_list_access (igmp_interface_ptr->grp_tab, j);
			if ( ip_address_equal( igmp_group_ptr->grp_ip, grp_ip) ) {
				FRET(1);
			}
		}
	} 
}

FRET(0);
}
