/* ospf_interface.ex.c:  External code for interface sub-package. */

/***** Header files *****/
#include <opnet.h>

/* OPNET Model Support (OMS) headers */
#include "oms_pr.h"

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

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

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

/***** Globals *****/
extern IpT_Address			OspfI_All_Spf_Routers;
extern IpT_Address			OspfI_All_Dr_Routers;
extern Stathandle			OspfI_Global_Load_Stat;
extern double				OspfI_Global_Bits_Accum;

/* Table indicating procedure to execute for a particular state/event combination. */
OspfT_Interface_State_Event_Proc 
OspfI_Interface_Trans_Table [OSPFC_INTF_NUM_STATES] [OSPFC_INTF_NUM_EVENTS] = 
{
	/* Down state */
	{
		ospf_interface_undef_handler,	/* Interface Up */
		ospf_interface_undef_handler,	/* Wait */
		ospf_interface_undef_handler,	/* Backup Seen */
		ospf_interface_undef_handler,	/* Neighbor Change */
		ospf_interface_undef_handler,	/* Loop Indication */
		ospf_interface_undef_handler,	/* Unloop Indication */
		ospf_interface_undef_handler	/* Interface Down */
	},

	/* Loopback state */
	{
		ospf_interface_undef_handler,	/* Interface Up */
		ospf_interface_undef_handler,	/* Wait */
		ospf_interface_undef_handler,	/* Backup Seen */
		ospf_interface_undef_handler,	/* Neighbor Change */
		ospf_interface_undef_handler,	/* Loop Indication */
		ospf_interface_undef_handler,	/* Unloop Indication */
		ospf_interface_undef_handler	/* Interface Down */
	},

	/* Waiting state */
	{
		ospf_interface_undef_handler,	/* Interface Up */
		ospf_interface_wait_cancel_invoke,	/* Wait */
		ospf_interface_process_invoke,	/* Backup Seen */
		ospf_interface_undef_handler,	/* Neighbor Change */
		ospf_interface_event_suppress,	/* Loop Indication */
		ospf_interface_undef_handler,	/* Unloop Indication */
		ospf_interface_destroy			/* Interface Down */
	},

	/* Point-to-Point state */
	{
		ospf_interface_undef_handler,	/* Interface Up */
		ospf_interface_undef_handler,	/* Wait */
		ospf_interface_undef_handler,	/* Backup Seen */
		ospf_interface_undef_handler,	/* Neighbor Change */
		ospf_interface_event_suppress,	/* Loop Indication */
		ospf_interface_undef_handler,	/* Unloop Indication */
		ospf_interface_destroy			/* Interface Down */
	},

	/* DR Other state */
	{
		ospf_interface_undef_handler,	/* Interface Up */
		ospf_interface_undef_handler,	/* Wait */
		ospf_interface_undef_handler,	/* Backup Seen */
		ospf_interface_process_invoke,	/* Neighbor Change */
		ospf_interface_event_suppress,	/* Loop Indication */
		ospf_interface_undef_handler,	/* Unloop Indication */
		ospf_interface_destroy			/* Interface Down */
	},

	/* Backup state */
	{
		ospf_interface_undef_handler,	/* Interface Up */
		ospf_interface_undef_handler,	/* Wait */
		ospf_interface_undef_handler,	/* Backup Seen */
		ospf_interface_process_invoke,	/* Neighbor Change */
		ospf_interface_event_suppress,	/* Loop Indication */
		ospf_interface_undef_handler,	/* Unloop Indication */
		ospf_interface_destroy			/* Interface Down */
	},

	/* DR state */
	{
		ospf_interface_undef_handler,	/* Interface Up */
		ospf_interface_undef_handler,	/* Wait */
		ospf_interface_undef_handler,	/* Backup Seen */
		ospf_interface_process_invoke,	/* Neighbor Change */
		ospf_interface_event_suppress,	/* Loop Indication */
		ospf_interface_undef_handler,	/* Unloop Indication */
		ospf_interface_destroy			/* Interface Down */
	}
};

/***** Procedure Definitions *****/

OspfT_Interface *
ospf_interface_alloc (void)
	{
	OspfT_Interface *		interface_ptr;

	/** Allocate and initialize a new interface data structure. **/
	FIN (ospf_interface_alloc (void));

	/* Allocate data structure. */
	interface_ptr = op_prg_mem_alloc (sizeof (OspfT_Interface));

	/* Initialize interface components. */
	interface_ptr->interface_type = OspfC_Interface_Type_Pt_To_Pt;
	interface_ptr->interface_address = IPC_ADDR_INVALID;
	interface_ptr->interface_mask = IPC_ADDR_INVALID;
	interface_ptr->mtu = 0;
	interface_ptr->area_id = OSPFC_AREA_ID_INVALID;
	interface_ptr->hello_interval = 0.0;
	interface_ptr->router_dead_interval = 0.0;
	interface_ptr->transmit_delay = 0.0;
	interface_ptr->router_priority = 0;
	interface_ptr->cost = 0;
	interface_ptr->retransmit_interval = 0.0;
	interface_ptr->delayed_ack_list_ptr = op_prg_list_create ();

	FRET (interface_ptr);
	}

OspfT_Interface_Invoke_Info *
ospf_interface_invoke_info_create (void)
	{
	OspfT_Interface_Invoke_Info *		intf_info_ptr;

	/** Create an interface invocation object, which represents state to         **/
	/** pass to an interface object upon invocation (passed as argument memory). **/
	FIN (ospf_interface_invoke_info_create (void));

	/* Allocate storage for object. */
	intf_info_ptr = (OspfT_Interface_Invoke_Info *) op_prg_mem_alloc (sizeof 
		(OspfT_Interface_Invoke_Info));

	/* Initialize components. */
	intf_info_ptr->interface_objid = OPC_OBJID_INVALID;
	intf_info_ptr->interface_ptr = OPC_NIL;
	intf_info_ptr->ip_interface_ptr = OPC_NIL;
	intf_info_ptr->interface_event = OspfC_Interface_Event_Intf_Up;

	FRET (intf_info_ptr);
	}

OspfT_Interface *
ospf_interface_create (Objid interface_objid, IpT_Interface_Info *ip_interface_ptr, 
	OspfT_Area *parent_area_ptr, Objid root_objid)
	{
	OspfT_Interface *				interface_ptr;
	OspfT_Interface_Invoke_Info *	intf_info_ptr;

	/** Create an interface object based on the state found in the **/
	/** OPNET interface table.  The interface objid represents a   **/
	/** single interface in that table.                            **/
	FIN (ospf_interface_create (interface_objid, ip_interface_ptr, parent_area_ptr, root_objid));

	/* Create initialized interface object. */
	interface_ptr = ospf_interface_alloc ();

	/* Create an instance of the interface process model (that implements */
	/* the interface state machine specified by OSPF).                    */
	interface_ptr->proc_handle = op_pro_create ("ospf_interface", OPC_NIL);

	/* Register this process model in the process registry. */
	interface_ptr->proc_reg_handle = (OmsT_Pr_Handle) oms_pr_process_register 
		(op_topo_parent (root_objid), root_objid, interface_ptr->proc_handle, "ospf_interface");

	/* Specify the area the interface belongs to. */
	interface_ptr->parent_area_ptr = parent_area_ptr;

	/* Add the interface to the area. */
	op_prg_list_insert (interface_ptr->parent_area_ptr->interface_list_ptr, interface_ptr, OPC_LISTPOS_TAIL);

	/* Create an object that contains useful state to pass to interface process. */
	intf_info_ptr = ospf_interface_invoke_info_create ();

	/* Set components of interface info object. */
	intf_info_ptr->interface_objid = interface_objid;
	intf_info_ptr->interface_ptr = interface_ptr;
	intf_info_ptr->ip_interface_ptr = ip_interface_ptr;

	/* Invoke the interface process so that it can initialize itself. */
	op_pro_invoke (interface_ptr->proc_handle, intf_info_ptr);
	
	FRET (interface_ptr);
	}

void
ospf_interface_destroy (OspfT_Interface *interface_ptr, OspfT_Interface_Event interface_event)
	{
	/** Deallocates an interface, and calls op_pro_destroy () on the interface **/
	/** process (to allow the process to deallocate its state).                **/
	FIN (ospf_interface_destroy (interface_ptr, interface_event));

	ip_address_destroy (interface_ptr->interface_address);
	ip_address_destroy (interface_ptr->interface_mask);
	ip_address_destroy (interface_ptr->area_id);

	op_pro_destroy (interface_ptr->proc_handle);
	op_prg_mem_free (interface_ptr);

	FOUT;
	}

OspfT_Interface_Type
ospf_interface_type_get (OspfT_Interface *interface_ptr, char *interface_type_str)
	{
	char		interface_type_str_array [3][128] = {"Point-to-Point", "Broadcast", "Non-Broadcast"};
	
	/** Retrieve the type of the interface.  Note that this can be accessed **/
	/** directly by looking at the interface structure.  This function is   **/
	/** useful if you need the string representation of the interface type. **/
	/** If interface_type_str is NIL, then no value will be returned.       **/
	FIN (ospf_interface_type_get (interface_ptr, interface_type_str));

	/* Return the string value if specified. */
	if (interface_type_str != OPC_NIL)
		strcpy (interface_type_str, interface_type_str_array [(int) interface_ptr->interface_type]);

	FRET (interface_ptr->interface_type);
	}

OspfT_Interface_State
ospf_interface_state_get (OspfT_Interface *interface_ptr, char *interface_state_str)
	{
	OspfT_Interface_State *	interface_state_ptr;

	/** Get the current operational state of an interface.  If interface_state_str **/
	/** is non-NIL, then also return the string representation of the state.       **/
	FIN (ospf_interface_state_get (interface_ptr, interface_state_str));

	/* The interface state is retrieved via the process registry. */
	oms_pr_attr_get (interface_ptr->proc_reg_handle,
		"state", OMSC_PR_ADDRESS, &interface_state_ptr);

	/* Return the string value if specified. */
	if (interface_state_str != OPC_NIL)
		ospf_interface_state_str_get (*interface_state_ptr, interface_state_str);

	FRET (*interface_state_ptr);
	}

void
ospf_interface_state_str_get (OspfT_Interface_State intf_state, char *interface_state_str)
	{
	char interface_state_str_array [OSPFC_INTF_NUM_STATES][128] = {"Down", "Loopback", "Waiting", "Pt-Pt", 
																   "DR Other", "Backup", "DR      "};

	/** Get the string representation of the operational state of the interface. **/
	FIN (ospf_interface_state_str_get (intf_state, interface_state_str));

	strcpy (interface_state_str, interface_state_str_array [(int) intf_state]);

	FOUT;
	}

void
ospf_interface_event_str_get (OspfT_Interface_Event intf_event, char *interface_event_str)
	{
	char interface_event_str_array [OSPFC_INTF_NUM_EVENTS][128] = {"Intf Up", "Wait Timer", "Backup Seen",
																   "Neighbor Change", "Loop Ind", "Unloop Ind",
																   "Intf Down"};
	/** Get the string representation of the interface event. **/
	FIN (ospf_interface_event_str_get (intf_event, interface_event_str));

	strcpy (interface_event_str, interface_event_str_array [(int) intf_event]);

	FOUT;
	}

OspfT_Router_Id
ospf_interface_dr_get (OspfT_Interface *interface_ptr)
	{
	OspfT_Router_Id	*		designated_router_id_ptr;

	/** Get the designated router for this interface. **/
	FIN (ospf_interface_dr_get (interface_ptr));

	/* The designated router ID is part of the dynamic state of          */
	/* the interface, which must be retrieved from the process registry. */
	oms_pr_attr_get (interface_ptr->proc_reg_handle,
		"designated router", OMSC_PR_ADDRESS, &designated_router_id_ptr);

	FRET (*designated_router_id_ptr);
	}

IpT_Address
ospf_interface_dr_intf_get (OspfT_Interface *interface_ptr)
	{
	IpT_Address *		dr_intf_addr_ptr;

	/** Get the designated router interface address for this interface. **/
	FIN (ospf_interface_dr_intf_get (interface_ptr));

	/* The designated router address is part of the dynamic state of     */
	/* the interface, which must be retrieved from the process registry. */
	oms_pr_attr_get (interface_ptr->proc_reg_handle,
		"DR interface addr", OMSC_PR_ADDRESS, &dr_intf_addr_ptr);

	FRET (*dr_intf_addr_ptr);
	}

OspfT_Router_Id
ospf_interface_backup_dr_get (OspfT_Interface *interface_ptr)
	{
	OspfT_Router_Id	*		backup_designated_router_id_ptr;

	/** Get the backup designated router for this interface. **/
	FIN (ospf_interface_backup_dr_get (interface_ptr));

	/* The backup designated router ID is part of the dynamic state of   */
	/* the interface, which must be retrieved from the process registry. */
	oms_pr_attr_get (interface_ptr->proc_reg_handle,
		"backup designated router", OMSC_PR_ADDRESS, &backup_designated_router_id_ptr);

	FRET (*backup_designated_router_id_ptr);
	}

IpT_Address
ospf_interface_backup_dr_intf_get (OspfT_Interface *interface_ptr)
	{
	IpT_Address *		backup_dr_intf_addr_ptr;

	/** Get the backup designated router interface address for this interface. **/
	FIN (ospf_interface_backup_dr_intf_get (interface_ptr));

	/* The backup designated router address is part of the dynamic state of */
	/* the interface, which must be retrieved from the process registry.    */
	oms_pr_attr_get (interface_ptr->proc_reg_handle,
		"backup DR interface addr", OMSC_PR_ADDRESS, &backup_dr_intf_addr_ptr);

	FRET (*backup_dr_intf_addr_ptr);
	}

List *
ospf_interface_neighbor_list_get (OspfT_Interface *interface_ptr)
	{
	List **			neighbor_list_pptr;

	/** Get the current list of neighbors for the interface. **/
	FIN (ospf_interface_neighbor_list_get (interface_ptr));

	/* The neighbor list is part of the dynamic state of the interface, */
	/* which must be retrieved from the process registry.               */
	oms_pr_attr_get (interface_ptr->proc_reg_handle,
		"neighbor list", OMSC_PR_ADDRESS, &neighbor_list_pptr);

	FRET (*neighbor_list_pptr);
	}

void
ospf_interface_print (OspfT_Interface *interface_ptr)
	{
	char					interface_type_str [128];
	char					ip_address_str [IPC_ADDR_STR_LEN];
	char					subnet_mask_str [IPC_ADDR_STR_LEN];
	char					area_id_str [IPC_ADDR_STR_LEN];
	char					info_str [512];
	OspfT_Interface_State	temp_state;

	/** Print out elements of an interface.  Useful for debugging. **/
	FIN (ospf_interface_print (interface_ptr));

	ip_address_print (ip_address_str, interface_ptr->interface_address);
	ip_address_print (subnet_mask_str, interface_ptr->interface_mask);
	sprintf (info_str, "Interface Information for Address: (%s), Mask: (%s)", ip_address_str,
		subnet_mask_str);
	op_prg_odb_print_minor ("", info_str, OPC_NIL);
	
	temp_state = ospf_interface_type_get (interface_ptr, interface_type_str);
	sprintf (info_str, "Interface Type:       %s", interface_type_str);
	op_prg_odb_print_minor (info_str, OPC_NIL);

	ip_address_print (area_id_str, interface_ptr->area_id);
	sprintf (info_str, "Area ID:              %s", area_id_str);
	op_prg_odb_print_minor (info_str, OPC_NIL);

	sprintf (info_str, "OPNET Process ID:     %d", op_pro_id (interface_ptr->proc_handle));
	op_prg_odb_print_minor (info_str, OPC_NIL);

	sprintf (info_str, "Cost:                 %d", interface_ptr->cost);
	op_prg_odb_print_minor (info_str, OPC_NIL);

	sprintf (info_str, "Router Priority:      %d", interface_ptr->router_priority);
	op_prg_odb_print_minor (info_str, OPC_NIL);

	sprintf (info_str, "Hello Interval:       %f", interface_ptr->hello_interval);
	op_prg_odb_print_minor (info_str, OPC_NIL);

	sprintf (info_str, "Router Dead Interval: %f", interface_ptr->router_dead_interval);
	op_prg_odb_print_minor (info_str, OPC_NIL);

	sprintf (info_str, "Retransmit Interval:  %f", interface_ptr->retransmit_interval);
	op_prg_odb_print_minor (info_str, OPC_NIL);

	sprintf (info_str, "Transmit Delay:       %f", interface_ptr->transmit_delay);
	op_prg_odb_print_minor (info_str, "", OPC_NIL);

	FOUT;
	}

void
ospf_interface_list_print (List *interface_list_ptr)
	{
	int					num_interfaces, interface_index;
	OspfT_Interface *	interface_ptr;
	
	/** Print out the interface list; useful for debugging. **/
	FIN (ospf_interface_list_print (interface_list_ptr));

	/* Loop through the interfaces and print out information on each one. */
	num_interfaces = op_prg_list_size (interface_list_ptr);
	
	for (interface_index = 0; interface_index < num_interfaces; interface_index++)
		{
		interface_ptr = (OspfT_Interface *) op_prg_list_access (interface_list_ptr, interface_index);
		ospf_interface_print (interface_ptr);
		}

	FOUT;
	}

void
ospf_interface_hello_message_send (OspfT_Interface *interface_ptr, Packet *message_ptr)
	{
	static Ici *		ip_encap_ici_ptr = OPC_NIL;
	OspfT_Router *		parent_router_ptr;
	List *				neighbor_list_ptr;
	OspfT_Neighbor *	neighbor_ptr;
	int					num_nbrs, nbr_index;
	char				addr_str [IPC_ADDR_STR_LEN];
	char				trace_str [128];
	double				pk_size;

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

	/* Create the IP encap ICI to be associated with this packet. */
	if (ip_encap_ici_ptr == OPC_NIL)
		ip_encap_ici_ptr = op_ici_create ("ip3_encap_req");

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

		/* Since the destination is multicast, then specify the interface to send on. */
		op_ici_attr_set (ip_encap_ici_ptr, "multicast_interface_address", interface_ptr->interface_address);

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

		/* Get the parent router. */
		parent_router_ptr = ospf_interface_parent_router_get (interface_ptr);

		if (op_prg_odb_ltrace_active ("ospf_message_send"))
			{
			ip_address_print (addr_str, interface_ptr->interface_address);
			sprintf (trace_str, "Hello message multicast on interface %s", addr_str);
			op_prg_odb_print_minor (trace_str, OPC_NIL);
			}

		/* Update the router load statistics. */
		pk_size = (double) op_pk_total_size_get (message_ptr);
		parent_router_ptr->bits_accum += pk_size;
		OspfI_Global_Bits_Accum += pk_size;

		if (op_sim_time () > 0.0)
			{
			op_stat_write (parent_router_ptr->load_stat, parent_router_ptr->bits_accum / op_sim_time ());
			op_stat_write (OspfI_Global_Load_Stat, OspfI_Global_Bits_Accum / op_sim_time ());
			}

		/* Send the message. */
		op_pk_send_forced (message_ptr, parent_router_ptr->ip_outstrm);
		}
	else
		{
		/* With multi-access, non-broadcast interfaces, send unicasts to each neighbor. */
		neighbor_list_ptr = ospf_interface_neighbor_list_get (interface_ptr);
		
		num_nbrs = op_prg_list_size (neighbor_list_ptr);
		for (nbr_index = 0; nbr_index < num_nbrs; nbr_index++)
			{
			neighbor_ptr = op_prg_list_access (neighbor_list_ptr, nbr_index);
			ospf_neighbor_message_send (neighbor_ptr, op_pk_copy (message_ptr), OPC_FALSE);
			}

		if (op_prg_odb_ltrace_active ("ospf_message_send"))
			{
			ip_address_print (addr_str, interface_ptr->interface_address);
			sprintf (trace_str, "Hello message sent as successive unicasts on interface %s", addr_str);
			op_prg_odb_print_minor (trace_str, OPC_NIL);
			}

		/* Since we never sent the original, destroy it. */
		op_pk_destroy (message_ptr);
		}

	FOUT;
	}

void
ospf_interface_message_adj_send (OspfT_Interface *interface_ptr, Packet *message_ptr)
	{
	static Ici *			ip_encap_ici_ptr = OPC_NIL;
	OspfT_Router *			parent_router_ptr;
	OspfT_Interface_State	intf_state;
	List *					neighbor_list_ptr;
	OspfT_Neighbor *		neighbor_ptr;
	int						num_nbrs, nbr_index;
	OspfT_Neighbor_State	nbr_state;
	int						message_type;
	char					addr_str [IPC_ADDR_STR_LEN];
	char					type_str [64];
	char					trace_str [128];
	double					pk_size;

	/** Send an OSPF message over the interface.  On point-to-point or broadcast   **/
	/** links, send a multicast message to all adjacent routers.  On non-broadcast **/
	/** links, send successive unicasts to adjacent routers.  This procedure is    **/
	/** suitable for sending Link State Updates and/or Acknowledgements, but not   **/
	/** Hello messages (since those are sent to all neighbors).                    **/
	FIN (ospf_interface_message_adj_send (interface_ptr, message_ptr));

	if (ip_encap_ici_ptr == OPC_NIL)
		ip_encap_ici_ptr = op_ici_create ("ip3_encap_req");

	intf_state = ospf_interface_state_get (interface_ptr, OPC_NIL);

	if ((interface_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt) ||
		(interface_ptr->interface_type == OspfC_Interface_Type_Broadcast))
		{
		/* The destination depends on the state of the interface. */
		if ((intf_state == OspfC_Interface_State_DR) ||
			(intf_state == OspfC_Interface_State_Backup) ||
			(interface_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt))
			op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", OspfI_All_Spf_Routers);
		else 
			op_ici_attr_set (ip_encap_ici_ptr, "dest_addr", OspfI_All_Dr_Routers);

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

		/* Since the destination is multicast, then specify the interface to send on. */
		op_ici_attr_set (ip_encap_ici_ptr, "multicast_interface_address", interface_ptr->interface_address);

		/* Get the parent router. */
		parent_router_ptr = ospf_interface_parent_router_get (interface_ptr);

		if (op_prg_odb_ltrace_active ("ospf_message_send"))
			{
			ip_address_print (addr_str, interface_ptr->interface_address);
			message_type = ospf_message_type_get (message_ptr, type_str);
			sprintf (trace_str, "%s message multicast on interface %s", type_str, addr_str);
			op_prg_odb_print_minor (trace_str, OPC_NIL);
			}

		/* Update the router load statistics. */
		pk_size = (double) op_pk_total_size_get (message_ptr);
		parent_router_ptr->bits_accum += pk_size;
		OspfI_Global_Bits_Accum += pk_size;

		if (op_sim_time () > 0.0)
			{
			op_stat_write (parent_router_ptr->load_stat, parent_router_ptr->bits_accum / op_sim_time ());
			op_stat_write (OspfI_Global_Load_Stat, OspfI_Global_Bits_Accum / op_sim_time ());
			}

		/* Send the message. */
		op_pk_send_forced (message_ptr, parent_router_ptr->ip_outstrm);
		}
	else
		{
		/* With multi-access, non-broadcast interfaces, send unicasts to each neighbor. */
		neighbor_list_ptr = ospf_interface_neighbor_list_get (interface_ptr);
		
		num_nbrs = op_prg_list_size (neighbor_list_ptr);
		for (nbr_index = 0; nbr_index < num_nbrs; nbr_index++)
			{
			neighbor_ptr = op_prg_list_access (neighbor_list_ptr, nbr_index);
			nbr_state = ospf_neighbor_state_get (neighbor_ptr, OPC_NIL);
			
			if (nbr_state >= OspfC_Neighbor_State_Exchange)
				ospf_neighbor_message_send (neighbor_ptr, op_pk_copy (message_ptr), OPC_FALSE);
			}

		if (op_prg_odb_ltrace_active ("ospf_message_send"))
			{
			ip_address_print (addr_str, interface_ptr->interface_address);
			message_type = ospf_message_type_get (message_ptr, type_str);
			sprintf (trace_str, "%s message sent to adjacencies on interface %s", type_str, addr_str);
			op_prg_odb_print_minor (trace_str, OPC_NIL);
			}

		/* Since we never sent the original, destroy it. */
		op_pk_destroy (message_ptr);
		}
	
	FOUT;
	}

OspfT_Router *
ospf_interface_parent_router_get (OspfT_Interface *interface_ptr)
	{
	/** Return a pointer to the parent router of this interface. */
	FIN (ospf_interface_parent_router_get (interface_ptr));	
	FRET (interface_ptr->parent_area_ptr->parent_router_ptr);
	}

OspfT_Interface *
ospf_interface_list_find (List *interface_list_ptr, IpT_Address intf_addr)
	{
	int						num_interfaces, interface_index;
	OspfT_Interface *		interface_ptr;	

	/** Find an interface with the specified address in the list. **/
	/** Returns OPC_NIL if no interface is found.                 **/
	FIN (ospf_interface_list_find (interface_list_ptr, intf_addr));

	/* Loop through the interfaces and check for a match. */
	num_interfaces = op_prg_list_size (interface_list_ptr);

	for (interface_index = 0; interface_index < num_interfaces; interface_index++)
		{
		/* Get current interface. */
		interface_ptr = op_prg_list_access (interface_list_ptr, interface_index);

		/* Check addresses. */
		if (ip_address_equal (interface_ptr->interface_address, intf_addr))
			FRET (interface_ptr);
		}

	FRET (OPC_NIL);
	}

void
ospf_interface_ev_schedule (OspfT_Interface *interface_ptr, OspfT_Interface_Event interface_event,
	double delta_t)
	{
	/** Schedule an OSPF event to start the interface state machine. **/
	FIN (ospf_interface_ev_schedule (interface_ptr, interface_event, delta_t));

	/* Schedule a process interrupt for the specified time. */
	op_intrpt_schedule_process (interface_ptr->proc_handle, op_sim_time () + delta_t, (int) interface_event);

	FOUT;
	}

OspfT_Neighbor *
ospf_interface_neighbor_find (OspfT_Interface *interface_ptr, IpT_Address addr)
	{
	List *					neighbor_list_ptr;
	int						num_neighbors, neighbor_index;
	OspfT_Neighbor *		neighbor_ptr;

	/** Find a neighbor within a particular interface whose ID or IP address **/
	/** matches addr.  If no neighbor is found, OPC_NIL is returned.         **/
	FIN (ospf_interface_neighbor_find (interface_ptr, addr));

	/* According to RFC 1583, perform the lookup based on Router ID for       */
	/* point-to-point interfaces, and IP address for multi-access interfaces. */
	neighbor_list_ptr = ospf_interface_neighbor_list_get (interface_ptr);

	num_neighbors = op_prg_list_size (neighbor_list_ptr);
	for (neighbor_index = 0; neighbor_index < num_neighbors; neighbor_index++)
		{
		neighbor_ptr = op_prg_list_access (neighbor_list_ptr, neighbor_index);

		/* Compare neighbor, based on type of interface. */
		if (interface_ptr->interface_type == OspfC_Interface_Type_Pt_To_Pt)
			{
			/* Check is based on neighbor ID. */
			if (ip_address_equal (addr, ospf_neighbor_router_id_get (neighbor_ptr)))
				FRET (neighbor_ptr);
			}
		else
			{
			/* Check is based on neighbor's IP address. */
			if (ip_address_equal (addr, neighbor_ptr->ip_address))
				FRET (neighbor_ptr);
			}
		}

	FRET (OPC_NIL);
	}

void
ospf_interface_neighbor_add (OspfT_Interface *intf_ptr, OspfT_Neighbor *neighbor_ptr)
	{
	List *				neighbor_list_ptr;

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

	neighbor_list_ptr = ospf_interface_neighbor_list_get (intf_ptr);

	op_prg_list_insert (neighbor_list_ptr, neighbor_ptr, OPC_LISTPOS_TAIL);
	
	FOUT;
	}	

void
ospf_interface_invoke (OspfT_Interface *interface_ptr, OspfT_Interface_Event interface_event)
	{
	char						interface_addr_str [IPC_ADDR_STR_LEN];
	char						trace_msg [256];
	char						state_str [128], event_str [128];
	OspfT_Interface_State		intf_state;
	
	/** Cover function that will pass an event to the interface state machine. **/
	FIN (ospf_interface_invoke (interface_ptr, interface_event));

	intf_state = ospf_interface_state_get (interface_ptr, state_str);

	if (op_prg_odb_ltrace_active ("ospf_event"))
		{
		ip_address_print (interface_addr_str, interface_ptr->interface_address);
		ospf_interface_event_str_get (interface_event, event_str);

		sprintf (trace_msg, "On interface %s in state %s, invoked event %s", interface_addr_str,
			state_str, event_str);

		op_prg_odb_print_minor (trace_msg, OPC_NIL);
		}

	OspfI_Interface_Trans_Table [(int) intf_state] [(int) interface_event] (interface_ptr, interface_event);

	FOUT;
	}

/***** State/Event handlers *****/
void
ospf_interface_undef_handler (OspfT_Interface *interface_ptr, OspfT_Interface_Event interface_event)
	{
	char					err_msg [256];
	char					state_str [128], event_str [128], interface_addr_str [IPC_ADDR_STR_LEN];
	OspfT_Interface_State	intf_state;

	/** This handles the case where we've encountered an undefined state/event **/
	/** combination.  End the simulation in this case.                         **/
	FIN (ospf_interface_undef_handler (interface_ptr, interface_event));

	intf_state = ospf_interface_state_get (interface_ptr, state_str);
	ospf_interface_event_str_get (interface_event, event_str);
	ip_address_print (interface_addr_str, interface_ptr->interface_address);

	sprintf (err_msg, "Invalid state/event combination: Event %s received in state %s in interface %s",
		event_str, state_str, interface_addr_str);

	ospf_fatal_error (err_msg);

	FOUT;
	}

void
ospf_interface_process_invoke (OspfT_Interface *interface_ptr, OspfT_Interface_Event interface_event)
	{
	OspfT_Interface_Invoke_Info *		interface_info_ptr;

	/** Handles the case where simply invoking the interface process is sufficient. **/
	FIN (ospf_interface_process_invoke (interface_ptr, interface_event));

	/* Set up argument memory associated with the interface process invocation. */
	interface_info_ptr = ospf_interface_invoke_info_create ();
	interface_info_ptr->interface_event = interface_event;

	/* Invoke the process. */
	op_pro_invoke (interface_ptr->proc_handle, interface_info_ptr);

	FOUT;
	}

void
ospf_interface_event_suppress (OspfT_Interface *interface_ptr, OspfT_Interface_Event interface_event)
	{
	/** Handles the case where the event/state combination is defined, but **/
	/** no action is to be taken.  In this case, don't bother invoking     **/
	/** the state machine.                                                 **/
	FIN (ospf_interface_event_suppress (interface_ptr, interface_event));

	/* This function is a no-op. */

	FOUT;
	}

void
ospf_interface_wait_cancel_invoke (OspfT_Interface *interface_ptr, OspfT_Interface_Event interface_event)
	{	
	/** Cancel the wait timer for the interface, and invoke the state machine. **/
	FIN (ospf_interface_wait_cancel_invoke (interface_ptr, interface_event));

	if (op_ev_valid (interface_ptr->wait_timer_handle))
		op_ev_cancel (interface_ptr->wait_timer_handle);

	ospf_interface_process_invoke (interface_ptr, interface_event);

	FOUT;
	}

void
ospf_interface_network_links_lsa_regenerate (OspfT_Interface *intf_ptr, int intrpt_code)
	{
	OspfT_Lsa *					lsa_ptr;
	char						network_addr_str [IPC_ADDR_STR_LEN];
	char						router_id_str [IPC_ADDR_STR_LEN];
	IpT_Address					network_addr;
	char						trace_msg [64];

	/** Handler for MinLSInterval delay timer. **/
	FIN (ospf_interface_network_links_lsa_regenerate (intf_ptr, intrpt_code));

	/* Now create the network links LSA. */
	lsa_ptr = ospf_interface_network_links_lsa_create (intf_ptr, OPC_TRUE);

	if (op_prg_odb_ltrace_active ("ospf_lsa"))
		{
		ip_address_print (router_id_str, intf_ptr->parent_area_ptr->parent_router_ptr->router_id);
		network_addr = ip_address_mask (intf_ptr->interface_address, intf_ptr->interface_mask);
		ip_address_print (network_addr_str, network_addr);
		ip_address_destroy (network_addr);
		if (lsa_ptr != OPC_NIL)
			sprintf (trace_msg, "Router %s regenerates network LSA (%d) for network %s",
				router_id_str, lsa_ptr->lsa_header_ptr->sequence_num, network_addr_str);
		else
			sprintf (trace_msg, "Router %s regenerates network LSA (NIL)", router_id_str);
			
		op_prg_odb_print_minor (trace_msg, OPC_NIL);
		}

	/* Exit if the network links LSA can not be created (possible if this */
	/* router stopped being DR since the timer expired).                  */

	if (lsa_ptr != OPC_NIL)
		{
		/* And install it into the database. */
		ospf_area_lsa_install (intf_ptr->parent_area_ptr, lsa_ptr);
		
		/* And then flood the advertisement. */
		ospf_area_flood (intf_ptr->parent_area_ptr, lsa_ptr, OPC_NIL, OPC_TRUE);
		}

	FOUT;
	}

void
ospf_interface_network_links_originate (OspfT_Interface *intf_ptr)
	{
	OspfT_Lsa *					lsa_ptr;
	char						network_addr_str [IPC_ADDR_STR_LEN];
	char						router_id_str [IPC_ADDR_STR_LEN];
	IpT_Address					network_addr;
	char						trace_msg [64];

	/** Originate a network links advertisement for a given area. **/
	FIN (ospf_interface_network_links_originate (intf_ptr));	

	/* First create a network links. */
	lsa_ptr = ospf_interface_network_links_lsa_create (intf_ptr, OPC_FALSE);

	if (op_prg_odb_ltrace_active ("ospf_lsa"))
		{
		ip_address_print (router_id_str, intf_ptr->parent_area_ptr->parent_router_ptr->router_id);
		network_addr = ip_address_mask (intf_ptr->interface_address, intf_ptr->interface_mask);
		ip_address_print (network_addr_str, network_addr);
		ip_address_destroy (network_addr);
		
		if (lsa_ptr == OPC_NIL)
			sprintf (trace_msg, "Router %s originates network LSA (NIL) for network %s",
				router_id_str, network_addr_str);
		else
			sprintf (trace_msg, "Router %s originates network LSA (%d) for network %s",
				router_id_str, lsa_ptr->lsa_header_ptr->sequence_num, network_addr_str);

		op_prg_odb_print_minor (trace_msg, OPC_NIL);
		}

	/* If we didn't create one, assume that there was */
	/* a good reason for not doing so.                */
	if (lsa_ptr != OPC_NIL)
		{
		/* If we have one, then install it. */
		ospf_area_lsa_install (intf_ptr->parent_area_ptr, lsa_ptr);	

		/* And then flood the advertisement. */
		ospf_area_flood (intf_ptr->parent_area_ptr, lsa_ptr, OPC_NIL, OPC_TRUE);
		}
	else if (op_prg_odb_ltrace_active ("ospf_lsa"))
		op_prg_odb_print_minor ("Network LSA installation suppressed.", OPC_NIL);

	FOUT;
	}

OspfT_Lsa *
ospf_interface_network_links_lsa_create (OspfT_Interface *intf_ptr, Boolean override_timer_check)
	{
	List *						nbr_list_ptr;
	OspfT_Lsa_Header *			lsa_header_ptr;
	OspfT_Lsa *					lsa_ptr;
	int							num_nbrs, nbr_index;
	OspfT_Lsa_Network_Links *	network_links_ptr;
	OspfT_Neighbor *			neighbor_ptr;
	OspfT_Lsa *					inst_lsa_ptr;

	/** Create a network links advertisement.  Should only be called with an interface   **/
	/** that is the designated router for its attached network, and it is fully adjacent **/
	/** to at least one other router.                                                    **/
	FIN (ospf_interface_network_links_lsa_create (intf_ptr, override_timer_check));

	/* Before creating an LSA, see if we have an instance of it in the database. */
	inst_lsa_ptr = ospf_lsa_area_lookup (intf_ptr->parent_area_ptr, 
		intf_ptr->interface_address,
		intf_ptr->parent_area_ptr->parent_router_ptr->router_id, 
		OspfC_Lsa_Network_Links);

	/* If we found an installed LSA, then see if it's been long */
	/* enough (MinLSInterval) since the last replacement.       */
	/* Do not make this check if we are called from the         */
	/* handler for the MinLSInterval timer.                     */
	if ((inst_lsa_ptr != OPC_NIL) && !override_timer_check)
		{
		if ((op_sim_time () - inst_lsa_ptr->timestamp) < OSPFC_MIN_LS_INTERVAL)
			{
			if (!inst_lsa_ptr->replace)
				{
				/* We found an instance of the advertisement in the database, but */
				/* it hasn't been long enough since the last installation.  Set a */
				/* timer to regenerate an advertisement.                          */
				inst_lsa_ptr->replace = OPC_TRUE;
				op_intrpt_schedule_call (inst_lsa_ptr->timestamp + OSPFC_MIN_LS_INTERVAL,
					0, ospf_interface_network_links_lsa_regenerate, intf_ptr);
				}
			
			FRET (OPC_NIL);
			}
		}

	/* Get the list of neighbors. */
	nbr_list_ptr = ospf_interface_neighbor_list_get (intf_ptr);

	/* First check the interface to see if it meets the requirements */
	/* for originating a network links advertisement.                */
	if ((ospf_interface_state_get (intf_ptr, OPC_NIL) != OspfC_Interface_State_DR) ||
		(ospf_neighbor_list_num_states_match (nbr_list_ptr,	OspfC_Neighbor_State_Full) < 1))
		{
		/* Have not met requirements to originate LSA.  Return NIL. */
		FRET (OPC_NIL);
		}

	/* Create the LSA header. */
	lsa_header_ptr = ospf_lsa_header_create (OspfC_Lsa_Network_Links, 
		intf_ptr->parent_area_ptr->parent_router_ptr->router_id, intf_ptr->interface_address);
	
	/* Create the LSA from the header. */
	lsa_ptr = ospf_lsa_create (lsa_header_ptr);

	/* Create a reference to the network links portion. */
	network_links_ptr = lsa_ptr->lsa_data.lsa_network_links_ptr;

	/* Add the network mask. */
	network_links_ptr->network_mask = ip_address_copy (intf_ptr->interface_mask);

	/* Add the router IDs of the routers also attached to this network. */
	num_nbrs = op_prg_list_size (nbr_list_ptr);
	for (nbr_index = 0; nbr_index < num_nbrs; nbr_index++)
		{
		neighbor_ptr = op_prg_list_access (nbr_list_ptr, nbr_index);
		
		if (ospf_neighbor_state_get (neighbor_ptr, OPC_NIL) == OspfC_Neighbor_State_Full)
			{
			/* Add this router ID. */
			op_prg_list_insert (network_links_ptr->router_list_ptr,
				ip_address_copy (ospf_neighbor_router_id_get (neighbor_ptr)),
				OPC_LISTPOS_TAIL);
			}
		}
	
	FRET (lsa_ptr);
	}
