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



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



/* Header block */
#include "udp_api.h"
#include "udp.h"
#include "oms_pr.h"
#include "ip3_addr.h"

/* Transition macros */
#define CREATE			((intrpt_type == OPC_INTRPT_REMOTE) &&			\
						(intrpt_code == UDPC_COMMAND_CREATE_PORT))
#define SEND			((intrpt_type == OPC_INTRPT_STRM) && 			\
						(intrpt_strm !=	UDPC_INSTRM_NETWORK))
#define DGRAM_ARRIVAL	((intrpt_type == OPC_INTRPT_STRM) &&			\
						(intrpt_strm == UDPC_INSTRM_NETWORK))

/*UdpT_Tcb*		udp_tcb_from_port ();*/
;
#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
	List*	                  		sv_tcb_list;
	Ici*	                   		sv_net_ici_ptr;
	Ici*	                   		sv_app_ici_ptr;
	int	                    		sv_udp_trace_active;
	int	                    		sv_port_id_new;
	Objid	                  		sv_own_mod_objid;
	Objid	                  		sv_own_node_objid;
	Prohandle	              		sv_own_prohandle;
	OmsT_Pr_Handle	         		sv_own_process_record_handle;
	char	                   		sv_proc_model_name[20];
	} rip_udp2_state;

#define pr_state_ptr            		((rip_udp2_state*) SimI_Mod_State_Ptr)
#define tcb_list                		pr_state_ptr->sv_tcb_list
#define net_ici_ptr             		pr_state_ptr->sv_net_ici_ptr
#define app_ici_ptr             		pr_state_ptr->sv_app_ici_ptr
#define udp_trace_active        		pr_state_ptr->sv_udp_trace_active
#define port_id_new             		pr_state_ptr->sv_port_id_new
#define own_mod_objid           		pr_state_ptr->sv_own_mod_objid
#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

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




/* Process model interrupt handling procedure */



void
rip_udp2 ()
	{
	int				intrpt_type;
	int				intrpt_strm;
	int				intrpt_code;
	Ici*			ici_ptr;
	Objid			strm_objid;
	
	UdpT_Port		local_port;
	IpT_Address		rem_addr;
	 
	UdpT_Port		rem_port;
	int				strm_index;
	
	int				i;
	int				list_size;
	
	UdpT_Tcb*		tcb_ptr;
	
	char			msg0 [512], msg1 [512];
	
	int				port_assigned;
	
	Packet*			pkptr;
	Packet*			data_pkptr;
	
	int				conn_class;
	
	int _block_origin = 0;


	FSM_ENTER (rip_udp2)

	FSM_BLOCK_SWITCH
		{
		/*---------------------------------------------------------*/
		/** state (init) enter executives **/
		FSM_STATE_ENTER_FORCED (0, state0_enter_exec, "init", "rip_udp2 () [init enter execs]")
			{
			tcb_list = op_prg_list_create ();
			
			net_ici_ptr = op_ici_create ("ip3_encap_req");
			app_ici_ptr = op_ici_create ("udp_command2");
			
			/* when assigning ports, start at lowest unreserved port */
			port_id_new = 255;
			
			/* Obtain the udp module's objid. */
			own_mod_objid = op_id_self ();
			
			/* Obtain the node's objid. */
			own_node_objid = op_topo_parent (own_mod_objid);
			
			/* Obtain the udp 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_mod_objid, "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_mod_objid, 
										own_prohandle, proc_model_name);
			
			/* Register the protocol attribute in the registry. */
			oms_pr_attr_set (own_process_record_handle, "protocol", OMSC_PR_STRING, "udp", OPC_NIL);
			}


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


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



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


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


		/** state (idle) exit executives **/
		FSM_STATE_EXIT_UNFORCED (1, state1_exit_exec, "idle", "rip_udp2 () [idle exit execs]")
			{
			/* gather information relevant to the current interrupt */   
			udp_trace_active = op_prg_odb_ltrace_active ("udp");
			
			intrpt_type = op_intrpt_type ();
			if (intrpt_type == OPC_INTRPT_STRM)
				intrpt_strm = op_intrpt_strm ();
			else
				intrpt_code = op_intrpt_code ();
			ici_ptr = op_intrpt_ici ();
			}


		/** state (idle) transition processing **/
		FSM_INIT_COND (CREATE)
		FSM_TEST_COND (SEND)
		FSM_TEST_COND (DGRAM_ARRIVAL)
		FSM_TEST_LOGIC ("idle")

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



		/** state (CREATE) enter executives **/
		FSM_STATE_ENTER_FORCED (2, state2_enter_exec, "CREATE", "rip_udp2 () [CREATE enter execs]")
			{
			/* Read the arguments to the CREATE_PORT command. */
			op_ici_attr_get (ici_ptr, "strm_index", &strm_index);
			op_ici_attr_get (ici_ptr, "local_port", &local_port);
			
			/* Find the preexisting TCB for this port, if any.				*/
			/* If none exists, create a new TCB and add it to the			*/
			/* list.  If the port id is unspecified, assign a free port.	*/
			
			if (udp_tcb_from_port (local_port) != OPC_NIL)
				{
				/* A port with the given id is already assigned. */
				/* Return an error status code to the application. */
				if (udp_trace_active)
					op_prg_odb_print_minor ("Port assignment already exists.", OPC_NIL);
			
				op_ici_attr_set (ici_ptr, "status", UDPC_IND_EADDRINUSE);
				}
			
			else
				{
				if (local_port == UDPC_PORT_UNSPEC)
					/* No port was specified. */
					{
					/** determine an unassigned port. **/
					port_assigned = 1;
					while (port_assigned == 1)
						{
						if (udp_tcb_from_port (port_id_new++) == OPC_NIL)
							port_assigned = 0;
						}
					local_port = port_id_new;
					}
			
				/* If no stream index has been specified, pick a logical one. */
				if (strm_index == UDPC_STRM_INDEX_UNSPEC)
					{
					strm_objid = op_topo_connect (op_id_self (), op_intrpt_source (),
						OPC_OBJTYPE_STRM, 0);
					if (strm_objid == OPC_OBJID_INVALID)
						op_sim_end ("Illegal UDP CREATE_PORT command",
							"No stream connection exists from UDP process to application.", "", "");
					else
						{
						op_ima_obj_attr_get (strm_objid, "src stream", &strm_index);
						op_ici_attr_set (ici_ptr, "strm_index", strm_index);
						}
					}
			
				/* This is a new port registration.			*/
				/* Create a new TCB and add it to the list. */
				tcb_ptr = (UdpT_Tcb *) op_prg_mem_alloc (sizeof (UdpT_Tcb));
				tcb_ptr->app_objid = op_intrpt_source ();
				tcb_ptr->strm_index = strm_index;
				tcb_ptr->local_port = local_port;
				op_prg_list_insert (tcb_list, tcb_ptr, OPC_LISTPOS_TAIL);
				
				/* Return a successful status code to the application. */
				op_ici_attr_set (ici_ptr, "status", UDPC_IND_SUCCESS);
				}
			}


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


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



		/** state (SEND) enter executives **/
		FSM_STATE_ENTER_FORCED (3, state3_enter_exec, "SEND", "rip_udp2 () [SEND enter execs]")
			{
			/** Encapsulate the data packet in the UDP datagram **/
			/** and send to the lower layer. **/ 
			
			data_pkptr = op_pk_get (intrpt_strm);
			
			op_ici_attr_get (ici_ptr, "rem_addr", &rem_addr);
			 
			op_ici_attr_get (ici_ptr, "rem_port", &rem_port);
			op_ici_attr_get (ici_ptr, "local_port", &local_port);
			op_ici_attr_get (ici_ptr, "connection_class", &conn_class);
			
			/* Create and format the UDP datagram. */
			pkptr = op_pk_create_fmt ("udp_dgram");
			op_pk_nfd_set (pkptr, "data", data_pkptr); 
			op_pk_nfd_set (pkptr, "src_port", local_port); 
			op_pk_nfd_set (pkptr, "dest_port", rem_port); 
			op_pk_nfd_set (pkptr, "length", op_pk_total_size_get (pkptr)); 
			
			/* Format the IP ici */
			 
			op_ici_attr_set (net_ici_ptr, "dest_addr", rem_addr); 
			op_ici_attr_set (net_ici_ptr, "connection_class", conn_class); 
			
			op_ici_install (net_ici_ptr);
			op_pk_send_forced (pkptr, UDPC_OUTSTRM_NETWORK); 
			}


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


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



		/** state (ARRIVE) enter executives **/
		FSM_STATE_ENTER_FORCED (4, state4_enter_exec, "ARRIVE", "rip_udp2 () [ARRIVE enter execs]")
			{
			/* Get the received packet. */
			pkptr = op_pk_get (UDPC_INSTRM_NETWORK);
			
			/* Determine the address information from the packet.	*/
			/* Note that context is switching from sender to		*/
			/* receiver, so variable names change appropriately.	*/
			op_ici_attr_get (ici_ptr, "src_addr", &rem_addr);
			 
			op_pk_nfd_get (pkptr, "src_port", &rem_port);
			op_pk_nfd_get (pkptr, "dest_port", &local_port);
			
			/* Tried by cjin to receive mcast data packet */
			if( (rem_port == 1001) ||
				(rem_port == 1002) ||
				(local_port == 1001) ||
				(local_port == 1002) ) {
				printf("%s received a udp packet with rem_port = %d, local_port = %d\n", 
					proc_model_name, rem_port, local_port);
				op_sim_end("Sim end after UDP get a packet with above info\n", OPC_NIL, OPC_NIL, OPC_NIL);
			}
			
			/* Find the destination TCB from the port.	*/
			tcb_ptr = udp_tcb_from_port (local_port);
			if (tcb_ptr == OPC_NIL)
				{
				/* No match was found. Discard the datagram. */
				if (udp_trace_active)
					{
					sprintf (msg0, "Datagram received for unknown port %d", local_port);
					op_prg_odb_print_major (msg0, "Discarding datagram.", OPC_NIL);
					}
				op_pk_destroy (pkptr);
				}		
			 
			else
				{
				/* A port match was found. Forward the	*/
			 	/* datagram to the application.			*/
				op_ici_attr_set (app_ici_ptr, "rem_addr", rem_addr); 
				 
				op_ici_attr_set (app_ici_ptr, "rem_port", rem_port);
				op_ici_attr_set (app_ici_ptr, "local_port", local_port);
				op_ici_install (app_ici_ptr);
			
				op_pk_nfd_get (pkptr, "data", &data_pkptr); 
				op_pk_send (data_pkptr, tcb_ptr->strm_index);
			
				/* Destroy the UDP header */
				op_pk_destroy (pkptr);  
				}
			}


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


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



		}


	FSM_EXIT (0,rip_udp2)
	}




void
rip_udp2_svar (prs_ptr,var_name,var_p_ptr)
	rip_udp2_state		*prs_ptr;
	char			*var_name, **var_p_ptr;
	{

	FIN (rip_udp2_svar (prs_ptr))

	*var_p_ptr = VOS_NIL;
	if (Vos_String_Equal ("tcb_list" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_tcb_list);
	if (Vos_String_Equal ("net_ici_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_net_ici_ptr);
	if (Vos_String_Equal ("app_ici_ptr" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_app_ici_ptr);
	if (Vos_String_Equal ("udp_trace_active" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_udp_trace_active);
	if (Vos_String_Equal ("port_id_new" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_port_id_new);
	if (Vos_String_Equal ("own_mod_objid" , var_name))
		*var_p_ptr = (char *) (&prs_ptr->sv_own_mod_objid);
	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);

	FOUT;
	}




void
rip_udp2_diag ()
	{
	int				intrpt_type;
	int				intrpt_strm;
	int				intrpt_code;
	Ici*			ici_ptr;
	Objid			strm_objid;
	
	UdpT_Port		local_port;
	IpT_Address		rem_addr;
	 
	UdpT_Port		rem_port;
	int				strm_index;
	
	int				i;
	int				list_size;
	
	UdpT_Tcb*		tcb_ptr;
	
	char			msg0 [512], msg1 [512];
	
	int				port_assigned;
	
	Packet*			pkptr;
	Packet*			data_pkptr;
	
	int				conn_class;
	
	int _block_origin;

	FIN (rip_udp2_diag ())

	BINIT
	op_prg_odb_print_major ("TCB list:", OPC_NIL);
	list_size = op_prg_list_size (tcb_list);
	for (i = 0; i < list_size; i++)
		{
		tcb_ptr = (UdpT_Tcb *) op_prg_list_access (tcb_list, i);
	
		/* Print socket information. */
		sprintf (msg0, "Port (%d) information: ", tcb_ptr->local_port);
		sprintf (msg1, "Application objid (%d), traffic through stream (%d)",
			tcb_ptr->app_objid, tcb_ptr->strm_index);
		op_prg_odb_print_major (msg0, msg1, OPC_NIL);
	
		}

	FOUT;
	}




void
rip_udp2_terminate ()
	{
	int				intrpt_type;
	int				intrpt_strm;
	int				intrpt_code;
	Ici*			ici_ptr;
	Objid			strm_objid;
	
	UdpT_Port		local_port;
	IpT_Address		rem_addr;
	 
	UdpT_Port		rem_port;
	int				strm_index;
	
	int				i;
	int				list_size;
	
	UdpT_Tcb*		tcb_ptr;
	
	char			msg0 [512], msg1 [512];
	
	int				port_assigned;
	
	Packet*			pkptr;
	Packet*			data_pkptr;
	
	int				conn_class;
	
	int _block_origin;

	FIN (rip_udp2_terminate ())

	BINIT

	FOUT;
	}



Compcode
rip_udp2_init (pr_state_pptr)
	rip_udp2_state			**pr_state_pptr;
	{
	int _block_origin = 0;
	static VosT_Cm_Obtype	obtype = OPC_NIL;

	extern int				Vos_Nop ();

	FIN (rip_udp2_init (pr_state_pptr))

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

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



enum { _block_origin = __LINE__ };
UdpT_Tcb*
udp_tcb_from_port (loc_port) 
	UdpT_Port			loc_port;
	{
	UdpT_Tcb*			tcb_ptr;
	int					i, list_size;

	/** Find the TCB with the given port number			**/
	/** in the TCB list.  Returns a pointer to the TCB 	**/
	/** record, or NIL if no match is found.			**/
	FIN (udp_tcb_from_port (loc_port))

	list_size = op_prg_list_size (tcb_list);
	for (i = 0; i < list_size; i++)
		{
		tcb_ptr = (UdpT_Tcb *) op_prg_list_access (tcb_list, i);
		if (loc_port == tcb_ptr->local_port)
			{
			break;
			}
		}
	if (i == list_size)
		tcb_ptr = OPC_NIL;

	FRET (tcb_ptr)
	}
