/* ospf_msg.ex.c: External Code Support for OSPF messages. */

#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_msg.h"

/***** Message Constructors *****/
Packet *
ospf_message_hello_create (OspfT_Interface *interface_ptr)
	{
	Packet *			hello_msg_ptr;
	OspfT_Router_Id		designated_router;
	OspfT_Router_Id		backup_designated_router;
	List *				neighbor_list_ptr;
	int					neighbor_index, num_neighbors;
	OspfT_Neighbor *	neighbor_ptr;
	
	/** Create an OSPF Hello message.  The fields are the message are filled in according **/
	/** to the data found in the interface DS passed to this function.  If no interface   **/
	/** is passed, the default values for the message will be used.                       **/
	FIN (ospf_message_hello_create (interface_ptr));

	/* First create the packet with the correct format. */
	hello_msg_ptr = op_pk_create_fmt ("ospf_hello");

	/* Then check to see if the caller passed an interface DS. */
	if (interface_ptr == OPC_NIL)
		FRET (hello_msg_ptr);

	/* Initialize the header of the message. */
	op_pk_nfd_set (hello_msg_ptr, "router ID",
		ip_address_copy (interface_ptr->parent_area_ptr->parent_router_ptr->router_id),
		ip_address_copy_create, ip_address_destroy, sizeof (OspfT_Router_Id));
	op_pk_nfd_set (hello_msg_ptr, "area ID",
		ip_address_copy (interface_ptr->area_id),
		ip_address_copy_create, ip_address_destroy, sizeof (OspfT_Area_Id));

	/* Set the remaining static fields of the message. */
	op_pk_nfd_set (hello_msg_ptr, "network mask", ip_address_copy (interface_ptr->interface_mask), 
		ip_address_copy_create,	ip_address_destroy, sizeof (IpT_Address));
	op_pk_nfd_set (hello_msg_ptr, "hello interval", interface_ptr->hello_interval);
	op_pk_nfd_set (hello_msg_ptr, "router priority", interface_ptr->router_priority);
	op_pk_nfd_set (hello_msg_ptr, "dead interval", interface_ptr->router_dead_interval);

	/* For the options field, set it to 0, which indicates no TOS or external route capability. */
	op_pk_nfd_set (hello_msg_ptr, "options", 0);

	/* Retrieve the dynamic information for the interface. */
	designated_router = ip_address_copy (ospf_interface_dr_get (interface_ptr));
	backup_designated_router = ip_address_copy (ospf_interface_backup_dr_get (interface_ptr));
	neighbor_list_ptr = ospf_interface_neighbor_list_get (interface_ptr);

	/* Set the designated routers. */
	op_pk_nfd_set (hello_msg_ptr, "designated router", designated_router,
		ip_address_copy_create, ip_address_destroy, sizeof (OspfT_Router_Id));
	op_pk_nfd_set (hello_msg_ptr, "backup designated router", backup_designated_router,
		ip_address_copy_create, ip_address_destroy, sizeof (OspfT_Router_Id));
	
	/* Loop through the neighbors and add them to the message. */
	num_neighbors = op_prg_list_size (neighbor_list_ptr);
	
	for (neighbor_index = 0; neighbor_index < num_neighbors; neighbor_index++)
		{
		/* Get current neighbor. */
		neighbor_ptr = op_prg_list_access (neighbor_list_ptr, neighbor_index);

		/* Set the neighbor field. */
		op_pk_fd_set (hello_msg_ptr, neighbor_index + OSPFC_HELLO_NUM_STATIC_FIELDS + 1, OPC_FIELD_TYPE_STRUCT,
			ip_address_copy (ospf_neighbor_router_id_get (neighbor_ptr)), 32, ip_address_copy_create,
			ip_address_destroy, sizeof (OspfT_Router_Id));
		}

	FRET (hello_msg_ptr);
	}

Packet *
ospf_message_dbase_desc_create (OspfT_Neighbor *neighbor_ptr, int flags)
	{
	Packet *					dbase_desc_msg_ptr;
	int							ims_bits = 0;

	/** Create a database description message, using the information found in **/
	/** the passed in neighbor structure.  If the neighbor structure is       **/
	/** OPC_NIL, then a 'blank' message is created.  The flags argument is    **/
	/** used to specify the Init and More bits in the message.  Note that     **/
	/** this function does not add LSA headers to the message; another        **/
	/** function called ospf_message_lsa_header_add () does that.             **/
	FIN (ospf_message_dbase_desc_create (neighbor_ptr, flags));

	/* First create the packet with the correct format. */
	dbase_desc_msg_ptr = op_pk_create_fmt ("ospf_dbase_desc");

	/* Then check to see if the caller passed a neighbor structure. */
	if (neighbor_ptr == OPC_NIL)
		FRET (dbase_desc_msg_ptr);

	/* Initialize the header of the message. */
	ospf_message_header_init (dbase_desc_msg_ptr,
		neighbor_ptr->parent_interface_ptr->parent_area_ptr->parent_router_ptr->router_id,
		neighbor_ptr->parent_interface_ptr->area_id);

	/* Set the remaining static fields of the message. */

	/* Set 0 for the options field, which indicates no TOS, or external route capability. */
	op_pk_nfd_set (dbase_desc_msg_ptr, "options", 0);

	/* Set the IMS bits.  The init and more bits are set by the flags */
	/* argument, and the master bit is set by the neighbor.           */
	ims_bits = flags;
	
	if (ospf_neighbor_master_status_get (neighbor_ptr))
		ims_bits |= OSPFC_MSG_DBASE_DESC_MASTER_SET;

	op_pk_nfd_set (dbase_desc_msg_ptr, "IMS bits", ims_bits);

	/* Set the sequence number. */
	op_pk_nfd_set (dbase_desc_msg_ptr, "sequence number", ospf_neighbor_seq_num_get (neighbor_ptr));

	FRET (dbase_desc_msg_ptr);
	}

Packet *
ospf_message_lsr_create (OspfT_Neighbor *neighbor_ptr)
	{
	Packet *						lsr_msg_ptr;

	/** Create a Link State Request message.  It is the responsibility   **/
	/** of the client of this procedure to add requests to this message. **/ 
	FIN (ospf_message_lsr_create (neighbor_ptr));

	/* First create the packet with the correct format. */
	lsr_msg_ptr = op_pk_create_fmt ("ospf_ls_request");

	/* Then check to see if the caller passed a neighbor structure. */
	if (neighbor_ptr == OPC_NIL)
		FRET (lsr_msg_ptr);

	/* Initialize the header of the message. */
	ospf_message_header_init (lsr_msg_ptr,
		neighbor_ptr->parent_interface_ptr->parent_area_ptr->parent_router_ptr->router_id,
		neighbor_ptr->parent_interface_ptr->area_id);

	/* The remaining fields are set by the client. */

	FRET (lsr_msg_ptr);
	}

Packet *
ospf_message_lsu_create (OspfT_Neighbor *neighbor_ptr)
	{
	Packet *				lsu_msg_ptr;

	/** Create a Link State Update message.  It is the responsibility of the **/
	/** client of this procedure to add advertisements to this message.      **/
	FIN (ospf_message_lsu_create (neighbor_ptr));

	/* First create the packet with the correct format. */
	lsu_msg_ptr = op_pk_create_fmt ("ospf_ls_update");

	/* Then check to see if the caller passed a neighbor structure. */
	if (neighbor_ptr == OPC_NIL)
		FRET (lsu_msg_ptr);

	/* Initialize the header of the message. */
	ospf_message_header_init (lsu_msg_ptr,
		neighbor_ptr->parent_interface_ptr->parent_area_ptr->parent_router_ptr->router_id,	
		neighbor_ptr->parent_interface_ptr->area_id);

	/* The remaining fields are set by the client. */

	FRET (lsu_msg_ptr);
	}

Packet *
ospf_message_lsack_create (OspfT_Neighbor *neighbor_ptr)
	{
	Packet *				lsack_msg_ptr;

	/** Create a Link State Acknowledgement message.  It is the responsibility **/
	/** of the client of this procedure to add advertisements to this message. **/
	FIN (ospf_message_lsack_create (neighbor_ptr));

	/* First create the packet with the correct format. */
	lsack_msg_ptr = op_pk_create_fmt ("ospf_ls_ack");

	/* Then check to see if the caller passed a neighbor structure. */
	if (neighbor_ptr == OPC_NIL)
		FRET (lsack_msg_ptr);

	/* Initialize the header of the message. */
	ospf_message_header_init (lsack_msg_ptr, 
		neighbor_ptr->parent_interface_ptr->parent_area_ptr->parent_router_ptr->router_id,
		neighbor_ptr->parent_interface_ptr->area_id);

	/* The remaining fields are set by the client. */

	FRET (lsack_msg_ptr);
	}

void
ospf_message_header_init (Packet *message_ptr, OspfT_Router_Id router_id, OspfT_Area_Id area_id)
	{
	/** Initialize the OSPF header of a message. **/
	FIN (ospf_message_header_init (neighbor_ptr, router_id, area_id));

	op_pk_nfd_set (message_ptr, "router ID",
		ip_address_copy (router_id),
		ip_address_copy_create, ip_address_destroy, sizeof (OspfT_Router_Id));
	op_pk_nfd_set (message_ptr, "area ID",
		ip_address_copy (area_id),
		ip_address_copy_create, ip_address_destroy, sizeof (OspfT_Area_Id));

	/* The remaining fields are either not modeled explicitly, or have good defaults. */

	FOUT;
	}

void
ospf_message_ls_request_add (Packet *ospf_message_ptr, OspfT_Lsa_Header *lsa_header_ptr)
	{
	int					last_fd_index;

	/** Add a request for an LSA matching the given header to the end of an OSPF message. **/
	FIN (ospf_message_ls_request_add (ospf_message_ptr, lsa_header_ptr));

	last_fd_index = op_pk_fd_max_index (ospf_message_ptr);

	/* Set the request fields. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSR_FD_TYPE,
		OPC_FIELD_TYPE_INTEGER, lsa_header_ptr->type, 32);

	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSR_FD_LINK_ID,
		OPC_FIELD_TYPE_STRUCT, ip_address_copy (lsa_header_ptr->link_state_id), 32,
		ip_address_copy_create, ip_address_destroy, sizeof (IpT_Address));

	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSR_FD_ROUTER_ID,
		OPC_FIELD_TYPE_STRUCT, ip_address_copy (lsa_header_ptr->router_id), 32,
		ip_address_copy_create, ip_address_destroy, sizeof (IpT_Address));

	FOUT;
	}

void
ospf_message_lsa_header_add (Packet *ospf_message_ptr, OspfT_Lsa_Header *lsa_header_ptr)
	{
	int					last_fd_index;

	/** Add a link state advertisement header to the end of an OSPF message. **/
	FIN (ospf_message_lsa_header_add (ospf_message_ptr, lsa_header_ptr));

	last_fd_index = op_pk_fd_max_index (ospf_message_ptr);

	/* Set the age field. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_AGE, OPC_FIELD_TYPE_DOUBLE,
		lsa_header_ptr->age, 16);

	/* Set the options field.  This will always be 0, until TOS and external routes are added. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_OPTIONS, OPC_FIELD_TYPE_INTEGER,
		0, 8);

	/* Set the type field. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_TYPE, OPC_FIELD_TYPE_INTEGER,	
		(int) lsa_header_ptr->type, 8);

	/* Set the link state ID field. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_LINK_STATE_ID, OPC_FIELD_TYPE_STRUCT,
		ip_address_copy (lsa_header_ptr->link_state_id), 32, ip_address_copy_create,
		ip_address_destroy, sizeof (IpT_Address));

	/* Set the advertising router field. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_ADV_ROUTER_ID, OPC_FIELD_TYPE_STRUCT,
		ip_address_copy (lsa_header_ptr->router_id), 32, ip_address_copy_create,
		ip_address_destroy, sizeof (OspfT_Router_Id));

	/* Set the sequence number field. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_SEQ_NUM, OPC_FIELD_TYPE_INTEGER,	
		lsa_header_ptr->sequence_num, 32);

	/* Finally, add padding to account for the checksum and length fields.  The value is always 0. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_INFO, OPC_FIELD_TYPE_INTEGER,
		0, 32);

	FOUT;
	}

void
ospf_message_lsa_add (Packet *ospf_message_ptr, OspfT_Lsa *lsa_ptr)
	{
	int						msg_type;
	int						num_adv;

	/** Add a link state advertisement to the end of an OSPF message. **/
	FIN (ospf_message_lsa_add (ospf_message_ptr, lsa_ptr));

	/* First check to see if this is actually a Link State Update packet. */
	op_pk_nfd_get (ospf_message_ptr, "type", &msg_type);

	if (msg_type != OSPFC_MSG_TYPE_LSU)
		{
		/* Abort the simulation at this point. */
		ospf_fatal_error ("Trying to add a Link State Advertisement to a non-Link State Update message.");
		}

	/* First add the header. */
	ospf_message_lsa_header_add (ospf_message_ptr, lsa_ptr->lsa_header_ptr);

	/* Then add the body of the LSA.  The format will depend on the LSA type. */
	switch (lsa_ptr->lsa_header_ptr->type)
		{	
		case OspfC_Lsa_Router_Links:
			{
			ospf_message_lsa_router_links_add (ospf_message_ptr, 
				lsa_ptr->lsa_data.lsa_router_links_ptr);
			break;
			}

		case OspfC_Lsa_Network_Links:
			{
			ospf_message_lsa_network_links_add (ospf_message_ptr,
				lsa_ptr->lsa_data.lsa_network_links_ptr);
			break;
			}

		case OspfC_Lsa_Summary_Links:
			{
			ospf_message_lsa_summary_links_add (ospf_message_ptr, 
				lsa_ptr->lsa_data.lsa_summary_links_ptr);
			break;
			}

		default:
			{
			/* Give a fatal error message. */
			ospf_fatal_error ("Trying to add Link State Advertisement of unknown type to message.");
			break;
			}
		}

	/* Increase the number of advertisements in the field. */
	op_pk_nfd_get (ospf_message_ptr, "num advertisements", &num_adv);
	op_pk_nfd_set (ospf_message_ptr, "num advertisements", ++num_adv);

	FOUT;
	}

void
ospf_message_lsa_router_links_add (Packet *ospf_message_ptr, OspfT_Lsa_Router_Links *router_links_ptr)
	{
	int								last_fd_index;
	int								options = 0;
	int								num_links, link_index;
	OspfT_Lsa_Router_Links_Info *	link_info_ptr;

	/** Add the router links specific portion of a router links **/
	/** advertisement to an OSPF message.                        **/
	FIN (ospf_message_lsa_router_links_add (ospf_message_ptr, router_links_ptr));

	/* Find the end of the message, so we know where to add the LSA. */
	last_fd_index = op_pk_fd_max_index (ospf_message_ptr);

	/* Start adding components to the message. */
	if (router_links_ptr->is_abr)
		options |= OSPFC_MSG_ROUTER_LINKS_AREA_BORDER_SET;

	/* Neither the V-bit nor the E-bit are supported at this time. */

	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_ROUTER_LINKS_FD_OPTIONS, 
		OPC_FIELD_TYPE_INTEGER,	options, 3);

	num_links = op_prg_list_size (router_links_ptr->link_list_ptr);
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_ROUTER_LINKS_FD_NUM_LINKS, 
		OPC_FIELD_TYPE_INTEGER, num_links, 16);
	
	/* Add a blank info field, to account for the fields we don't explicitly model. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_ROUTER_LINKS_FD_INFO,
		OPC_FIELD_TYPE_INTEGER, 0, 13);

	/* Now start to add information about the links themselves. */
	/* First reset the last field index.                        */
	last_fd_index += 3;

	for (link_index = 0; link_index < num_links; link_index++)
		{
		link_info_ptr = op_prg_list_access (router_links_ptr->link_list_ptr, link_index);

		op_pk_fd_set (ospf_message_ptr, last_fd_index + (link_index * 5) + OSPFC_MSG_ROUTER_LINKS_FD_LINK_ID,
			OPC_FIELD_TYPE_STRUCT, ip_address_copy (link_info_ptr->link_id), 32,
			ip_address_copy_create, ip_address_destroy, sizeof (IpT_Address));

		op_pk_fd_set (ospf_message_ptr, last_fd_index + (link_index * 5) + OSPFC_MSG_ROUTER_LINKS_FD_LINK_DATA,
			OPC_FIELD_TYPE_STRUCT, ip_address_copy (link_info_ptr->link_data), 32,
			ip_address_copy_create, ip_address_destroy, sizeof (IpT_Address));

		op_pk_fd_set (ospf_message_ptr, last_fd_index + (link_index * 5) + OSPFC_MSG_ROUTER_LINKS_FD_LINK_TYPE,
			OPC_FIELD_TYPE_INTEGER, link_info_ptr->link_type, 8);

		/* This is for TOS 0 cost only. */
		op_pk_fd_set (ospf_message_ptr, last_fd_index + (link_index * 5) + OSPFC_MSG_ROUTER_LINKS_FD_COST,
			OPC_FIELD_TYPE_INTEGER, link_info_ptr->link_cost, 16);

		/* Add padding for the fields we don't explicitly model. */
		op_pk_fd_set (ospf_message_ptr, last_fd_index + (link_index * 5) + OSPFC_MSG_ROUTER_LINKS_FD_LINK_INFO,
			OPC_FIELD_TYPE_INTEGER, 0, 8);
		}
	
	FOUT;
	}

void
ospf_message_lsa_network_links_add (Packet *ospf_message_ptr, OspfT_Lsa_Network_Links *network_links_ptr)
	{
	int						last_fd_index;
	int						num_routers, router_index;
	OspfT_Router_Id			router_id;

	/** Add the network links specific portion of a network **/
	/** links advertisement to an OSPF message.             **/
	FIN (ospf_message_lsa_network_links_add (ospf_message_ptr, network_links_ptr));

	/* Find the end of the message, so we know where to add the LSA. */
	last_fd_index = op_pk_fd_max_index (ospf_message_ptr);

	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_NETWORK_LINKS_FD_MASK, 
		OPC_FIELD_TYPE_STRUCT, ip_address_copy (network_links_ptr->network_mask), 32,
		ip_address_copy_create, ip_address_destroy, sizeof (IpT_Address));

	/* Now add the router IDs of each the attached routers. */
	num_routers = op_prg_list_size (network_links_ptr->router_list_ptr);

	/* Add the number of routers to a zero-length field of the packet. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_NETWORK_LINKS_FD_NUM_ROUTERS,
		OPC_FIELD_TYPE_INTEGER, num_routers, 0);

	for (router_index = 0; router_index < num_routers; router_index++)
		{
		router_id = op_prg_list_access (network_links_ptr->router_list_ptr, router_index);
		
		op_pk_fd_set (ospf_message_ptr, last_fd_index + 3 + router_index,
			OPC_FIELD_TYPE_STRUCT, ip_address_copy (router_id), 32, 
			ip_address_copy_create, ip_address_destroy, sizeof (OspfT_Router_Id));
		}

	FOUT;
	}

void
ospf_message_lsa_summary_links_add (Packet *ospf_message_ptr, OspfT_Lsa_Summary_Links *summary_links_ptr)
	{
	int						last_fd_index;

	/** Add the summary links specific portion of a summary **/
	/** links advertisement to an OSPF message.             **/
	FIN (ospf_message_lsa_summary_links_add (ospf_message_ptr, summary_links_ptr));

	last_fd_index = op_pk_fd_max_index (ospf_message_ptr);

	/* Since we only support TOS 0 at this point, we just have three fields to add. */
	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_SUMMARY_LINKS_FD_MASK, 
		OPC_FIELD_TYPE_STRUCT, ip_address_copy (summary_links_ptr->network_mask), 32,
		ip_address_copy_create, ip_address_destroy, sizeof (IpT_Address));

	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_SUMMARY_LINKS_FD_TOS,
		OPC_FIELD_TYPE_INTEGER, 0, 8);

	op_pk_fd_set (ospf_message_ptr, last_fd_index + OSPFC_MSG_SUMMARY_LINKS_FD_COST,
		OPC_FIELD_TYPE_INTEGER, summary_links_ptr->network_cost, 24);

	FOUT;
	}

/***** Message Inspectors *****/

List *
ospf_message_lsa_list_get (Packet *ospf_message_ptr)
	{
	List *					lsa_list_ptr;
	int						message_type;
	int						num_lsa, lsa_index;
	int						num_lsa_fields = 0, num_all_lsa_fields = 0;
	OspfT_Lsa *				lsa_ptr;

	/** Return the list of link state advertisements contained in the message. **/
	FIN (ospf_message_lsa_list_get (ospf_message_ptr));

	/* Create the return list. */
	lsa_list_ptr = op_prg_list_create ();

	/* This message should be a link state update.  If not, then return an */
	/* empty list and issue a warning.                                     */
	op_pk_nfd_get (ospf_message_ptr, "type", &message_type);
	
	if (message_type != OSPFC_MSG_TYPE_LSU)
		{
		ospf_warn_error ("Trying to extract LSAs from a non-Link State Update message.");
		FRET (lsa_list_ptr);
		}

	/* Read the number of LSAs in this message. */
	op_pk_nfd_get (ospf_message_ptr, "num advertisements", &num_lsa);

	/* Go through the fields of the message, and create LSAs from the contents. */
	for (lsa_index = 0; lsa_index < num_lsa; lsa_index++)
		{
		/* Get the current LSA.  Keep track of the amount of fields the LSA */
		/* takes up, so we know where to start in the next iteration.       */
		lsa_ptr = ospf_message_lsa_get (ospf_message_ptr, OSPFC_LSU_NUM_STATIC_FIELDS - 1 +
			num_all_lsa_fields, &num_lsa_fields);
		
		/* Add the LSA to the return list. */
		op_prg_list_insert (lsa_list_ptr, lsa_ptr, OPC_LISTPOS_TAIL);

		/* Add to the total number of fields seen. */
		num_all_lsa_fields += num_lsa_fields;
		}

	FRET (lsa_list_ptr);
	}

OspfT_Lsa *
ospf_message_lsa_get (Packet *ospf_message_ptr, int last_fd_index, int *num_lsa_fields_ptr)
	{
	OspfT_Lsa *			lsa_ptr;
	OspfT_Lsa_Header *	lsa_header_ptr;
	OspfT_Lsa_Type		lsa_type;
	OspfT_Router_Id		orig_router_id;
	IpT_Address			link_state_id;
	int					num_lsa_fields = 0;

	/** Return the LSA starting right after the field index specified by **/
	/** last_fd_index.  Also returns the number of fields the LSA spans. **/
	FIN (ospf_message_lsa_get (ospf_message_ptr, last_fd_index, num_lsa_fields_ptr));

	/* First make sure last_fd_index is valid for this message. */
	if (last_fd_index > op_pk_fd_max_index (ospf_message_ptr))
		{
		ospf_warn_error ("Tried to get LSA past last message index");
		FRET (OPC_NIL);
		}

	/* Read the header. */
	lsa_header_ptr = ospf_message_lsa_header_get (ospf_message_ptr, last_fd_index);

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

	/* Based on type, retrieve the rest of the LSA. */
	switch (lsa_header_ptr->type)
		{
		case OspfC_Lsa_Router_Links:
			{
			ospf_message_lsa_router_links_get (ospf_message_ptr, lsa_ptr, 
				last_fd_index + OSPFC_LSH_NUM_STATIC_FIELDS, &num_lsa_fields);
			break;
			}

		case OspfC_Lsa_Network_Links:
			{
			ospf_message_lsa_network_links_get (ospf_message_ptr, lsa_ptr, 
				last_fd_index + OSPFC_LSH_NUM_STATIC_FIELDS, &num_lsa_fields);
			break;
			}

		case OspfC_Lsa_Summary_Links:
			{
			ospf_message_lsa_summary_links_get (ospf_message_ptr, lsa_ptr, 
				last_fd_index + OSPFC_LSH_NUM_STATIC_FIELDS, &num_lsa_fields);
			break;
			}

		default:
			{
			ospf_fatal_error ("Trying to extract LSA of unknown or unsupported type.");
			break;
			}
		}

	/* Set the total number of fields in the return argument. */
	*num_lsa_fields_ptr = num_lsa_fields + OSPFC_LSH_NUM_STATIC_FIELDS;

	FRET (lsa_ptr);
	}

void
ospf_message_lsa_router_links_get (Packet *ospf_message_ptr, OspfT_Lsa *lsa_ptr, int last_fd_index, 
	int *num_lsa_fields_ptr)
	{
	OspfT_Lsa_Router_Links *			router_links_ptr;
	int									num_lsa_fields;
	int									options;
	int									num_links, link_index;
	OspfT_Lsa_Router_Links_Info *		link_info_ptr;
	IpT_Address							link_id, link_data;

	/** Get the router links portion of the advertisement from the message. **/	
	FIN (ospf_message_lsa_router_links_get (ospf_message_ptr, lsa_ptr, last_fd_index, num_lsa_fields_ptr));

	/* Create a reference to the router links DS. */
	router_links_ptr = lsa_ptr->lsa_data.lsa_router_links_ptr;

	/* Get the various components. */
	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_ROUTER_LINKS_FD_OPTIONS, &options);

	/* Only check the area border router bit for now; since the other bits are not supported. */
	router_links_ptr->is_abr = (options & OSPFC_MSG_ROUTER_LINKS_AREA_BORDER_SET);
	
	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_ROUTER_LINKS_FD_NUM_LINKS, &num_links);

	/* Update the last field index to account for the static fields. */
	last_fd_index += 3;
	num_lsa_fields = 3;

	/* Scan through the rest of the fields to find the links attached to the router. */
	for (link_index = 0; link_index < num_links; link_index++)
		{
		/* First create a link information DS. */
		link_info_ptr = ospf_lsa_router_links_info_create ();

		/* Read the components. */
		op_pk_fd_get (ospf_message_ptr, last_fd_index + (link_index * 5) + OSPFC_MSG_ROUTER_LINKS_FD_LINK_ID, 
			&link_id);
		link_info_ptr->link_id = ip_address_copy (link_id);

		op_pk_fd_get (ospf_message_ptr, last_fd_index + (link_index * 5) + OSPFC_MSG_ROUTER_LINKS_FD_LINK_DATA,
			&link_data);
		link_info_ptr->link_data = ip_address_copy (link_data);

		op_pk_fd_get (ospf_message_ptr, last_fd_index + (link_index * 5) + OSPFC_MSG_ROUTER_LINKS_FD_LINK_TYPE,
			&link_info_ptr->link_type);
		op_pk_fd_get (ospf_message_ptr, last_fd_index + (link_index * 5) + OSPFC_MSG_ROUTER_LINKS_FD_COST,
			&link_info_ptr->link_cost);

		/* Add the link information DS to the list. */
		op_prg_list_insert (router_links_ptr->link_list_ptr, link_info_ptr, OPC_LISTPOS_TAIL);

		/* Update the number of LSA fields (including the ones not read). */
		num_lsa_fields += 5;
		}

	/* Update the number of fields traversed in this message. */
	*num_lsa_fields_ptr = num_lsa_fields;
	FOUT;
	}

void
ospf_message_lsa_network_links_get (Packet *ospf_message_ptr, OspfT_Lsa *lsa_ptr, int last_fd_index,
	int *num_lsa_fields_ptr)
	{
	OspfT_Lsa_Network_Links *			network_links_ptr;
	int									num_routers, router_index;
	int									num_lsa_fields;
	OspfT_Router_Id						router_id;

	/** Get the network links portion of the advertisement from the message. **/
	FIN (ospf_message_lsa_network_links_get (ospf_message_ptr, lsa_ptr, last_fd_index, num_lsa_fields_ptr));

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

	/* Get the various components. */
	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_NETWORK_LINKS_FD_MASK,
		&network_links_ptr->network_mask);

	/* Loop through the remaining fields in the advertisement to get the list */
	/* of router IDs attached to this network.  The amount of router IDs can  */
	/* be found in the next field.  Note that this field does not normally    */
	/* appear in an OSPF advertisement, but is included here since we do not  */
	/* model the 'length' field in the advertisement header, which is what is */
	/* normally used to infer the number of routers.                          */
	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_NETWORK_LINKS_FD_NUM_ROUTERS,
		&num_routers);

	/* Update the number of fields read. */
	num_lsa_fields = 2;

	for (router_index = 0; router_index < num_routers; router_index++)
		{
		/* Get the router ID, and add to the network routers list. */
		op_pk_fd_get (ospf_message_ptr, last_fd_index + 3 + router_index, &router_id);
		op_prg_list_insert (network_links_ptr->router_list_ptr, ip_address_copy (router_id),
			OPC_LISTPOS_TAIL);
		num_lsa_fields++;
		}

	/* Modify the return argument to reflect the number of fields read. */
	*num_lsa_fields_ptr = num_lsa_fields;

	FOUT;
	}

void
ospf_message_lsa_summary_links_get (Packet *ospf_message_ptr, OspfT_Lsa *lsa_ptr, int last_fd_index,
	int *num_lsa_fields_ptr)
	{
	OspfT_Lsa_Summary_Links *			summary_links_ptr;
	IpT_Address							summary_mask;

	/** Get the summary links portion of the advertisement from the message. **/
	FIN (ospf_message_lsa_summary_links_get (ospf_message_ptr, lsa_ptr, last_fd_index, num_lsa_fields_ptr));

	/* Createa a reference to the summary links DS. */
	summary_links_ptr = lsa_ptr->lsa_data.lsa_summary_links_ptr;

	/* Get the components. */
	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_SUMMARY_LINKS_FD_MASK,
		&summary_mask);
	summary_links_ptr->network_mask = ip_address_copy (summary_mask);

	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_SUMMARY_LINKS_FD_COST,
		&summary_links_ptr->network_cost);

	/* There are fixed number of fields in this advertisement. */
	*num_lsa_fields_ptr = 3;

	FOUT;
	}

List *
ospf_message_lsa_header_list_get (Packet *ospf_message_ptr)
	{
	List *				lsa_header_list_ptr;
	OspfT_Lsa_Header *	lsa_header_ptr;
	int					max_static_fields, num_fields;
	int					num_lsh, lsh_index;
	int					message_type;

	/** Return the list of link state headers contained in the message. **/
	FIN (ospf_message_lsa_header_list_get (ospf_message_ptr));

	/* Create the return list. */
	lsa_header_list_ptr = op_prg_list_create ();

	/* In order to parse this message, we need to know where the last static */
	/* field ends.  This depends on the message type, so get the type first. */
	op_pk_nfd_get (ospf_message_ptr, "type", &message_type);

	if (message_type == OSPFC_MSG_TYPE_DBASE_DESC)
		max_static_fields = OSPFC_DBASE_DESC_NUM_STATIC_FIELDS;
	else if (message_type == OSPFC_MSG_TYPE_LSACK)
		max_static_fields = OSPFC_LSACK_NUM_STATIC_FIELDS;
	else
		{
		/* Trying to get LSA headers from a type that doesn't have them. */
		ospf_warn_error ("Trying to extract LSA headers from a message that does not have them.");
		FRET (lsa_header_list_ptr);
		}

	num_fields = op_pk_fd_max_index (ospf_message_ptr) + 1;

	if (num_fields > max_static_fields)
		{
		/* There are extra fields besides the static fields; presumably */
		/* these fields comprise the LSA headers.                       */
		num_lsh = (num_fields - max_static_fields) / OSPFC_LSH_NUM_STATIC_FIELDS;

		/* Loop through the fields, and compose the LSH list. */
		for (lsh_index = 0; lsh_index < num_lsh; lsh_index++)
			{
			lsa_header_ptr = ospf_message_lsa_header_get (ospf_message_ptr,
				max_static_fields - 1 + (lsh_index * OSPFC_LSH_NUM_STATIC_FIELDS));
			op_prg_list_insert (lsa_header_list_ptr, lsa_header_ptr, OPC_LISTPOS_TAIL);
			}
		}

	FRET (lsa_header_list_ptr);
	}

OspfT_Lsa_Header *
ospf_message_lsa_header_get (Packet *ospf_message_ptr, int last_fd_index)
	{
	OspfT_Lsa_Header *				lsa_header_ptr;
	OspfT_Lsa_Type					lsa_type;
	double							lsa_age;
	int								options;
	IpT_Address						link_state_id;
	OspfT_Router_Id					adv_router_id;
	int								seq_num;

	/** Allocate and return a link state header based on the fields of the passed   **/
	/** in OSPF message, starting right after the field specified by last_fd_index. **/
	FIN (ospf_message_lsa_header_get (ospf_message_ptr, last_fd_index));

	/* First check that the index is valid. */
	if (last_fd_index > op_pk_fd_max_index (ospf_message_ptr))
		{
		ospf_warn_error ("Tried to get LSA header past last message index");
		FRET (OPC_NIL);
		}

	/* Create a new header.  First, gather the information needed to create the header. */
	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_AGE, &lsa_age);
	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_TYPE, &lsa_type);
	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_LINK_STATE_ID, &link_state_id);
	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_ADV_ROUTER_ID, &adv_router_id);
	op_pk_fd_get (ospf_message_ptr, last_fd_index + OSPFC_MSG_LSAH_FD_SEQ_NUM, &seq_num);

	lsa_header_ptr = ospf_lsa_header_create (lsa_type, adv_router_id, link_state_id);
	lsa_header_ptr->age = lsa_age;
	lsa_header_ptr->sequence_num = seq_num;

	FRET (lsa_header_ptr);
	}

int
ospf_message_type_get (Packet *ospf_message_ptr, char *type_str)
	{
	int 	ospf_message_type;
	char	type_str_array [5][64] = {"Hello", "Database Desc.", "LS Request", "LS Update", "LS Ack."};

	/** Return the OSPF message type, and string representation. **/
	FIN (ospf_message_type_get (ospf_message_ptr, type_str));

	/* The type is given in a named field of the message. */
	op_pk_nfd_get (ospf_message_ptr, "type", &ospf_message_type);

	if (type_str != OPC_NIL)
		strcpy (type_str, type_str_array [ospf_message_type - 1]);
		
	FRET (ospf_message_type);
	}

List *
ospf_message_hello_nbr_list_get (Packet *hello_message_ptr)
	{
	List *				neighbor_id_list_ptr;
	int					num_msg_fields;
	int					neighbor_index;
	OspfT_Router_Id		neighbor_id;

	/** Return the list of neighbor IDs contained in the hello message. **/
	FIN (ospf_message_hello_nbr_list_get (hello_message_ptr));

	/* Create the list. */
	neighbor_id_list_ptr = op_prg_list_create ();

	num_msg_fields = op_pk_fd_max_index (hello_message_ptr);

	/* Exit early if there are no fields to read. */
	if (num_msg_fields <= OSPFC_HELLO_NUM_STATIC_FIELDS)
		FRET (neighbor_id_list_ptr);

	/* Loop through the hello ID fields, and add them to the list. */
	for (neighbor_index = OSPFC_HELLO_NUM_STATIC_FIELDS + 1; 
		 neighbor_index <= num_msg_fields;
		 neighbor_index++)
		{
		/* Get the neighbor id. */
		op_pk_fd_get (hello_message_ptr, neighbor_index, &neighbor_id);

		/* Copy and add that to the neighbor list. */
		op_prg_list_insert (neighbor_id_list_ptr, ip_address_copy (neighbor_id), OPC_LISTPOS_TAIL);
		}

	FRET (neighbor_id_list_ptr);
	}
