/* Procedures for dealing with OSPF areas. */

#include <opnet.h>

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

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

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

/* OSPF specific header files */
#include "ospf_defs.h"
#include "ospf_const.h"
#include "ospf_lsa.h"
#include "ospf_interface.h"
#include "ospf_neighbor.h"
#include "ospf_msg.h"
#include "ospf_area.h"
#include "ospf_rte_table.h"

/***** Procedures *****/
OspfT_Area *
ospf_area_create (void)
	{
	OspfT_Area *		area_ptr;

	/** Allocate and initialize an OSPF area object. **/
	FIN (ospf_area_create (void));

	/* Allocate memory for area object. */
	area_ptr = op_prg_mem_alloc (sizeof (OspfT_Area));

	/* Initialize elements of area object. */
	area_ptr->area_id = OSPFC_AREA_ID_INVALID;
	area_ptr->router_lsa_list_ptr = op_prg_list_create ();
	area_ptr->network_lsa_list_ptr = op_prg_list_create ();
	area_ptr->summary_lsa_list_ptr = op_prg_list_create ();
	area_ptr->interface_list_ptr = op_prg_list_create ();
	area_ptr->address_list_ptr = op_prg_list_create ();
	area_ptr->hidden_addr_list_ptr = op_prg_list_create ();

	FRET (area_ptr);
	}

OspfT_Area_Range *
ospf_area_range_create (void)
	{
	OspfT_Area_Range *			area_range_ptr;

	/** Allocate and initialize an OSPF area range object. **/
	FIN (ospf_area_range_create (void));	

	/* Allocate memory for object. */
	area_range_ptr = op_prg_mem_alloc (sizeof (OspfT_Area_Range));
	
	/* Initialize elements. */
	area_range_ptr->addr_range_ptr = OPC_NIL;
	area_range_ptr->active = OPC_FALSE;

	FRET (area_range_ptr);
	}

List *
ospf_area_list_get (OspfT_Router *router_ptr)
	{
	List **				area_list_pptr;
	Compcode			status;

	/** Get the list of areas for a given router. **/
	FIN (ospf_area_list_get (router_ptr));

	/* The area list is stored in the router's process registry. */
	status = oms_pr_attr_get (router_ptr->proc_reg_handle,
		"area list", OMSC_PR_ADDRESS, &area_list_pptr);

	if (status == OPC_COMPCODE_FAILURE)
		{
		FRET (OPC_NIL);
		}

	FRET (*area_list_pptr);
	}

OspfT_Area *
ospf_area_backbone_get (OspfT_Router *router_ptr)
	{
	OspfT_Area **		backbone_area_pptr;
	Compcode			status;

	/** Return a pointer to the backbone area as seen by the given router. **/
	FIN (ospf_area_backbone_get (router_ptr));

	/* The backbone area is stored in the router's process registry. */
	oms_pr_attr_get (router_ptr->proc_reg_handle,
		"backbone area", OMSC_PR_ADDRESS, &backbone_area_pptr);

	if (status == OPC_COMPCODE_FAILURE)
		{
		FRET (OPC_NIL);
		}

	FRET (*backbone_area_pptr);
	}

Boolean
ospf_area_border_router_check (OspfT_Router *router_ptr)
	{
	List *					area_list_ptr;
	char					trace_msg [128];
	char					router_id_str [IPC_ADDR_STR_LEN];

	/** Return OPC_TRUE if router is an area border router, OPC_FALSE if not. **/
	/** If the router is connected to multiple areas, but not the backbone,   **/
	/** then this procedure issues a warning message.                         **/
	FIN (ospf_area_border_router_check (router_ptr));
	
	/* Get the list of areas connected to this router. */
	area_list_ptr = ospf_area_list_get (router_ptr);

	/* Exit early if area list is not completely set up yet. */
	if (area_list_ptr == OPC_NIL)
		{
		FRET (OPC_FALSE);
		}

	if (op_prg_list_size (area_list_ptr) > 1)
		{
		/* Router is connected to multiple areas. */
		if (ospf_area_backbone_get (router_ptr) == OPC_NIL)
			{
			ip_address_print (router_id_str, router_ptr->router_id);
			sprintf (trace_msg, "Router %s is attached to multiple areas, but not the backbone",
				router_id_str);
			ospf_warn_error (trace_msg);
			FRET (OPC_FALSE);
			}
		else
			{
			FRET (OPC_TRUE);
			}
		}

	FRET (OPC_FALSE);
	}

void
ospf_area_list_summary_print (List *area_list_ptr)
	{	
	OspfT_Area *		area_ptr;
	char				area_id_str [IPC_ADDR_STR_LEN];
	int					num_areas, area_index;
	int					num_interfaces, num_networks, num_routers;
	char				area_stat_str [512];

	/** Print summary statistics about the areas connected to this router. **/
	/** This report is similar to the one described in D.2.1 in RFC 1247.  **/
	FIN (ospf_area_list_summary_print (area_list_ptr));

	/* Print header. */
	op_prg_odb_print_minor ("", "Area ID\t\t# Interfaces\t# Networks\t# Routers", OPC_NIL);
   	op_prg_odb_print_minor ("-------\t\t------------\t----------\t---------", OPC_NIL);

	/* Loop through the areas and print out summary lines for each one. */
	num_areas = op_prg_list_size (area_list_ptr);
	
	for (area_index = 0; area_index < num_areas; area_index++)
		{
		area_ptr = (OspfT_Area *) op_prg_list_access (area_list_ptr, area_index);
		
		/* Create area ID string. */
		ip_address_print (area_id_str, area_ptr->area_id);

		num_interfaces = op_prg_list_size (area_ptr->interface_list_ptr);
		num_networks = op_prg_list_size (area_ptr->network_lsa_list_ptr);
		num_routers = op_prg_list_size (area_ptr->router_lsa_list_ptr);

		sprintf (area_stat_str, "%s\t\t   %d\t\t   %d\t\t    %d", area_id_str, num_interfaces,
			num_networks, num_routers);

		op_prg_odb_print_minor (area_stat_str, OPC_NIL);
		}

	FOUT;
	}

void
ospf_area_interfaces_print (OspfT_Area *area_ptr)
	{
	List *					interface_list_ptr;
	int						num_interfaces, interface_index;
	char					area_id_str [IPC_ADDR_STR_LEN];
	char					area_title_str [128];
	OspfT_Interface *		interface_ptr;
	char					ip_address_str [IPC_ADDR_STR_LEN];
	char					dr_str [IPC_ADDR_STR_LEN];
	char					backup_dr_str [IPC_ADDR_STR_LEN];
	char					interface_state_str [32];
	OspfT_Interface_State	interface_state;
	OspfT_Router_Id			designated_router;
	OspfT_Router_Id			backup_designated_router;
	List *					neighbor_list_ptr;
	int						num_neighbors;
	char					interface_str [512];

	/** Print out the interfaces connected to the area. **/
	FIN (ospf_area_interfaces_print (area_ptr));

	/* Create a reference to the interface list. */
	interface_list_ptr = area_ptr->interface_list_ptr;

	/* Print table header. */
	ip_address_print (area_id_str, area_ptr->area_id);
	sprintf (area_title_str, "[Area ID: (%s)]", area_id_str);
	op_prg_odb_print_minor ("", area_title_str, OPC_NIL);
	op_prg_odb_print_minor ("Intf IP Address\t   State\tCost\tDes. Router\tBackup DR\t# Neighbors", 
		OPC_NIL);
	op_prg_odb_print_minor ("---------------\t   -----\t----\t-----------\t---------\t-----------", 
		OPC_NIL);

	/* Loop through interfaces and print each one. */
	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);
		
		/* Get string representation of interface address. */
		ip_address_print (ip_address_str, interface_ptr->interface_address);

		/* Get interface state. */
		interface_state = ospf_interface_state_get (interface_ptr, interface_state_str);

		/* Get the designated router and the backup designated router. */
		designated_router = ospf_interface_dr_get (interface_ptr);
		backup_designated_router = ospf_interface_backup_dr_get (interface_ptr);
		ip_address_print (dr_str, designated_router);
		ip_address_print (backup_dr_str, backup_designated_router);

		/* Get the neighbor list. */
		neighbor_list_ptr = ospf_interface_neighbor_list_get (interface_ptr);

		/* We're only interested in the size. */
		num_neighbors = op_prg_list_size (neighbor_list_ptr);

		/* Create the interface information string. */
		sprintf (interface_str, "%-15s\t   %-10s\t%d\t%-15s\t%-15s\t     %d", 
			ip_address_str, interface_state_str, interface_ptr->cost, dr_str, backup_dr_str, 
			num_neighbors);

		/* Print the information string. */
		op_prg_odb_print_minor (interface_str, OPC_NIL);
		}

	FOUT;
	}

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

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

	/* Loop through the areas and print out an interface table for each one. */
	num_areas = op_prg_list_size (area_list_ptr);
	
	for (area_index = 0; area_index < num_areas; area_index++)
		{
		/* Get current area. */
		area_ptr = (OspfT_Area *) op_prg_list_access (area_list_ptr, area_index);

		/* Print interface report for area. */
		if (detailed)
			ospf_interface_list_print (area_ptr->interface_list_ptr);
		else
			ospf_area_interfaces_print (area_ptr);
		}
	
	FOUT;
	}

OspfT_Area *
ospf_area_lookup (List *area_list_ptr, OspfT_Area_Id area_id)
	{
	OspfT_Area *		area_ptr;
	int					num_areas, area_index;

	/** Lookup an area based on the area ID.  If no area of the given **/
	/** area ID exists, then return OPC_NIL.                          **/
	FIN (ospf_area_lookup (area_list_ptr, area_id));

	/* Loop through list of areas, and try to find a match. */
	num_areas = op_prg_list_size (area_list_ptr);

	for (area_index = 0; area_index < num_areas; area_index++)
		{
		area_ptr = op_prg_list_access (area_list_ptr, area_index);
		
		/* Check the area id for a match. */
		if (ip_address_equal (area_id, area_ptr->area_id))
			FRET (area_ptr);
		}

	FRET (OPC_NIL);
	}

void
ospf_area_lsa_install (OspfT_Area *area_ptr, OspfT_Lsa *lsa_ptr)
	{
	char				err_msg [128];
	OspfT_Lsa *			inst_lsa_ptr;
	IpT_Address			dest_net_addr;
	OspfT_Rte_Entry *	entry_ptr;

	/** Install an LSA into an area's link state database. **/
	FIN (ospf_area_lsa_install (area_ptr, lsa_ptr));

	switch (lsa_ptr->lsa_header_ptr->type)
		{
		case OspfC_Lsa_Router_Links:
			{
			/* Check for existence first. */
			inst_lsa_ptr = ospf_lsa_lookup (area_ptr->router_lsa_list_ptr, 
				lsa_ptr->lsa_header_ptr->link_state_id,	lsa_ptr->lsa_header_ptr->router_id,
				lsa_ptr->lsa_header_ptr->type);
			
			if (inst_lsa_ptr != OPC_NIL)
				ospf_lsa_remove (area_ptr->router_lsa_list_ptr, inst_lsa_ptr);

			op_prg_list_insert (area_ptr->router_lsa_list_ptr, lsa_ptr, OPC_LISTPOS_TAIL);

			/* Installation means re-calculating the routing table. */
			area_ptr->parent_router_ptr->rte_table_ptr->dirty_flag = OPC_TRUE;
			break;
			}

		case OspfC_Lsa_Network_Links:
			{
			/* Check for existence first. */
			inst_lsa_ptr = ospf_lsa_lookup (area_ptr->network_lsa_list_ptr, 
				lsa_ptr->lsa_header_ptr->link_state_id,	lsa_ptr->lsa_header_ptr->router_id,
				lsa_ptr->lsa_header_ptr->type);
			
			if (inst_lsa_ptr != OPC_NIL)
				ospf_lsa_remove (area_ptr->network_lsa_list_ptr, inst_lsa_ptr);

			op_prg_list_insert (area_ptr->network_lsa_list_ptr, lsa_ptr, OPC_LISTPOS_TAIL);

			/* Installation means re-calculating the routing table. */
			area_ptr->parent_router_ptr->rte_table_ptr->dirty_flag = OPC_TRUE;			
			break;
			}

		case OspfC_Lsa_Summary_Links:
			{
			/* Check for existence first. */
			inst_lsa_ptr = ospf_lsa_lookup (area_ptr->summary_lsa_list_ptr, 
				lsa_ptr->lsa_header_ptr->link_state_id,	lsa_ptr->lsa_header_ptr->router_id,
				lsa_ptr->lsa_header_ptr->type);
			
			if (inst_lsa_ptr != OPC_NIL)
				ospf_lsa_remove (area_ptr->summary_lsa_list_ptr, inst_lsa_ptr);

			op_prg_list_insert (area_ptr->summary_lsa_list_ptr, lsa_ptr, OPC_LISTPOS_TAIL);

			/* Determine if this advertisement necessitates a change in the routing table. */
			/* If a change is necessary, add the inter-area route directly, without        */
			/* scheduling a routing table change.  Ignore a MaxAge advertisement since the */
			/* intent is to eventually flush the advertisement.                            */
			if (lsa_ptr->lsa_header_ptr->age != OSPFC_MAX_AGE)
				ospf_rte_table_inter_area_rtes_add (area_ptr->parent_router_ptr,
					lsa_ptr->lsa_header_ptr->link_state_id, area_ptr);
			break;
			}

		default:
			{
			/* This situation should never occur. */
			sprintf (err_msg, "Trying to install unknown LSA type: %d", lsa_ptr->lsa_header_ptr->type);
			ospf_fatal_error (err_msg);
			break;
			}
		}

	FOUT;
	}

List *
ospf_area_summary_lsa_generate (List *area_list_ptr, OspfT_Area_Id exclude_area_id, 
	OspfT_Router_Id router_id)
	{
	int							num_areas, area_index;
	OspfT_Area *				area_ptr;
	OspfT_Area_Range *			area_range_ptr;
	OspfT_Lsa_Header *			lsa_header_ptr;
	OspfT_Lsa *					lsa_ptr;
	int							num_ranges, range_index;
	IpT_Address_Range *			addr_range_ptr;
	IpT_Address					dest_net_addr;
	List *						area_lsa_list_ptr;

	/** Generate a list of summary LSAs for the address ranges in the given areas. **/
	/** Do not include the area specified by the area ID.                          **/
	FIN (ospf_area_summary_lsa_generate (area_list_ptr, exclude_area_id, router_id));

	num_areas = op_prg_list_size (area_list_ptr);
	area_lsa_list_ptr = op_prg_list_create ();
	for (area_index = 0; area_index < num_areas; area_index++)
		{
		area_ptr = op_prg_list_access (area_list_ptr, area_index);

		if (ip_address_equal (area_ptr->area_id, exclude_area_id))
			continue;

		/* Generate the list of advertisements that represent the address ranges */
		/* that this area can advertise.                                         */
		num_ranges = op_prg_list_size (area_ptr->address_list_ptr);
		for (range_index = 0; range_index < num_ranges; range_index++)
			{
			area_range_ptr = op_prg_list_access (area_ptr->address_list_ptr, range_index);
			addr_range_ptr = area_range_ptr->addr_range_ptr;
			dest_net_addr = ip_address_mask (addr_range_ptr->address, addr_range_ptr->subnet_mask);
			lsa_header_ptr = ospf_lsa_header_create (OspfC_Lsa_Summary_Links, router_id,
				dest_net_addr);
			ip_address_destroy (dest_net_addr);

			/* Create the summary LSA that represents the range.  Set the cost initially */
			/* to 0, but update it as we find the component networks in this range.      */
			lsa_ptr = ospf_lsa_create (lsa_header_ptr);
			lsa_ptr->lsa_data.lsa_summary_links_ptr->network_mask = 
				ip_address_copy (addr_range_ptr->subnet_mask);
			lsa_ptr->lsa_data.lsa_summary_links_ptr->network_cost = 0;

			/* Add this LSA to the list of advertisements to flood. */
			op_prg_list_insert (area_lsa_list_ptr, lsa_ptr, OPC_LISTPOS_TAIL);
			}
		}

	FRET (area_lsa_list_ptr);
	}

Boolean
ospf_area_flood (OspfT_Area *area_ptr, OspfT_Lsa *lsa_ptr, OspfT_Neighbor *rcvd_nbr_ptr, Boolean self_orig)
	{
	OspfT_Lsa_Header *		lsa_header_ptr;
	int						num_intfs, intf_index;
	OspfT_Interface *		intf_ptr;
	OspfT_Interface_State	intf_state;
	int						num_nbrs, nbr_index;
	OspfT_Neighbor *		nbr_ptr;
	List *					nbr_list_ptr;
	OspfT_Neighbor_State	nbr_state;
	List *					nbr_ls_req_list_ptr;
	OspfT_Lsa_Header *		lsa_header_req_ptr;
	int						compare_status;
	Boolean					rxmt_added;
	Packet *				ls_update_msg_ptr;
	Boolean					flood_intf_received = OPC_FALSE;
	char					trace_msg [128];
	char					intf_addr_str [IPC_ADDR_STR_LEN];
	Packet *				lsr_message_ptr;
	OspfT_Lsa_Header *		new_lsa_header_ptr;

	/** Flood an LSA out to an area.  Returns OPC_TRUE if advertisement was  **/
	/** flooded back on receiving interface, OPC_FALSE if not.  This info    **/
	/** is used by the client to determine whether or not this advertisement **/
	/** should be acknowledged.                                              **/
	FIN (ospf_area_flood (area_ptr, lsa_ptr, rcvd_nbr_ptr, self_orig));

	/* Create reference to the header. */
	lsa_header_ptr = lsa_ptr->lsa_header_ptr;

	/* Loop through the interfaces in the area, and determine whether */
	/* the LSA should be sent on each interface.                      */
	num_intfs = op_prg_list_size (area_ptr->interface_list_ptr);
	for (intf_index = 0; intf_index < num_intfs; intf_index++)
		{
		intf_ptr = op_prg_list_access (area_ptr->interface_list_ptr, intf_index);

		intf_state = ospf_interface_state_get (intf_ptr, OPC_NIL);

		/* Examine all neighbors in the interface. */
		nbr_list_ptr = ospf_interface_neighbor_list_get (intf_ptr);
		num_nbrs = op_prg_list_size (nbr_list_ptr);
		rxmt_added = OPC_FALSE;
		for (nbr_index = 0; nbr_index < num_nbrs; nbr_index++)
			{
			nbr_ptr = op_prg_list_access (nbr_list_ptr, nbr_index);

			nbr_state = ospf_neighbor_state_get (nbr_ptr, OPC_NIL);
			nbr_ls_req_list_ptr = ospf_neighbor_ls_request_list_get (nbr_ptr);

			/* If the neighbor is in a lesser state than 'Exchange', examine the next neighbor. */
			if (nbr_state < OspfC_Neighbor_State_Exchange)
				continue;

			/* See if a request exists. */
			lsa_header_req_ptr = ospf_lsa_header_lookup (nbr_ls_req_list_ptr, lsa_header_ptr->link_state_id,
				lsa_header_ptr->router_id, lsa_header_ptr->type);

			/* If the adjacency is not yet full, then see if this matches the request. */
			if (((nbr_state == OspfC_Neighbor_State_Exchange) ||
				(nbr_state == OspfC_Neighbor_State_Loading)) &&
				(lsa_header_req_ptr != OPC_NIL))
				{
				/* Compare the requests. */
				compare_status = ospf_lsa_compare (lsa_header_ptr, lsa_header_req_ptr);

				if (compare_status == -1)
					{
					/* New LSA is less recent, so ignore it. */
					continue;
					}
				else 
					{
					/* Same or newer instance.  Delete from Link State Request list */
					/* and go on to next neighbor.                                  */
					ospf_lsa_header_remove (nbr_ls_req_list_ptr, lsa_header_req_ptr);

					/* Did this update fufill an outstanding request? */
					if (lsa_header_req_ptr == nbr_ptr->pending_req_ptr)
						{
						/* Cancel the retransmission timer. */
						op_ev_cancel (nbr_ptr->rxmt_timer);

						/* Send another request, if available. */
						if (op_prg_list_size (nbr_ls_req_list_ptr) > 0)
							{
							lsr_message_ptr = ospf_message_lsr_create (nbr_ptr);
							new_lsa_header_ptr = op_prg_list_access (nbr_ls_req_list_ptr, OPC_LISTPOS_HEAD);
							ospf_message_ls_request_add (lsr_message_ptr, new_lsa_header_ptr);
							ospf_neighbor_message_send (nbr_ptr, lsr_message_ptr, OPC_TRUE);

							/* Log the pending request */
							nbr_ptr->pending_req_ptr = new_lsa_header_ptr;
							}
						}

					if (compare_status == 0)
						continue;
					}
				}

			/* Did the advertisement come from this neighbor? */
			if (!self_orig && (rcvd_nbr_ptr == nbr_ptr))
				continue;

			/* At this point, we can't be sure the neighbor has a copy of the */
			/* LSA.  Place the LSA on the Link State Retransmission list for  */
			/* this adjacency.                                                */
			ospf_neighbor_lsa_rxmt_add (nbr_ptr, lsa_ptr);
			rxmt_added = OPC_TRUE;
			}

		/* Before moving on, make sure neighbors on this interface */
		/* generate LoadingDone if we've removed the last request  */
		/* on the link Link State Request list.                    */
		for (nbr_index = 0; nbr_index < num_nbrs; nbr_index++)
			{
			nbr_ptr = op_prg_list_access (nbr_list_ptr, nbr_index);
			nbr_state = ospf_neighbor_state_get (nbr_ptr, OPC_NIL);
			
			if ((nbr_state == OspfC_Neighbor_State_Loading) &&
				(op_prg_list_size (ospf_neighbor_ls_request_list_get (nbr_ptr)) == 0))
				{
				/* Cancel any outstanding Link State Requests. */
				if (op_ev_valid (nbr_ptr->rxmt_timer))
					op_ev_cancel (nbr_ptr->rxmt_timer);

 				ospf_neighbor_invoke (nbr_ptr, OspfC_Neighbor_Event_Load_Done);
				}
			}

		/* If we didn't add this LSA to any retransmission list, examine the next interface. */
		if (!rxmt_added)
			continue;

		/* Was the LSA received on this interface? */
		if (!self_orig && (intf_ptr == rcvd_nbr_ptr->parent_interface_ptr))
			{
			/* Did the LSA come from the DR?  If so, then it's likely neighbors */
			/* have already received the LSA, so examine the next interface.    */
			if (!self_orig &&
				ip_address_equal (ospf_interface_dr_get (rcvd_nbr_ptr->parent_interface_ptr),
				ospf_neighbor_router_id_get (rcvd_nbr_ptr)))
				continue;

			/* If this interface is the backup DR, examine the next interface. */
			/* The designated router will flood the LSA.                       */
			if (intf_state == OspfC_Interface_State_Backup)
				continue;

			/* The LSA will be flooded back on the receiving interface. */
			/* The client of this procedure needs to know this.         */
			flood_intf_received = OPC_TRUE;
			}

		/* At this point, we know the LSA should be flooded on the interface. */
		if (self_orig)
			{
			ls_update_msg_ptr = ospf_message_lsu_create (OPC_NIL);
			
			/* Since we received the LSA from this router, init the header ourselves. */
			ospf_message_header_init (ls_update_msg_ptr, area_ptr->parent_router_ptr->router_id,
				area_ptr->area_id);
			}
		else
			ls_update_msg_ptr = ospf_message_lsu_create (rcvd_nbr_ptr);
	
		/* Age the advertisement. */
		if (lsa_header_ptr->age != OSPFC_MAX_AGE)
			lsa_header_ptr->age += intf_ptr->transmit_delay;

		ospf_message_lsa_add (ls_update_msg_ptr, lsa_ptr);

		/* Send the Link State Update. */
		ospf_interface_message_adj_send (intf_ptr, ls_update_msg_ptr);

		/* Issue trace message. */
		if (op_prg_odb_ltrace_active ("ospf_flood"))
			{
			ip_address_print (intf_addr_str, intf_ptr->interface_address);
			sprintf (trace_msg, "LSA (%d) flooded on interface %s", lsa_ptr->lsa_header_ptr->sequence_num,
				intf_addr_str);
			op_prg_odb_print_minor (trace_msg, OPC_NIL);
			}
		}

	FRET (flood_intf_received);
	}

void
ospf_area_router_links_lsa_regenerate (OspfT_Area *area_ptr, int intrpt_code)
	{
	OspfT_Lsa *					lsa_ptr;
	char						area_id_str [IPC_ADDR_STR_LEN];
	char						router_id_str [IPC_ADDR_STR_LEN];
	char						trace_msg [64];

	/** Handler for MinLSInterval delay timer. **/
	FIN (ospf_area_router_links_lsa_regenerate (area_ptr, intrpt_code));

	/* Now create the router links LSA. */
	lsa_ptr = ospf_area_router_links_lsa_create (area_ptr, OPC_TRUE);

	if (op_prg_odb_ltrace_active ("ospf_lsa"))
		{
		ip_address_print (router_id_str, area_ptr->parent_router_ptr->router_id);
		ip_address_print (area_id_str, area_ptr->area_id);
		sprintf (trace_msg, "Regenerating router links LSA (%d) in area %s for router %s", 
			lsa_ptr->lsa_header_ptr->sequence_num, area_id_str, router_id_str);
		op_prg_odb_print_minor (trace_msg, OPC_NIL);
		}

	/* And install it into the database. */
	ospf_area_lsa_install (area_ptr, lsa_ptr);

	/* And then flood the advertisement. */
	ospf_area_flood (area_ptr, lsa_ptr, OPC_NIL, OPC_TRUE);

	FOUT;
	}

void
ospf_area_router_links_originate (OspfT_Area *area_ptr)
	{
	OspfT_Lsa *					lsa_ptr;
	char						area_id_str [IPC_ADDR_STR_LEN];
	char						router_id_str [IPC_ADDR_STR_LEN];
	char						trace_msg [64];

	/** Originate a router links advertisement for a given area. **/
	FIN (ospf_area_router_links_originate (area_ptr));	

	/* First create a router links. */
	lsa_ptr = ospf_area_router_links_lsa_create (area_ptr, OPC_FALSE);

	if (op_prg_odb_ltrace_active ("ospf_lsa"))
		{
		ip_address_print (router_id_str, area_ptr->parent_router_ptr->router_id);
		ip_address_print (area_id_str, area_ptr->area_id);
		if (lsa_ptr == OPC_NIL)
			sprintf (trace_msg, "Originating router links LSA (NIL) in area %s for router %s", 
				area_id_str, router_id_str);
		else
			sprintf (trace_msg, "Originating router links LSA (%d) in area %s for router %s", 
				lsa_ptr->lsa_header_ptr->sequence_num, area_id_str, router_id_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 (area_ptr, lsa_ptr);

		/* And then flood the advertisement. */
		ospf_area_flood (area_ptr, lsa_ptr, OPC_NIL, OPC_TRUE);
		}
	else if (op_prg_odb_ltrace_active ("ospf_lsa"))
		op_prg_odb_print_minor ("Router Links LSA installation suppressed.", OPC_NIL);

	FOUT;
	}

OspfT_Lsa *
ospf_area_router_links_lsa_create (OspfT_Area *area_ptr, Boolean override_timer_check)
	{
	OspfT_Lsa *						lsa_ptr;
	OspfT_Lsa_Header *				lsa_header_ptr;
	int								num_intfs, intf_index;
	OspfT_Interface *				intf_ptr;
	List *							nbr_list_ptr;
	OspfT_Neighbor *				neighbor_ptr;
	OspfT_Lsa_Router_Links_Info *	link_info_ptr;
	OspfT_Lsa *						inst_lsa_ptr;
	OspfT_Interface_State			intf_state;

	/** Create a router links advertisement for the given area. **/
	/** Corresponds to section 12.4.1 in RFC 1583.              **/
	FIN (ospf_area_router_links_lsa_create (area_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 (area_ptr, area_ptr->parent_router_ptr->router_id,
		area_ptr->parent_router_ptr->router_id, OspfC_Lsa_Router_Links);

	/* If we found an installed LSA, then see if it's been long */
	/* enough (MinLSInterval) since the last replacement.       */
	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_area_router_links_lsa_regenerate, area_ptr);
				}
			
			FRET (OPC_NIL);
			}
		}

	/* First create the LSA header. */
	lsa_header_ptr = ospf_lsa_header_create (OspfC_Lsa_Router_Links, area_ptr->parent_router_ptr->router_id,
		area_ptr->parent_router_ptr->router_id);

	/* Then create the LSA from the header. */
	lsa_ptr = ospf_lsa_create (lsa_header_ptr);

	/* Indicate whether or not this is an area border router. */
	if (ospf_area_border_router_check (area_ptr->parent_router_ptr))
		lsa_ptr->lsa_data.lsa_router_links_ptr->is_abr = OPC_TRUE;

	/* Loop through the interfaces and construct an advertisement according to the interface. */
	num_intfs = op_prg_list_size (area_ptr->interface_list_ptr);

	for (intf_index = 0; intf_index < num_intfs; intf_index++)
		{
		intf_ptr = op_prg_list_access (area_ptr->interface_list_ptr, intf_index);
		
		/* Get state of interface. */
		intf_state = ospf_interface_state_get (intf_ptr, OPC_NIL);
		nbr_list_ptr = ospf_interface_neighbor_list_get (intf_ptr);

		/* If the interface is in the Point-to-Point state, add a link depending */
		/* on the state of the router's conversation with the neighbor.          */
		if (intf_state == OspfC_Interface_State_Pt_To_Pt)
			{
			/* For a fully adjacent neighbor connection, add a type 1 or 3 link. */
			/* There should only be one neighbor on this type of interface.      */
			if (op_prg_list_size (nbr_list_ptr) > 1)
				{
				/* This shouldn't happen, so give a fatal error. */
				ospf_fatal_error ("Encountered more than one neighbor on a point-to-point interface");
				}
			else if (op_prg_list_size (nbr_list_ptr) == 0)
				{
				/* No neighbors yet, so just look at the next interface. */
				continue;
				}

			neighbor_ptr = op_prg_list_access (nbr_list_ptr, OPC_LISTPOS_HEAD);
			
			if (ospf_neighbor_state_get (neighbor_ptr, OPC_NIL) == OspfC_Neighbor_State_Full)
				{
				/* The adjacency is fully developed, so add a Type 1 (Point-to-point) link. */
				link_info_ptr = ospf_lsa_router_links_info_create ();
				link_info_ptr->link_type = OspfC_Lsa_Router_Link_Point_To_Point;
				link_info_ptr->link_id = ip_address_copy (ospf_neighbor_router_id_get (neighbor_ptr));
				link_info_ptr->link_data = ip_address_copy (intf_ptr->interface_address);
				link_info_ptr->link_cost = intf_ptr->cost;
				}
			else
				{
				/* The adjacency is not fully developed.  If we know the neighbor's IP */
				/* address, then add a type 3 link (stub network).                     */
				if (neighbor_ptr->ip_address != OPC_NIL)
					{
					link_info_ptr = ospf_lsa_router_links_info_create ();
					link_info_ptr->link_type = OspfC_Lsa_Router_Link_Stub_Net;
					link_info_ptr->link_id = ip_address_copy (neighbor_ptr->ip_address);
					link_info_ptr->link_data = ip_address_create ("255.255.255.255");
					link_info_ptr->link_cost = intf_ptr->cost;
					}
				}

			/* Add the link to the advertisement. */
			op_prg_list_insert (lsa_ptr->lsa_data.lsa_router_links_ptr->link_list_ptr,
				link_info_ptr, OPC_LISTPOS_TAIL);
			}
		else if (intf_state == OspfC_Interface_State_Waiting)
			{
			/* For an interface in the Waiting state, add a stub network link. */
			link_info_ptr = ospf_lsa_router_links_info_create ();
			link_info_ptr->link_type = OspfC_Lsa_Router_Link_Stub_Net;
			link_info_ptr->link_id = ip_address_mask (intf_ptr->interface_address,
				intf_ptr->interface_mask);
			link_info_ptr->link_data = ip_address_copy (intf_ptr->interface_mask);
			link_info_ptr->link_cost = intf_ptr->cost;

			/* Add the link to the advertisement. */
			op_prg_list_insert (lsa_ptr->lsa_data.lsa_router_links_ptr->link_list_ptr,
				link_info_ptr, OPC_LISTPOS_TAIL);
			}
		else
			{
			/* In this case, we expect to have a designated router selected for  */
			/* this network.  If this router is fully adjacent to the DR, or is  */
			/* the DR itself and is fully adjacent to at least one other router, */
			/* add a single type 2 link.  Otherwise, add a type 3 link similar   */
			/* to the one added in the 'Waiting' state.                          */

			if (ospf_neighbor_list_num_states_match (nbr_list_ptr, OspfC_Neighbor_State_Full) >= 1)
				{
				/* We're fully adjacent to at least one other router. */
				/* Add a type 2 (transit network) link.               */
				link_info_ptr = ospf_lsa_router_links_info_create ();
				link_info_ptr->link_type = OspfC_Lsa_Router_Link_Transit_Net;
				link_info_ptr->link_id = ip_address_copy (ospf_interface_dr_intf_get (intf_ptr));
				link_info_ptr->link_data = ip_address_copy (intf_ptr->interface_address);
				link_info_ptr->link_cost = intf_ptr->cost;
				}
			else
				{
				/* If we're not fully adjacent to at least one other router, */
				/* then add a link as if the state were Waiting.             */
				link_info_ptr = ospf_lsa_router_links_info_create ();
				link_info_ptr->link_type = OspfC_Lsa_Router_Link_Stub_Net;
				link_info_ptr->link_id = ip_address_mask (intf_ptr->interface_address,
					intf_ptr->interface_mask);
				link_info_ptr->link_data = ip_address_copy (intf_ptr->interface_mask);
				link_info_ptr->link_cost = intf_ptr->cost;
				}

			/* Add the link to the advertisement. */
			op_prg_list_insert (lsa_ptr->lsa_data.lsa_router_links_ptr->link_list_ptr,
				link_info_ptr, OPC_LISTPOS_TAIL);
			}
		}

	FRET (lsa_ptr);
	}

/***** Print procedures *****/
void
ospf_area_list_lsa_print (List *area_list_ptr)
	{
	int				num_areas, area_index;
	OspfT_Area *	area_ptr;

	/** Print out the contents of the each area's link state database list. **/
	FIN (ospf_area_list_lsa_print (area_list_ptr));

	num_areas = op_prg_list_size (area_list_ptr);

	for (area_index = 0; area_index < num_areas; area_index++)
		{
		area_ptr = op_prg_list_access (area_list_ptr, area_index);
		ospf_area_lsa_print (area_ptr);
		op_prg_odb_print_minor ("", OPC_NIL);
		}

	FOUT;
	}

void
ospf_area_lsa_print (OspfT_Area *area_ptr)
	{
	char					header_str [128];
	char					area_id_str [IPC_ADDR_STR_LEN];

	/** Print out contents of each area's link state database. **/
	FIN (ospf_area_lsa_print (area_ptr));

	ip_address_print (area_id_str, area_ptr->area_id);

	sprintf (header_str, "[Router Links Advertisements for Area %s]", area_id_str);
	op_prg_odb_print_minor (header_str, OPC_NIL);
	ospf_lsa_list_print (area_ptr->router_lsa_list_ptr);

	sprintf (header_str, "[Network Links Advertisements for Area %s]", area_id_str);
	op_prg_odb_print_minor (header_str, OPC_NIL);
	ospf_lsa_list_print (area_ptr->network_lsa_list_ptr);

	sprintf (header_str, "[Summary Links Advertisements for Area %s]", area_id_str);
	op_prg_odb_print_minor (header_str, OPC_NIL);
	ospf_lsa_list_print (area_ptr->summary_lsa_list_ptr);

	FOUT;
	}

void
ospf_area_list_config_print (List *area_list_ptr)
	{
	int				num_areas, area_index;
	OspfT_Area *	area_ptr;

	/** Print out the address range configuration for each area in the list. **/
	FIN (ospf_area_list_config_print (area_list_ptr));

	num_areas = op_prg_list_size (area_list_ptr);

	for (area_index = 0; area_index < num_areas; area_index++)
		{
		area_ptr = op_prg_list_access (area_list_ptr, area_index);
		ospf_area_config_print (area_ptr);
		op_prg_odb_print_minor ("", OPC_NIL);
		}

	FOUT;
	}

void
ospf_area_config_print (OspfT_Area *area_ptr)
	{
	int					num_ranges, range_index;
	char				area_id_str [IPC_ADDR_STR_LEN];
	char				addr_str [IPC_ADDR_STR_LEN];
	char				mask_str [IPC_ADDR_STR_LEN];
	char				msg [128];
	OspfT_Area_Range *	area_range_ptr;

	/** Print out address range area configuration. **/
	FIN (ospf_area_config_print (area_ptr));

	ip_address_print (area_id_str, area_ptr->area_id);
	sprintf (msg, "Address ranges for area %s:", area_id_str);
	op_prg_odb_print_minor (msg, OPC_NIL);
	
	num_ranges = op_prg_list_size (area_ptr->address_list_ptr);
	for (range_index = 0; range_index < num_ranges; range_index++)
		{
		area_range_ptr = op_prg_list_access (area_ptr->address_list_ptr, range_index);
		
		ip_address_print (addr_str, area_range_ptr->addr_range_ptr->address);
		ip_address_print (mask_str, area_range_ptr->addr_range_ptr->subnet_mask);
		
		if (area_range_ptr->active)
			sprintf (msg, "  Address: %s\tMask: %s\tRange is active", addr_str, mask_str);
		else
			sprintf (msg, "  Address: %s\tMask: %s\tRange is inactive", addr_str, mask_str);

		op_prg_odb_print_minor (msg, OPC_NIL);
		}

	num_ranges = op_prg_list_size (area_ptr->hidden_addr_list_ptr);
	for (range_index = 0; range_index < num_ranges; range_index++)
		{
		area_range_ptr = op_prg_list_access (area_ptr->hidden_addr_list_ptr, range_index);
		
		ip_address_print (addr_str, area_range_ptr->addr_range_ptr->address);
		ip_address_print (mask_str, area_range_ptr->addr_range_ptr->subnet_mask);
		
		sprintf (msg, "  Address: %s\tMask: %s\tRange is hidden", addr_str, mask_str);

		op_prg_odb_print_minor (msg, OPC_NIL);
		}

	op_prg_odb_print_minor ("", OPC_NIL);

	FOUT;
	}
