/* ip3_addr.ex.c: External code for IP address package. */

/***** Includes *****/
#include <opnet.h>

/* IP specific header files */
#include "ip3_addr.h"
#include "oms_pr.h"

/* Useful IP addresses. */
IpT_Address IpI_Broadcast_Addr = IPC_ADDR_INVALID;
IpT_Address IpI_Default_Addr = IPC_ADDR_INVALID;

/***** Functions *****/
void
ip_address_pkg_init (void)
	{
	/** Perform any necessary initializations for the IP address package.        **/
	/** Currently, this just initializes the commonly used IP special addresses. **/
	FIN (ip_address_pkg_init (void));

	/* Only initialize the addresses if they haven't yet been set. */
	if (IpI_Broadcast_Addr == IPC_ADDR_INVALID)
		IpI_Broadcast_Addr = ip_address_create ("255.255.255.255");

	if (IpI_Default_Addr == IPC_ADDR_INVALID)
		IpI_Default_Addr = ip_address_create ("0.0.0.0");

	FOUT;
	}

IpT_Address
ip_address_alloc (void)
	{
	IpT_Address			addr;
	static Boolean		ip_addr_poolmem_defined = OPC_FALSE;
	static Pmohandle	ip_addr_pmh;

	/** Allocate and return an empty IP address. **/
	FIN (ip_address_alloc (void));

	/* "Pooled" memory is used to allocate IP addreses since they are	*/
	/* frequently created and destroyed. If the pooled memory object	*/
	/* has not yet been defined, do so now, prior to allocation.		*/
	if (ip_addr_poolmem_defined == OPC_FALSE)
		{
		ip_addr_pmh = op_prg_pmo_define ("IP address allocation block", IPC_ADDR_SIZE * sizeof (unsigned char), 32);

		/* Prevent redundant definition.  */
		ip_addr_poolmem_defined = OPC_TRUE;
		}

	addr = (IpT_Address) op_prg_pmo_alloc (ip_addr_pmh);

	FRET (addr);
	}

IpT_Address
ip_address_create (char *addr_str)
	{
	IpT_Address		addr;
	List *			list_addr_ptr;
	int				addr_int_component;
	int				list_size, addr_index, num_zeros;
	char			err_msg [256];
	char *			addr_str_component;

	/** Create an IP address based on a string in dotted decimal **/
	/** notation.  For example, to create the IP address for     **/
	/** 127.0.0.1, use ip_address_create ("127.0.0.1").          **/
	FIN (ip_address_create (addr_str));

	/* Decompose the string into byte components to pass to ip_address_create (). */
	list_addr_ptr = op_prg_str_decomp (addr_str, ".");

	/* Get the size of the list. */
	list_size = op_prg_list_size (list_addr_ptr);

	/* Sanity check, make sure no more than the proper number of components are specified. */
	/* If the size of the list is less than IPC_ADDR_SIZE, then zeros will be added as the */
	/* most significant components of the address.  For example '13' becomes '0.0.0.13'.   */
	if (list_size > IPC_ADDR_SIZE)
		{
		/* Print out error message. */
		sprintf (err_msg, "Illegal number of components in address string (%s)", addr_str);
		op_sim_message (err_msg, "passed to ip_address_create (). ");
		FRET (OPC_NIL);
		}

	/* Determine number of zeros than are needed to extend the address. */
	num_zeros = IPC_ADDR_SIZE - list_size;

	/* Allocate number of bytes corresponding to address. */
	addr = ip_address_alloc ();

	for (addr_index = 0; addr_index < IPC_ADDR_SIZE; addr_index++)
		{
		/* Determine if this element is a zero-extension. */
		if ((addr_index + 1) > num_zeros)
			{
			/* Get current address component. */
			addr_str_component = (char *) op_prg_list_access (list_addr_ptr, addr_index - num_zeros);

			/* Convert to integer. */
			addr_int_component = atoi (addr_str_component);
			}
		else
			addr_int_component = 0;

		/* Check to see if it's a proper range. */
		if ((addr_int_component > 255) || (addr_int_component < 0))
			{
			/* Print out error message. */
			sprintf (err_msg, "Illegal format for IP address (%s)", addr_str);
			op_sim_message (err_msg, "passed to ip_address_create ().");

			/* Free the partially formed address. */
			op_prg_mem_free (addr);
			FRET (OPC_NIL);
			}

		/* Add this address component.  Cast to byte size before adding it. */
		addr [addr_index] = (unsigned char) addr_int_component;
		}
	
	/* Free the address list before leaving. */
	op_prg_list_free (list_addr_ptr);
	op_prg_mem_free (list_addr_ptr);

	FRET (addr);
	}

IpT_Address
ip_address_complement_create (IpT_Address addr)
	{
	IpT_Address			comp_addr;
	int					addr_index;

	/** Create and return the bitwise complement of the given address. **/
	FIN (ip_address_complement_create (addr));

	/* Create new address. */
	comp_addr = ip_address_alloc ();

	/* Loop through all the bytes, and get their bitwise complements. */
	for (addr_index = 0; addr_index < IPC_ADDR_SIZE; addr_index++)
		comp_addr [addr_index] = ~addr [addr_index];

	FRET (comp_addr);
	}


Compcode
ip_net_address_increment (IpT_Address addr, IpT_Address_Class address_class)
	{
	int					i, first_byte;
	unsigned char		class_boundary;
	Compcode			code = OPC_COMPCODE_SUCCESS;

	/* Increment an IP network address by one, assuming default subnet mask given	*/
	/* the specified class. If incrementation cannot be performed without going to	*/
	/* another class, then return a failure. 										*/
	FIN (ip_address_increment (IpT_Address addr, IpT_Address_Class address_class))

	/* Based on the specified class, determine which is the first byte	*/
	/* at which incrementation can begin, and the highest possible		*/
	/* value for the high order byte. 									*/
	switch (address_class)
		{
		case IPC_ADDRESS_CLASS_C:
			{
			first_byte = 3;
			class_boundary = 223;
			break;
			}
		case IPC_ADDRESS_CLASS_B:
			{
			first_byte = 2;
			class_boundary = 191;
			break;
			}
		case IPC_ADDRESS_CLASS_A:
			{
			first_byte = 1;
			class_boundary = 128;
			break;
			}
		}

	/* Loop through each of the octets in the modifiable range. Note that the	*/
	/* octets are arranged big-endian style: the zeroth octet is the			*/
	/* most signifcant one. We want to start incrementing in the lower			*/
	/* part of the address. 													*/
	for (i = first_byte - 1; i >= 0; i--)
		{
		/* If incrementing the octet exceeds 255, then let it go to zero, 	*/
		/* and continue the loop to increment the next one. Otherwise, exit	*/
		/* the loop. I.e., unless there is a carry-over, this loop performs	*/
		/* exactly one iteration. 											*/
		if (++(addr [i]) != 0)
			break;
		else if  (i == 0)
			code = OPC_COMPCODE_FAILURE;
		}

	/* If the incrementation caused the address to exit the class range, return an error. */
	if (code == OPC_COMPCODE_SUCCESS)
		{
		if (addr [0] > class_boundary)
			code = OPC_COMPCODE_FAILURE;
		}

	FRET (code)
	}


Compcode
ip_address_increment (IpT_Address addr, IpT_Address subnet_mask)
	{
	int					i;
	IpT_Address			old_net_address, new_net_address;
	Compcode			code = OPC_COMPCODE_SUCCESS;

	/* Increment an IP address by one, but remain within the specified 	*/
	/* subnet range. If the address was the last possible one in the	*/
	/* range, then return a failure code. 								*/
	FIN (ip_address_increment (IpT_Address addr, IpT_Address subnet_mask))

	/* Remember the network address as it is before modification. 	*/
	old_net_address = ip_address_mask (addr, subnet_mask);

	/* Loop through each of the octets in the address. Note that the	*/
	/* octets are arranged big-endian style: the zeroth octet is the	*/
	/* most signifcant one. We want to start incrementing in the lower	*/
	/* part of the address. 											*/
	for (i = IPC_ADDR_SIZE - 1; i >= 0; i--)
		{
		/* If incrementing the octet exceeds 255, then let it go to zero, 	*/
		/* and continue the loop to increment the next one.					*/
		if (++(addr [i]) != 0)
			break;
		else if  (i == 0)
			code = OPC_COMPCODE_FAILURE;
		}

	/* If the incrementation caused the address to no longer match the 	*/
	/* specified subnet mask, then return an error. 					*/
	if (code == OPC_COMPCODE_SUCCESS)
		{
		new_net_address = ip_address_mask (addr, subnet_mask);
		if (ip_address_equal (old_net_address, new_net_address) == OPC_FALSE)	
			code = OPC_COMPCODE_FAILURE;
		}

	/* Deallocate addresses used for temporary purposes. */
	ip_address_destroy (old_net_address);
	ip_address_destroy (new_net_address);
	FRET (code)
	}

IpT_Address
ip_address_node_broadcast_create (IpT_Address addr, IpT_Address subnet_mask)
	{
	IpT_Address			broadcast_addr;
	IpT_Address			comp_subnet_mask_addr;
	int					addr_index;

	/** Create a directed broadcast IP address.  This address broadcasts to **/
	/** a particular IP subnet, indicated by the address and subnet mask.   **/
	/** The broadcast address is created by masking out the node component  **/
	/** of the address, and replacing it with all 1's. This can be done by  **/
	/** bitwise ORing the complement of the subnet mask with the base       **/
	/** address of the address range.                                       **/
	FIN (ip_address_node_broadcast_create (addr, subnet_mask));

	/* Create the broadcast address. */
	broadcast_addr = ip_address_alloc ();

	/* Create the complement of the subnet mask. */
	comp_subnet_mask_addr = ip_address_complement_create (subnet_mask);

	/* Bitwise-OR in the subnet mask complement. */
	for (addr_index = 0; addr_index < IPC_ADDR_SIZE; addr_index++)
		broadcast_addr [addr_index] = addr [addr_index] | comp_subnet_mask_addr [addr_index];

	/* Deallocate the complement of the address. */
	ip_address_destroy (comp_subnet_mask_addr);

	FRET (broadcast_addr);
	}

Boolean
ip_address_is_broadcast (IpT_Address addr, IpT_Address subnet_mask)
	{
	IpT_Address		comp_subnet_mask;
	IpT_Address		mask_addr;
	Boolean			status;

	/** Returns OPC_TRUE if an addr is a broadcast address with respect to **/
	/** subnet_mask.  Determine this by creating the complement of the     **/
	/** subnet mask, and masking that with the address.  If the result is  **/
	/** the same as the subnet mask complement, then the address is a      **/
	/** broadcast address.                                                 **/
	FIN (ip_address_is_broadcast (addr, subnet_mask));

	/* When the subnet mask is all 1s, then the interface does not accept */
	/* a broadcast address, so the address is not a broadcast.            */
	if (ip_address_equal (subnet_mask, IpI_Broadcast_Addr))
		FRET (OPC_FALSE);

	/* Create a complement of the subnet mask. */
	comp_subnet_mask = ip_address_complement_create (subnet_mask);

	/* Mask the complement with the address. */
	mask_addr = ip_address_mask (addr, comp_subnet_mask);

	/* Does the result equal the complement? */
	status = ip_address_equal (mask_addr, comp_subnet_mask);

	/* Destroy the temporary addresses before leaving. */
	ip_address_destroy (mask_addr);
	ip_address_destroy (comp_subnet_mask);

	FRET (status);
	}

int
ip_address_to_int (IpT_Address addr)
	{
	int			int_ip_addr = 0;
	int			addr_index;
	int			addr_comp;

	/** Return an integer representation of an IP address.  Note that this only  **/
	/** works if ints are at least IPC_ADDR_SIZE bytes on this system; if not,   **/
	/** then an error message is given, and the simulation terminates.  Note     **/
	/** that this function is provided as a convienence to clients who need an   **/
	/** integer representation of an IP address; it is the client's              **/
	/** responsibility to ensure that an integer has enough bytes to hold the    **/
	/** IP address.  This function should be used only when an integer           **/
	/** representation is absolutely necessary.                                  **/
	FIN (ip_address_to_int (addr));

	/* First make a check that an int is at least the number of bytes in the IP address. */
	if (sizeof (int) < IPC_ADDR_SIZE)
		op_sim_end ("Error in ip_address_to_int ()", 
			"Unable to create integer representation of IP address", OPC_NIL, OPC_NIL);

	/* Now just bitwise-or in every component of the address. */
	for (addr_index = 0; addr_index < IPC_ADDR_SIZE; addr_index++)
		{
		addr_comp = (int) addr [addr_index] << ((IPC_ADDR_SIZE - addr_index - 1) * 8);
		int_ip_addr |= addr_comp;
		}

	FRET (int_ip_addr);
	}

IpT_Address
ip_address_from_int (int int_ip_addr)
	{
	IpT_Address 	addr;
	int				addr_index;
	int				byte_mask;
	int				addr_comp;

	/** Converts an integer representation of an IP address to an internal **/
	/** representation that can be used by the IP address package.  Note   **/
	/** the above caveats in ip_address_to_int (); they also apply here.   **/
	FIN (ip_address_from_int (int_ip_addr));

	/* First check to see if the integer type holds enough */
	/* bytes for the IP address.                           */
	if (sizeof (int) < IPC_ADDR_SIZE)
		op_sim_end ("Error in ip_address_from_int ()",
			"Unable to create IP address from integer representation", OPC_NIL, OPC_NIL);

	/* Next, loop through the bytes in the integer, and create the components */
	/* by masking out all bits except the byte we are interested in.          */
	addr = ip_address_alloc ();

	for (addr_index = 0; addr_index < IPC_ADDR_SIZE; addr_index++)
		{
		/* Create a mask that masks all bits but a specific byte. */
		byte_mask = 0x00FF << ((IPC_ADDR_SIZE - addr_index - 1) * 8);

		/* Mask out all bytes but the one we're interested in. */
		addr_comp = (byte_mask & int_ip_addr) >> ((IPC_ADDR_SIZE - addr_index - 1) * 8);

		addr [addr_index] = (unsigned char) addr_comp;
		}

	FRET (addr);
	}

IpT_Address
ip_address_copy (IpT_Address addr)
	{
	IpT_Address			copy_addr;

	/** Creates a copy of the IP address passed to this function. **/
	FIN (ip_address_copy (addr));

	/* Check for a NIL address first. */
	if (addr == OPC_NIL)
		FRET (OPC_NIL);

	/* Allocate the new address. */
	copy_addr = ip_address_alloc ();

	/* This assumes that the address is just a continuous set of bytes. */
	op_prg_mem_copy (addr, copy_addr, IPC_ADDR_SIZE);

	FRET (copy_addr);
	}

IpT_Address
ip_address_copy_create (IpT_Address addr, int alloc_size)
	{
	/** Creates a copy of the IP address passed to this function. **/
	/** This should be used for packet fields containing IP       **/
	/** addresses.  The alloc_size argument is ignored.           **/
	FIN (ip_address_copy_create (addr, alloc_size));
	FRET (ip_address_copy (addr));
	}

void
ip_address_destroy (IpT_Address addr)
	{
	/** Deallocates an IP address object. **/
	FIN (ip_address_destroy (addr));
	
	if (addr == OPC_NIL)
		FOUT;

	/* The address is just a continuous stream of bytes. */
	op_prg_mem_free (addr);

	FOUT;
	}

void
ip_address_print (char *str, IpT_Address addr)
	{
	int			addr_index;
	char		temp_str [512];

	/** Accept an IP address, and a pre-allocated string, and return the dotted **/
	/** decimal notation of the address in the string.                          **/
	FIN (ip_address_print (str, addr));

	/* Catch case where addr is OPC_NIL. */
	if (addr == OPC_NIL)
		{
		sprintf (str, "Invalid");
		FOUT;
		}

	/* Loop through all the bytes in IpT_Address and create   */
	/* an address string out of their decimal representation. */
	for (addr_index = 0; addr_index < IPC_ADDR_SIZE; addr_index++)
		{
		if (addr_index == 0)
			sprintf (str, "%d", addr [addr_index]);
		else
			{
			sprintf (temp_str, ".%d", addr [addr_index]);
			strcat (str, temp_str);
			}
		}

	FOUT;
	}

void
ip_address_print_debug (IpT_Address addr)
	{
	char 		temp_str [IPC_ADDR_STR_LEN];

	/** Print IP address to standard output.  Useful when using a C debugger. **/
	FIN (ip_address_print_debug (addr));

	ip_address_print (temp_str, addr);
	printf ("%s\n", temp_str);

	FOUT;
	}

IpT_Address_Range *
ip_address_range_create (IpT_Address addr, IpT_Address subnet_mask)
	{
	IpT_Address_Range *		addr_range_ptr;

	/** Create an address range based on an address and subnet mask.  The address **/
	/** range represents all legal IP addresses for the given net and subnet      **/
	/** numbers in the address (defined by the subnet mask).  Note that the input **/
	/** address and mask are not copied into the address range.                   **/
	FIN (ip_address_range_create (addr, subnet_mask_ptr));

	/* Allocate range object. */
	addr_range_ptr = (IpT_Address_Range *) op_prg_mem_alloc (sizeof (IpT_Address_Range));

	/* Set the components of the range. */
	addr_range_ptr->address = addr;
	addr_range_ptr->subnet_mask = subnet_mask;
	
	FRET (addr_range_ptr);
	}

IpT_Address_Range *
ip_address_range_copy (IpT_Address_Range *addr_range_ptr)
	{
	IpT_Address_Range *		copy_addr_range_ptr;

	/** Creates and returns a copy of the address range. **/
	FIN (ip_address_range_copy (addr_range_ptr));

	copy_addr_range_ptr = ip_address_range_create (addr_range_ptr->address,
		addr_range_ptr->subnet_mask);

	FRET (copy_addr_range_ptr);
	}

void
ip_address_range_destroy (IpT_Address_Range *addr_range_ptr)
	{
	/** Deallocate an address range. **/
	FIN (ip_address_range_destroy (addr_range_ptr));

	if (addr_range_ptr == OPC_NIL)
		FOUT;

	/* Deallocate components first. */
	ip_address_destroy (addr_range_ptr->address);
	ip_address_destroy (addr_range_ptr->subnet_mask);

	/* Then deallocate structure. */
	op_prg_mem_free (addr_range_ptr);

	FOUT;
	}

void
ip_address_range_print (char *addr_str, char *subnet_mask_str, IpT_Address_Range *addr_range_ptr)
	{
	/** Accept an IP address and two pre-allocated strings, and return the **/
	/** dotted decimal notation for the address and mask in the strings.   **/
	FIN (ip_address_range_print (addr_str, subnet_mask_str, addr_range_ptr));

	/* Use ip_address_print () to create strings for each component. */
	ip_address_print (addr_str, addr_range_ptr->address);
	ip_address_print (subnet_mask_str, addr_range_ptr->subnet_mask);

	FOUT;
	}

Boolean
ip_address_equal (IpT_Address addr1, IpT_Address addr2)
	{
	int			addr_index;

	/** If the two input addresses are equal, return OPC_TRUE, otherwise **/
	/** return OPC_FALSE.                                                **/
	FIN (ip_address_equal (addr1, addr2));

	/* Loop through each of the address components and compare each component. */
	for (addr_index = 0; addr_index < IPC_ADDR_SIZE; addr_index++)
		{
		if (addr1 [addr_index] != addr2 [addr_index])
			FRET (OPC_FALSE);
		}
	
	FRET (OPC_TRUE);
	}

IpT_Address
ip_address_mask (IpT_Address addr, IpT_Address subnet_mask)
	{
	IpT_Address 		masked_ip_addr;
	int					addr_index;

	/** Return the IP address that results from bitwise ANDing the address **/
	/** with the subnet mask.  Do this by ANDing each address component.   **/
	FIN (ip_address_mask (addr, subnet_mask));

	/* First allocate a new address. */
	masked_ip_addr = ip_address_alloc ();

	/* Then loop through each address byte, and mask the address mask pair. */
	for (addr_index = 0; addr_index < IPC_ADDR_SIZE; addr_index++)
		masked_ip_addr [addr_index] = addr [addr_index] & subnet_mask [addr_index];

	FRET (masked_ip_addr);
	}

Boolean
ip_address_range_check (IpT_Address addr, IpT_Address_Range *addr_range_ptr)
	{
	IpT_Address 		addr1;
	IpT_Address 		addr2;
	Boolean 			status;

	/** Given a particular IP address, return OPC_TRUE if that address falls  **/
	/** in the given range, OPC_FALSE if the address falls outside the range. **/
	/** This test is accomplished by simply masking the address with the      **/
	/** range subnet mask and masking the base address of the range with the  **/
	/** range subnet mask.  If the results of the masking are the same, then  **/
	/** the address falls within the address range.                           **/
	FIN (ip_address_range_check (addr, addr_range_ptr));

	/* Handle the case of the default address first. This address appears 	*/
	/* frequently due to auto-addressing or other uninitialized addresses. 	*/
	/* If the base or mask of the range is this default address then do 	*/
	/* not perform the comparison; simply return 'no match'. 				*/
	if (ip_address_equal (addr_range_ptr->address, IpI_Default_Addr) ||
		ip_address_equal (addr_range_ptr->subnet_mask, IpI_Default_Addr))
		{
		FRET (OPC_COMPCODE_FAILURE);
		}

	/* Mask input address and base address of range with range subnet mask. */
	addr1 = ip_address_mask (addr, addr_range_ptr->subnet_mask);
	addr2 = ip_address_mask (addr_range_ptr->address, addr_range_ptr->subnet_mask);

	/* Get the result of testing for equality. */
	status = ip_address_equal (addr1, addr2);

	/* Destroy the two temporary addresses. */
	ip_address_destroy (addr1);
	ip_address_destroy (addr2);

	FRET (status);
	}	

/***** IP Multicast Extensions *****/

Boolean
ip_address_is_multicast (IpT_Address addr)
	{
	/** Returns OPC_TRUE address is class D (multicast), returns OPC_FALSE otherwise. **/
	FIN (ip_address_is_multicast (addr));
	
	/* Class D addresses range from 224.0.0.0 to 239.255.255.255.  Check the */
	/* most significant byte to see if it falls within this range.           */
	if ((addr [0] < 224) || (addr [0] > 239))
		{
		FRET (OPC_FALSE);
		}
	else
		{
		FRET (OPC_TRUE);
		}
	}

void
ip_address_multicast_register (IpT_Address addr, Objid node_objid)
	{
	char			addr_str [IPC_ADDR_STR_LEN];
	char			err_msg [512];
	List *			proc_reg_list_ptr;
	OmsT_Pr_Handle	ip_proc_reg_handle;
	List *			mcast_addr_list_ptr;
	int				num_addrs, addr_index;
	IpT_Address		mcast_addr;

	/** Register a node to accept the given multicast address. **/
	FIN (ip_address_multicast_register (addr, node_objid));

	/* First make sure that the address we want to register is indeed a valid class D address. */
	if (!ip_address_is_multicast (addr))
		{
		/* Generate an error message. */
		ip_address_print (addr_str, addr);
		
		sprintf (err_msg, "Unable to register (%s) as a multicast address.  Not a class D address.",
			addr_str);
		op_prg_odb_print_minor (err_msg, OPC_NIL);

		FOUT;
		}

	/* Create the process registry list. */
	proc_reg_list_ptr = op_prg_list_create ();

	/* Next, search for the ip3_rte module in this node. */
	oms_pr_process_discover (OPC_OBJID_INVALID, proc_reg_list_ptr, 
		"protocol", 	OMSC_PR_STRING, "ip",
		"node objid", 	OMSC_PR_OBJID,	node_objid,
		OPC_NIL);

	/* Make sure we only have one ip3_rte module. */
	if (op_prg_list_size (proc_reg_list_ptr) != 1)
		{
		/* Report an error. */
		ip_address_print (addr_str, addr);

		sprintf (err_msg, "Unable to register (%s) as a multicast address.  Either zero or several ip modules in node",
			addr_str);
		op_prg_odb_print_major (err_msg, OPC_NIL);

		FOUT;
		}

	/* Get the process registry handle for IP. */
	ip_proc_reg_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_reg_list_ptr, OPC_LISTPOS_HEAD);

	/* Retrieve the multicast address list. */
	oms_pr_attr_get (ip_proc_reg_handle,
		"multicast address list", OMSC_PR_ADDRESS, &mcast_addr_list_ptr);

	/* Deallocate no longer needed process registry information. */
	while (op_prg_list_size (proc_reg_list_ptr))
		op_prg_list_remove (proc_reg_list_ptr, OPC_LISTPOS_HEAD);
	op_prg_mem_free (proc_reg_list_ptr);

	/* Loop through the list to make sure the address is not already registered. */
	num_addrs = op_prg_list_size (mcast_addr_list_ptr);
	for (addr_index = 0; addr_index < num_addrs; addr_index++)
		{
		mcast_addr = op_prg_list_access (mcast_addr_list_ptr, addr_index);
		if (ip_address_equal (mcast_addr, addr))
			{
			/* We already have the address registered, so there's nothing further to do. */
			FOUT;
			}
		}

	/* Add a copy of the new address to the node's list. */
	op_prg_list_insert (mcast_addr_list_ptr, ip_address_copy (addr), OPC_LISTPOS_TAIL);

	FOUT;
	}	

void
ip_address_multicast_deregister (IpT_Address addr, Objid node_objid)
	{
	List *				proc_reg_list_ptr;
	char				addr_str [IPC_ADDR_STR_LEN];
	char				err_msg [512];
	OmsT_Pr_Handle		ip_proc_reg_handle;
	List *				mcast_addr_list_ptr;
	int					addr_index, num_addrs;
	IpT_Address			cur_addr;

	/** Remove the address from the IP module's multicast address list. **/
	FIN (ip_address_multicast_deregister (addr, node_objid));

	/* Create the process registry list. */
	proc_reg_list_ptr = op_prg_list_create ();
	
	/* Search for the ip3_rte module in this node. */
	oms_pr_process_discover (OPC_OBJID_INVALID, proc_reg_list_ptr,
		"protocol",		OMSC_PR_STRING,	"ip",
		"node objid",	OMSC_PR_OBJID,	node_objid,
		OPC_NIL);

	/* Make sure we only have one ip3_rte module. */
	if (op_prg_list_size (proc_reg_list_ptr) != 1)
		{
		/* Report an error. */
		ip_address_print (addr_str, addr);

		sprintf (err_msg, "Unable to de-register multicast address (%s).  Either zero or several ip modules in node",
			addr_str);
		op_prg_odb_print_minor (err_msg, OPC_NIL);

		FOUT;
		}

	/* Get the process registry handle for IP. */
	ip_proc_reg_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_reg_list_ptr, OPC_LISTPOS_HEAD);

	/* Retrieve the multicast address list. */
	oms_pr_attr_get (ip_proc_reg_handle,
		"multicast address list", OMSC_PR_ADDRESS, &mcast_addr_list_ptr);

    /* Deallocate no longer needed process registry information. */
	while (op_prg_list_size (proc_reg_list_ptr))
		op_prg_list_remove (proc_reg_list_ptr, OPC_LISTPOS_HEAD);
	op_prg_mem_free (proc_reg_list_ptr);

	/* Search through the multicast address list for an IP address that matches. */
	num_addrs = op_prg_list_size (mcast_addr_list_ptr);

	for (addr_index = 0; addr_index < num_addrs; addr_index++)
		{
		/* Get current IP address. */
		cur_addr = (IpT_Address) op_prg_list_access (mcast_addr_list_ptr, addr_index);

		/* Compare with input address. */
		if (ip_address_equal (addr, cur_addr))
			{
			/* Delete the address from this list. */
			op_prg_list_remove (mcast_addr_list_ptr, addr_index);

			/* Deallocate the address. */
			ip_address_destroy (cur_addr);

			FOUT;
			}
		}

	FOUT;
	}


IpT_Address_Class
ip_address_class (IpT_Address addr)
	{
	IpT_Address_Class	address_class;
	char				err_msg [512];

	/**	Return the address class of the specified IP address.	**/
	FIN (ip_address_class (addr));

	/*	Determine the IP address class based on the value of 	*/
	/*	the fisrt byte of the address.							*/
	if (addr [0] == 224)
		address_class = IPC_ADDRESS_CLASS_D;
	
	else if ((addr [0] > 191) && (addr [0] < 223))
		address_class = IPC_ADDRESS_CLASS_C;

	else if ((addr [0] > 127) && (addr [0] < 192))
		address_class = IPC_ADDRESS_CLASS_B;

	else if ((addr [0] > 0) && (addr [0] < 127))
		address_class = IPC_ADDRESS_CLASS_A;

	else 
		{
		/*  Print out an error message.                    		 */
		sprintf (err_msg, "Unable to determine address class for IP address (%s)", addr);
		op_sim_end ("Error in \"ip_address_class ()\":", err_msg, OPC_NIL, OPC_NIL);
		}

	FRET (address_class);
	}


IpT_Address
ip_default_smask_from_class (IpT_Address_Class address_class)
	{
	IpT_Address			snet_mask;

	/* Returns a default subnet mask (as defined in ip_default_smask_create ())	*/
	/* for the specified network class. 										*/
	FIN (ip_default_smask_from_class (IpT_Address_Clas address_class))

	switch (address_class)
		{
		case IPC_ADDRESS_CLASS_A:
			{
			snet_mask = ip_address_create ("255.0.0.0");
			break;
			}

		case IPC_ADDRESS_CLASS_B:
			{
			snet_mask = ip_address_create ("255.255.0.0");
			break;
			}

		case IPC_ADDRESS_CLASS_C:
			{
			snet_mask = ip_address_create ("255.255.255.0");
			break;
			}

		default:
			{
			/* This is an unexpected condition; return an invalid subnet mask. */
			snet_mask = ip_address_create ("0.0.0.0");
			break;
			}
		}

	FRET (snet_mask)
	}


IpT_Address
ip_default_smask_create (IpT_Address ip_addr)
	{
	IpT_Address_Class	address_class;
	IpT_Address			snet_mask;

	/**	This procedure returns the default subnetwork mask		**/
	/**	based on the type of network address. A default subnet	**/
	/**	mask for a particular IP address class represents an 	**/
	/**	assignment of "1" for the bits used to represent the	**/
	/**	network portion of the address and "0" for the bits		**/
	/**	representing the hosts.									**/
	FIN (ip_default_smask_create (ip_addr));

	/*	Determine the address class of the IP address for which	*/
	/*	the subnet mask needs to be created.					*/
	address_class = ip_address_class (ip_addr);

	/* Based on the class, generate the default subnet mask. 	*/
	snet_mask = ip_default_smask_from_class (address_class);

	FRET (snet_mask);
	}


Boolean
ip_smask_address_verify (IpT_Address address, IpT_Address mask)
	{
	Boolean			mask_ok = OPC_TRUE;
	int				field1, field2, field3, field4;

	/* Determine if the specifeid subnet mask and address are consistent. 	*/
	FIN (ip_smask_address_verify (IpT_Address address, IpT_Address mask))

	/*	Break down the subnet mask into fields for field-by-field comparison 	*/
	/* with minimum legal subnet masks (i.e., default masks). 					*/
	ip_address_fields_obtain (mask, &field1, &field2, &field3, &field4);


	/* Obtain the class of the specified address and switch off of it to 	*/
	/* analyze mask for valid values. A mask is valid if it is at least as 	*/
	/* long as the default subnet mask for the given address. 				*/
	switch (ip_address_class (address))
		{
		case IPC_ADDRESS_CLASS_A:
			{
			if (field1 != 255)
				mask_ok = OPC_FALSE;
			break;
			}

		case IPC_ADDRESS_CLASS_B:
			{
			if ((field1 != 255) || (field2 != 255))
				mask_ok = OPC_FALSE;
			break;
			}

		case IPC_ADDRESS_CLASS_C:
			{
			if ((field1 != 255) || (field2 != 255) || (field3 != 255))
				mask_ok = OPC_FALSE;
			break;
			}

		default:
			{
			mask_ok = OPC_TRUE;
			break;
			}
		}

	FRET (mask_ok)
	}




void
ip_address_fields_obtain (IpT_Address addr, int *field1, int *field2, int *field3, int *field4)
	{
	/**	This procedure accepts an IP address and returns the	**/
	/**	integer values of the four octets in the IP address.	**/
	FIN (ip_address_fields_obtain (addr, field1, field2, field3, field4));

	/*	Based on the IP address, set the appropriate return		*/
	/*	values.													*/
	*field1 = addr [0];
	*field2 = addr [1];
	*field3 = addr [2];
	*field4 = addr [3];

	FOUT;
	}
