/* Process model C form file: A1_ip3m_encap.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_encap.pr.h"
FSM_EXT_DECS



/* Header block */
#include "ip3_addr.h"
#include "oms_pr.h"
#include "oms_tan.h"
#include <string.h>

#define IP_ENCAP_INSTRM_NETWORK			0
#define IP_ENCAP_OUTSTRM_NETWORK		0

#define FROM_TRANSPORT		op_intrpt_strm () != IP_ENCAP_INSTRM_NETWORK
#define FROM_NETWORK		op_intrpt_strm () == IP_ENCAP_INSTRM_NETWORK

/* Typedefs */
typedef struct
	{
	char*	protocol;
	int		outstream;
	int		instream;
	}IpT_Encap_Interface;

/* Functions */
char *	ip_encap_proto_get();
void	ip_encap_error ();

#define IP_ENCAPC_PROTOCOL_INVALID 	OPC_NIL
#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_dgram_id;
	Ici*	                   		sv_transp_iciptr;
	IpT_Encap_Interface**	  		sv_interface_table;
	Objid	                  		sv_own_id;
	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_record_handle_list_size;
	IpT_Address	            		sv_mcast_intf_addr;
	char                    		sv_router_name[30];
	} A1_ip3m_encap_state;

#define pr_state_ptr            		((A1_ip3m_encap_state*) SimI_Mod_State_Ptr)
#define dgram_id                		pr_state_ptr->sv_dgram_id
#define transp_iciptr           		pr_state_ptr->sv_transp_iciptr
#define interface_table         		pr_state_ptr->sv_interface_table
#define own_id                  		pr_state_ptr->sv_own_id
#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 record_handle_list_size 		pr_state_ptr->sv_record_handle_list_size
#define mcast_intf_addr         		pr_state_ptr->sv_mcast_intf_addr
#define router_name             		pr_state_ptr->sv_router_name

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




/* Process model interrupt handling procedure */



void
A1_ip3m_encap ()
	{
	Packet*		ip_pkptr;
	Packet*		pkptr;
	IpT_Address	dest_addr;
	Objid		ip_objid;
	Ici*		ip_iciptr;
	int			data_len;
	IpT_Address	org_addr;
	char		error_string [512];
	int			i;
	Objid		neighbor_mod_objid;
	int			input_strm, output_strm;
	char *		proto_str;
	List*		proc_record_handle_list_ptr;
	char		protocol_name[20];
	OmsT_Pr_Handle	process_record_handle;
	int			num_char;
	int			conn_class;
	char		dest_addr_str [IPC_ADDR_STR_LEN];
	Ici*		ip_rte_ici_ptr;
	Ici *		network_ici_ptr;
	IpT_Address	arrived_intf_addr;
	
	/* added by Xin Wang*/
	int         Mroute_hit;
	int         data_packet;
	char       format_name [256];
	char        addr_msg [256];
	
	Objid		proc_objid;
	int _block_origin = 0;


	FSM_ENTER (A1_ip3m_encap)

	FSM_BLOCK_SWITCH
		{
		/*---------------------------------------------------------*/
		/** state (ENCAP) enter executives **/
		FSM_STATE_ENTER_FORCED (0, state0_enter_exec, "ENCAP", "A1_ip3m_encap () [ENCAP enter execs]")
			{
			/* Obtain a packet arriving from a higher protocol layer.	*/
			input_strm = op_intrpt_strm();
			pkptr = op_pk_get (input_strm);
			if (pkptr == OPC_NIL)
				ip_encap_error ("Unable to get packet from transport layer.");
			
			/* Obtain the length of the encapsulated packet (in bytes)	*/
			/* (Note: up to 7 bits may be unmodeled for packets which 	*/
			/* do not contain an integral number of bytes.				*/ 
			data_len = op_pk_total_size_get (pkptr) / 8;
			
			/* Create an IP packet and encapsulate the new arrival		*/
			/* within its data field. The data field has a modeled size	*/
			/* of zero in order to allow breaking of the packet into	*/
			/* pre-determined arbitrarily small sizes. The bulk size	*/
			/* attribute of the IP packet will instead be used to model	*/
			/* the size of the encapsulated data.						*/
			ip_pkptr = op_pk_create_fmt ("ip3_dgram");
			if (ip_pkptr == OPC_NIL)
				ip_encap_error ("Unable to create IP datagram.");
			
			if (op_pk_nfd_set (ip_pkptr, "data", pkptr) == OPC_COMPCODE_FAILURE)
				ip_encap_error ("Unable to set data in IP datagram.");
			
			/* Determine the protocol number corresponding to the input	*/
			/* stream index.											*/
			proto_str = ip_encap_proto_get (input_strm, record_handle_list_size, interface_table);
			
			/* In case of an invalid protocol number, generate error.	*/
			if (proto_str == IP_ENCAPC_PROTOCOL_INVALID)
				{
				sprintf (error_string, "Simulation aborted; error in object (%d)", op_id_self ());
				op_sim_end (error_string, "ip_encap: protocol is invalid or unregistered", " ", " ");
				}
			else
				{
				/* Set the correct protocol number in the IP datagram. */
				op_pk_nfd_set (ip_pkptr, "protocol", proto_str, op_prg_mem_copy_create, op_prg_mem_free,
					strlen (proto_str) + 1);
				}
			
			/* Set the bulk size of the IP packet to model the space	*/
			/* occupied by the encapsulated data.						*/
			op_pk_bulk_size_set (ip_pkptr, data_len * 8); 
			
			/* Obtain the Destination address from the accompanying ICI	*/
			ip_iciptr = op_intrpt_ici ();
			if (ip_iciptr == OPC_NIL)
				{
				ip_encap_error ("Unable to get ICI from transport layer.");
				}
			
			if (op_ici_attr_get (ip_iciptr, "dest_addr", &dest_addr) == OPC_COMPCODE_FAILURE)
				{
				ip_encap_error ("Unable to get destination address from transport ICI.");
				}
			
			if (op_ici_attr_get (ip_iciptr, "connection_class", &conn_class) == OPC_COMPCODE_FAILURE)
				{
				ip_encap_error ("Unable to get connection class from transport ICI.");
				}
			
			/* Added by Xin Wang when data is sent from PIM to be forwarded by IP */
			op_ici_format (ip_iciptr, format_name);
			
			 
			/*   if ((strcmp (format_name, "ip3m_encap_ind") == 0)
			if (ip_address_is_multicast (dest_addr) && !address_is_mcast_reserved(dest_addr))*/
			if (ip_address_is_multicast (dest_addr) && (strcmp (format_name, "ip3m_encap_req") == 0))
			{
			  
				/* We enter here only if dest_addr is not mcast_reserved */
				if (op_ici_attr_get (ip_iciptr, "data_packet", &data_packet) == OPC_COMPCODE_FAILURE)
					{
					ip_address_print (dest_addr_str, dest_addr);
					sprintf (error_string, "Unable to retrieve multicast interface address for multicast address (%s)", 
						dest_addr_str);
					ip_encap_error (error_string);
					}
			   
				printf ("data = %d\n", data_packet); 
			    if (data_packet) {
			       /* Added by Xin Wang. When packet is forwarded hop by hop for triggering mcast building, src address need */
			       /* to be kept.                              */
			       if (op_ici_attr_get (ip_iciptr, "src_addr", &org_addr) == OPC_COMPCODE_FAILURE)
				  	   ip_encap_error ("Unable to get src_addr address from transport ICI."); 
			       /* Prepare an ICI that is to be sent to ip3_rte, indicating the pacekt is mcast data_packet */
				   ip_rte_ici_ptr = op_ici_create ("ip3m_rte_req");
			       
			       /* Set SRC address for packet sent back from PIM-DM */
			       if (op_pk_nfd_set (ip_pkptr, "src_addr", ip_address_copy (org_addr), op_prg_mem_copy_create,
				      op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char)) == OPC_COMPCODE_FAILURE)
				   {
				      ip_encap_error ("Unable to set destination address in IP datagram.");
				   }
			    } 
			    
				op_ici_attr_set (ip_rte_ici_ptr, "data_packet", data_packet);
				op_ici_install (ip_rte_ici_ptr);
				} /*end Xin Wang */
			/* BEGIN_JOE_SUN */
			/* if the 'multicast_interface_address' is set, then get it */
			else if ((strcmp (format_name, "ip3m_encap_req") != 0) && op_ici_attr_exists(ip_iciptr, "multicast_interface_address"))
				{
			       
					if (op_ici_attr_get (ip_iciptr, "multicast_interface_address", &mcast_intf_addr) == OPC_COMPCODE_FAILURE)
					{
					ip_address_print (dest_addr_str, dest_addr);
					sprintf (error_string, "Unable to retrieve multicast interface address for destination address (%s)", 
						dest_addr_str);
					ip_encap_error (error_string);
					}
			
				/* Prepare an ICI that is to be sent to ip3_rte, indicating which */
				/* interface on which to send the multicast packet.               */
				ip_rte_ici_ptr = op_ici_create ("ip3_rte_req");
			
				op_ici_attr_set (ip_rte_ici_ptr, "multicast_interface_address", mcast_intf_addr);
				op_ici_install (ip_rte_ici_ptr);
			}
			/* END_JOE_SUN */
			
			
			/* If the destination address is multicast, then we need to retrieve */
			/* the interface the higher layer specifies.                         */
			/* second condition added by Anindya Neogi */
			/* 'else' in the next line added by JOE_SUN */
			/* if (ip_address_is_multicast (dest_addr)) */
			else if (ip_address_is_multicast (dest_addr) && address_is_mcast_reserved(dest_addr))
				{
				if (op_ici_attr_get (ip_iciptr, "multicast_interface_address", &mcast_intf_addr) == OPC_COMPCODE_FAILURE)
					{
					ip_address_print (dest_addr_str, dest_addr);
					sprintf (error_string, "Unable to retrieve multicast interface address for multicast address (%s)", 
						dest_addr_str);
					ip_encap_error (error_string);
					}
			
				/* Prepare an ICI that is to be sent to ip3_rte, indicating which */
				/* interface on which to send the multicast packet.               */
				ip_rte_ici_ptr = op_ici_create ("ip3_rte_req");
			
				op_ici_attr_set (ip_rte_ici_ptr, "multicast_interface_address", mcast_intf_addr);
				op_ici_install (ip_rte_ici_ptr);
				}
			else
				{
				/* No request should be made to ip3_rte, so explicitly de-install any outstanding ICIs. */
				op_ici_install (OPC_NIL);
				}
			
			if (op_pk_nfd_set (ip_pkptr, "dest_addr", ip_address_copy (dest_addr), ip_address_copy_create,
				ip_address_destroy, IPC_ADDR_SIZE * sizeof (unsigned char)) == OPC_COMPCODE_FAILURE)
				{
				ip_encap_error ("Unable to set destination address in IP datagram.");
				}
			
			
			
			/*if (op_pk_nfd_get (ip_pkptr, "dest_addr", &dest_addr) == OPC_COMPCODE_FAILURE)
				  ip_rte_error ("Unable to get address from serviced packet.");
			
			    ip_address_print (dest_addr_str, dest_addr);
			    sprintf (error_string, "%s pk : encap dest %s \n", router_name, dest_addr_str);
			    printf (error_string); 
			    if (op_pk_nfd_set (ip_pkptr, "dest_addr", ip_address_copy (dest_addr), op_prg_mem_copy_create,
				      op_prg_mem_free, IPC_ADDR_SIZE * sizeof (unsigned char)) == OPC_COMPCODE_FAILURE)
			    //printf ("dest_addr set error\n"); 
			}*/
			
			
			if (op_pk_nfd_set (ip_pkptr, "connection_class", conn_class) == OPC_COMPCODE_FAILURE)
				{
				ip_encap_error ("Unable to set connection class in IP datagram.");
				}
			
			/* Set the length attributes of the IP packet.				*/
			if (op_pk_nfd_set (ip_pkptr, "orig_len", data_len) == OPC_COMPCODE_FAILURE ||
				op_pk_nfd_set (ip_pkptr, "frag_len", data_len) == OPC_COMPCODE_FAILURE)
				{
				ip_encap_error ("Unable to set length attributes in IP packet.");
				}
			
			/* Indicate that the packet is not yet fragmented.			*/
			if (op_pk_nfd_set (ip_pkptr, "frag", 0) == OPC_COMPCODE_FAILURE)
				ip_encap_error ("Unable to reset fragmentation flag in IP packet.");
			
			/* Assign a unique identity to this packet (unique among	*/
			/* packets injected into the net by this host.				*/
			if (op_pk_nfd_set (ip_pkptr, "ident", dgram_id++) == OPC_COMPCODE_FAILURE)
				ip_encap_error ("Unable to set identity field in IP packet.");
			
			
			/* Forward the packet to the IP layer.						*/
			op_pk_send (ip_pkptr, IP_ENCAP_OUTSTRM_NETWORK);
			
			/*proc_objid = op_id_from_name( own_node_objid, OPC_OBJTYPE_PROC, "igmphost");
			if( (ip_address_equal(dest_addr, ip_address_create("224.0.0.2"))) && 
				( proc_objid != OPC_OBJID_INVALID ) )
				op_sim_end("Sim end in ip ENCAP after got a IGMP packet\n", OPC_NIL, OPC_NIL, OPC_NIL);*/
			}


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


		/** state (ENCAP) transition processing **/
		FSM_TRANSIT_FORCE (3, state3_enter_exec, ;)
		/*---------------------------------------------------------*/



		/** state (INIT) enter executives **/
		FSM_STATE_ENTER_UNFORCED (1, state1_enter_exec, "INIT", "A1_ip3m_encap () [INIT enter execs]")
			{
			/* Create an Ici for communication with the higher layer. */
			/* modified by Xin Wang 10/30/97 to create ip3m_encap_ind instead of ip3_encap_ind */
			transp_iciptr = op_ici_create ("ip3m_encap_ind");
			if (transp_iciptr == OPC_NIL)
				ip_encap_error ("Unable to create ICI for communication with transport layer.");
			
			/* Determine the number of output streams connected to this module. */
			own_id = op_id_self ();
			
			/* Obtain the node's objid. */
			own_node_objid = op_topo_parent (own_id);
			
			/* Obtain the ip_encap 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_encap", OPC_NIL);
			
			/* Added Xin Wang */
			op_ima_obj_attr_get (own_node_objid, "name", router_name);
			
			
			
			
			
			
			                            
			}


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


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


		/** state (INIT) transition processing **/
		FSM_TRANSIT_FORCE (4, state4_enter_exec, ;)
		/*---------------------------------------------------------*/



		/** state (DECAP) enter executives **/
		FSM_STATE_ENTER_FORCED (2, state2_enter_exec, "DECAP", "A1_ip3m_encap () [DECAP enter execs]")
			{
			/* Obtain a packet arriving from the network layer		*/
			ip_pkptr = op_pk_get (IP_ENCAP_INSTRM_NETWORK);
			
			if (ip_pkptr == OPC_NIL)
				ip_encap_error ("Unable to get packet from network layer.");
			
			/* Obtain the ICI from IP indicating which interface	*/
			/* this packet arrived on.								*/
			network_ici_ptr = op_pk_ici_get (ip_pkptr);
			
			/*op_ici_format (network_ici_ptr, format_name);
			printf ("%s ===decap format = %s\n", router_name, format_name);*/
			
			/* Retrieve the address of the interface.				*/
			if (op_ici_attr_get (network_ici_ptr, "interface_received", &arrived_intf_addr) == OPC_COMPCODE_FAILURE)
				ip_encap_error ("Unable to retrieve interface received information from network layer indication");
			
			/* Retrieve Mroute_hit information added by Xin Wang */
			if (op_ici_attr_get (network_ici_ptr, "Mroute_hit", &Mroute_hit) == 
				OPC_COMPCODE_FAILURE)
				ip_encap_error ("Unable to retrieve Mroute_hit information from network layer indication");
			
			/* Destroy the lower layer ICI after use.				*/
			op_ici_destroy (network_ici_ptr);
			
			/* Decapsulate the contained transport level data.		*/
			if (op_pk_nfd_get (ip_pkptr, "data", &pkptr) == OPC_COMPCODE_FAILURE)
				ip_encap_error ("Unable to get data from IP datagram.");
			
			/* Determine the source address of the data.			*/
			if (op_pk_nfd_get (ip_pkptr, "src_addr", &org_addr) == OPC_COMPCODE_FAILURE)
				ip_encap_error ("Unable to get source address from IP datagram.");
			
			/*Added by Xin Wang */
			if (op_pk_nfd_get (ip_pkptr, "dest_addr", &dest_addr) == OPC_COMPCODE_FAILURE)
				{
				ip_encap_error ("Unable to get destination address from IP datagram.");
				} 
			
			/* Determine the protocol number from the ip datagram.	*/
			op_pk_nfd_get (ip_pkptr, "protocol", &proto_str);
			
			/* Determine the output stream index corresponding to	*/
			/* the protocol.										*/
			output_strm = -1;
			for (i = 0; i < record_handle_list_size; i++)
				{
				if (!strcmp (interface_table [i]->protocol, proto_str))
					{
					output_strm = interface_table [i]->outstream;
					break;
					}
				}
			
			/* Deallocate the memory allocated for the packet field	*/
			/* carrying protocol specific information.				*/
			op_prg_mem_free (proto_str);
			
			if (output_strm == -1)
				{
				/* If the protocol is unknown, print a trace error	*/
				/* message.											*/
				if (op_prg_odb_ltrace_active ("ip_errs"))
					{
					op_pk_format (pkptr, protocol_name);
					sprintf (error_string, "Packet of unknown protocol [%s] received by IP encap. Destroying packet",
						protocol_name);
					op_sim_message (error_string, OPC_NIL);
					}
			
				ip_address_destroy (arrived_intf_addr);
				op_pk_destroy (ip_pkptr);
				op_pk_destroy (pkptr);
				}
			else
				{
			    /* Discard the IP packet.							*/
				op_pk_destroy (ip_pkptr);
			
			    /* Forward the data to the transport layer. Pass	*/
				/* src address information in an accompanying ICI.	*/
				if (op_ici_attr_set (transp_iciptr, "src_addr", org_addr) == OPC_COMPCODE_FAILURE)
					ip_encap_error ("Unable to set source address in transport layer ICI.");
			
			    /* Added by Xin Wang Pass the destination address information in an accompanying ICI. */
			    if (op_ici_attr_set (transp_iciptr, "dest_addr", dest_addr) == OPC_COMPCODE_FAILURE)
					ip_encap_error ("Unable to set destination address in transport layer ICI.");
			
			    /* Pass the received interface also.				*/
				if (op_ici_attr_set (transp_iciptr, "interface_received", arrived_intf_addr) == OPC_COMPCODE_FAILURE)
					ip_encap_error ("Unable to set interface receieved address in transport layer ICI.");
			
			    /* Added by Xin Wang Pass the Mroute_hit. */
				if (op_ici_attr_set (transp_iciptr, "Mroute_hit", Mroute_hit) == OPC_COMPCODE_FAILURE)
					ip_encap_error ("Unable to set Mroute_hit in transport layer ICI.");
			
				/* Install the ICI and send the packet to the		*/
				/* higher layer.									*/
				op_ici_install (transp_iciptr);
				op_pk_send (pkptr, output_strm);
				}
			
			}


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


		/** state (DECAP) transition processing **/
		FSM_TRANSIT_FORCE (3, state3_enter_exec, ;)
		/*---------------------------------------------------------*/



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


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


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


		/** state (WAIT) transition processing **/
		FSM_INIT_COND (FROM_TRANSPORT)
		FSM_TEST_COND (FROM_NETWORK)
		FSM_TEST_LOGIC ("WAIT")

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



		/** state (STRM_DEMUX) enter executives **/
		FSM_STATE_ENTER_FORCED (4, state4_enter_exec, "STRM_DEMUX", "A1_ip3m_encap () [STRM_DEMUX enter execs]")
			{
			/* This state is used to find out which modules are directly connected to ip_encap.
			The stream indeces are stored together with the protocol attributes for demultiplexing
			different higher-layer protocols. */
			proc_record_handle_list_ptr =  op_prg_list_create();
			oms_pr_process_discover (own_id, proc_record_handle_list_ptr, "node objid", OMSC_PR_OBJID, own_node_objid, OPC_NIL);
			
			record_handle_list_size = op_prg_list_size (proc_record_handle_list_ptr);
			
			if (record_handle_list_size == 0)
				{
				/* An error should be created if no processes are connected to ip_encap. */
				op_sim_end ("Error: no modules connected to ip_encap", "", "", "");
				}
			else
				{
				/* For all the directly connected networks, find out the input and output stream indeces. */
				
				/* Create the interface information structure. */
				interface_table = (IpT_Encap_Interface **) op_prg_mem_alloc (record_handle_list_size * 
					sizeof (IpT_Encap_Interface *));
				
				for (i = 0; i < record_handle_list_size; i++)
					{
					process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, i);
				
					/* Obtain the module object id of the neighboring module, and the value of the protocol attribute. */
			        oms_pr_attr_get (process_record_handle, "module objid", OMSC_PR_OBJID, &neighbor_mod_objid);
			        oms_pr_attr_get (process_record_handle, "protocol", OMSC_PR_STRING, protocol_name);
			
					/* Determine the input and output stream indeces. */
					oms_tan_neighbor_streams_find (own_id, neighbor_mod_objid, &input_strm, &output_strm);
			
					/* Store the information in an interface table. */
					/* Allocate memory for the interface element. */
					interface_table[i] = (IpT_Encap_Interface*) op_prg_mem_alloc (sizeof (IpT_Encap_Interface));
			
					/* Store the protocol number and the corresponding stream indeces into the interface table. */
					num_char = (strlen (protocol_name) + 1);
					interface_table[i]->protocol = (char*) op_prg_mem_alloc (sizeof (char) * num_char);
					strcpy (interface_table[i]->protocol, protocol_name);
					interface_table[i]->outstream = output_strm;
					interface_table[i]->instream = input_strm;
					}
				}
			
			/* Deallocate the list pointer. */
			op_prg_mem_free (proc_record_handle_list_ptr);	
			}


		/** state (STRM_DEMUX) exit executives **/
		FSM_STATE_EXIT_FORCED (4, state4_exit_exec, "STRM_DEMUX", "A1_ip3m_encap () [STRM_DEMUX exit execs]")
			{
			}


		/** state (STRM_DEMUX) transition processing **/
		FSM_INIT_COND (FROM_NETWORK)
		FSM_TEST_COND (FROM_TRANSPORT)
		FSM_TEST_LOGIC ("STRM_DEMUX")

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



		}


	FSM_EXIT (1,A1_ip3m_encap)
	}




void
A1_ip3m_encap_svar (prs_ptr,var_name,var_p_ptr)
	A1_ip3m_encap_state		*prs_ptr;
	char			*var_name, **var_p_ptr;
	{

	FIN (A1_ip3m_encap_svar (prs_ptr))

	*var_p_ptr = VOS_NIL;
	if (Vos_String_Equal ("dgram_id" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_dgram_id);
	if (Vos_String_Equal ("transp_iciptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_transp_iciptr);
	if (Vos_String_Equal ("interface_table" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_interface_table);
	if (Vos_String_Equal ("own_id" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_own_id);
	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 ("record_handle_list_size" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_record_handle_list_size);
	if (Vos_String_Equal ("mcast_intf_addr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_mcast_intf_addr);
	if (Vos_String_Equal ("router_name" , var_name))
		*var_p_ptr = (char *) (prs_ptr->sv_router_name);

	FOUT;
	}




void
A1_ip3m_encap_diag ()
	{
	Packet*		ip_pkptr;
	Packet*		pkptr;
	IpT_Address	dest_addr;
	Objid		ip_objid;
	Ici*		ip_iciptr;
	int			data_len;
	IpT_Address	org_addr;
	char		error_string [512];
	int			i;
	Objid		neighbor_mod_objid;
	int			input_strm, output_strm;
	char *		proto_str;
	List*		proc_record_handle_list_ptr;
	char		protocol_name[20];
	OmsT_Pr_Handle	process_record_handle;
	int			num_char;
	int			conn_class;
	char		dest_addr_str [IPC_ADDR_STR_LEN];
	Ici*		ip_rte_ici_ptr;
	Ici *		network_ici_ptr;
	IpT_Address	arrived_intf_addr;
	
	/* added by Xin Wang*/
	int         Mroute_hit;
	int         data_packet;
	char       format_name [256];
	char        addr_msg [256];
	
	Objid		proc_objid;
	int _block_origin;

	FIN (A1_ip3m_encap_diag ())

	BINIT

	FOUT;
	}




void
A1_ip3m_encap_terminate ()
	{
	Packet*		ip_pkptr;
	Packet*		pkptr;
	IpT_Address	dest_addr;
	Objid		ip_objid;
	Ici*		ip_iciptr;
	int			data_len;
	IpT_Address	org_addr;
	char		error_string [512];
	int			i;
	Objid		neighbor_mod_objid;
	int			input_strm, output_strm;
	char *		proto_str;
	List*		proc_record_handle_list_ptr;
	char		protocol_name[20];
	OmsT_Pr_Handle	process_record_handle;
	int			num_char;
	int			conn_class;
	char		dest_addr_str [IPC_ADDR_STR_LEN];
	Ici*		ip_rte_ici_ptr;
	Ici *		network_ici_ptr;
	IpT_Address	arrived_intf_addr;
	
	/* added by Xin Wang*/
	int         Mroute_hit;
	int         data_packet;
	char       format_name [256];
	char        addr_msg [256];
	
	Objid		proc_objid;
	int _block_origin;

	FIN (A1_ip3m_encap_terminate ())

	BINIT

	FOUT;
	}



Compcode
A1_ip3m_encap_init (pr_state_pptr)
	A1_ip3m_encap_state			**pr_state_pptr;
	{
	int _block_origin = 0;
	static VosT_Cm_Obtype	obtype = OPC_NIL;

	extern int				Vos_Nop ();

	FIN (A1_ip3m_encap_init (pr_state_pptr))

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

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



enum { _block_origin = __LINE__ };
char *
ip_encap_proto_get (strm_num, num_iface, interface_table_handle)
	int			strm_num;
	int			num_iface;
	IpT_Encap_Interface**	interface_table_handle;
	{
	int			i;
	char *		proto_str;

	/** This function returns the protocol string corresponding to an input stream index. **/ 
	FIN (ip_encap_proto_get (strm_num, num_iface, interface_table_handle))

	for (i=0; i < num_iface; i++)
		{
		/*printf("interface_talbe_handle[%d]->instream = %d, strm_num = %d\n", 
				i, interface_table_handle[i]->instream, strm_num);*/
		if(interface_table_handle[i]->instream == strm_num)
			{
			proto_str = (char *) op_prg_mem_alloc (strlen (interface_table_handle [i]->protocol) + 1);
			strcpy (proto_str, interface_table_handle [i]->protocol);
			FRET (proto_str);
			}
		}

	/* If no match is found, return an error code. */
	FRET (IP_ENCAPC_PROTOCOL_INVALID);
	} 

void
ip_encap_error (msg)
	char*	msg;
	{
	/** Print an error message and exit the simulation. **/
	FIN (ip_encap_error (msg));

	op_sim_end ("Error from IP encapsulation process model (ip3_encap):",
				msg, OPC_NIL, OPC_NIL);
	
	FOUT;
	}


