
/*
 *
 *     Module Name: nainet.c
 *	       Version: 1.00
 *   Original Date: 09/15/02	
 *	        Author: Charles Gordon
 *	      Language: Ansi C
 * Compile Options:
 * Compile defines:
 *	     Libraries:
 *    Link Options:
 *
 *    Entry Points: NAinet_addr()
 *
 *  Copyright (c) Digi International Inc., All Rights Reserved
 *  
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *  
 *  Restricted Rights Legend
 * 
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 * 
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 * Description.
 * =======================================================================
 *  Created to make copy of NAinet_addr() for bootloader which does not
 *  want the other stuff inside of narmsock.c.
 *
 *
 *
 * Edit Date/Ver   Edit Description
 * ==============  =======================================================
 *
 *
 */
#include <trsocket.h>
#include <trmacro.h>
#include <trtype.h>
#include <trproto.h>
#include <trglobal.h>

#include "fwtypes.h"
#include "narmapi.h"
#include "sockapi.h"
#include "bsp_local.h"

/* masks for IP validation */
#ifndef _LITTLE_ENDIAN
#define ISOLATELEFTMOSTOCTET 0xFF000000
#define ISOLATERIGHTMOSTOCTET 0x000000FF
#define CHECKMULTICAST 0xDF000000
#define CHECKLOCALLOOP 0x7F000000
#define CHECKMAXSUBNET 0x000000FC
#define FIRSTOCTETZERO 0x00000000
#else
#define ISOLATELEFTMOSTOCTET 0x000000FF
#define ISOLATERIGHTMOSTOCTET 0xFF000000
#define CHECKMULTICAST 0x000000DF
#define CHECKLOCALLOOP 0x0000007F
#define CHECKMAXSUBNET 0xFC000000
#define FIRSTOCTETZERO 0x00000000
#endif

/* return codes for validating network information */
#define VALIDIP 0x00
#define NOSUBNETTING 0x01
#define ZEROIPNOTALLOWED 0x02
#define IPANDGWMATCH  0x03
#define IPORGWISMULTICAST 0x04
#define IPORGWISLOCALLOOP 0x05
#define ZEROOCTETNOTALLOWED 0x06
#define ZEROMASKNOTALLOWED 0x07
#define MAXSUBNETEXCEEDED 0x08
#define IPISNETWORKID 0x09
#define IPISBROADCAST 0x0A
#define GWISNETWORKID 0x0B
#define GWISBROADCAST 0x0C
#define GWNOTONSUBNET 0x0D


/*ad*
 * Converts an IP address from a string into an unsigned long, in network
 *
 * @external
 * @category SystemSWServices:general_purpose
 * @include "sockapi.h"
 * @since 6.0
 *
 * @param   addrp     Pointer to a NULL-terminated string that represents an IP address. 
 *		      (for example, 7.92.186.198)
 *
 * @return  0          Invalid IP address.
 * @return  otherwise  Unsigned long representation of the IP address defined by the
 *		       addrp argument.
*ad*/

unsigned long NAInet_addr(char * addrp)
{
      unsigned long l;
      unsigned int val;
      int i;
      l = 0;

      NA_ASSERT(addrp != NULL);
      
      for (i = 0; i < 4; i++)
      {
           l <<= 8;
           if (*addrp != '\0') 
           {
               val = 0;
               while (*addrp != '\0' && *addrp != '.') 
               {  
                    val *= 10;
                    val += *addrp - '0';
                    addrp++;
               }
               if (val > 255)
               {
                   return 0;
               }
               l |= val;
               if (*addrp != '\0' && *addrp != '.') 
               {
                   return 0;
               }
               if (*addrp != '\0') 
                   addrp++;
           } 
       }
        #ifdef BOOT_LOADER
            return(l);
        #else
            return(htonl(l));
        #endif
}

/*ad*
 * Converts an IP address from an unsigned long into dotted string notation (a.b.c.d).
 *
 * @external
 * @category SystemSWServices:general_purpose
 * @since 6.0
 *
 * @param   ulipaddr    Long format of the IP address to convert.
 * @param   destp       Pointer to the destination of the character representation. This
 *                      argument must point to user specified storage of at least 16 or 
 *                      more characters long (15 characters plus the NULL terminator).
 *
 * @note    This function provides similar functionality to inet_toa(), however not identical.
 *          Most importantly, the destp argument is user storage, as opposed to static 
 *          memory defined by the funcion.
*ad*/

void NAInet_toa(unsigned long ulipaddr, char * destp)
{
    unsigned long ltemp;
    
    NA_ASSERT(destp != NULL);

    ltemp = (0xFF000000 & ulipaddr) >> 24; 
    sprintf (destp, "%ld.", ltemp);
    ltemp = (0x00FF0000 & ulipaddr) >> 16;
    sprintf (destp, "%s%ld.", destp, ltemp);
    ltemp = (0x0000FF00 & ulipaddr) >> 8;
    sprintf (destp, "%s%ld.", destp, ltemp);
    ltemp = (0x000000FF & ulipaddr);
    sprintf (destp, "%s%ld", destp, ltemp);
}
/*
 * NAIpAddress
 *
 * This routine returns the IP address being used by the network
 * TCP/UDP/IP protocol stacks.
 *
 * RETURNS
 *
 *    Unit IP address
 */

unsigned long NAIpAddress(void)
{
    return 0;
}
#if NETWORK_STACK == TRECK


/*ad*
 * Converts the standard text presentation of an IPv4 address, IPv6 address or
 * an FQDN to the binary format.  When converted successfully, the family is
 * always set to AF_INET6.  If the converted address is an IPv4 address, it will
 * be represented as an IPv4 mapped address.  IPv4 mapped addresses are commonly
 * represented with their last 32 bits noted as an IPv4 address. For example, the
 * address 10.52.33.161 would typically be noted ::ffff:10.52.33.161.
 *
 * @include "sockapi.h"
 * @external
 * @category Networking:SOCKETS:socketapi
 * @since 7.0
 *
 * @param ipstring   Standard text presentation of an IPv4 address, IPv6 address
 *                   or an FQDN.
 * @param ipaddress  Pointer to a buffer where the binary format of the converted
 *                  IP address will be stored by this function.
 *    
 * @return 1        Success.
 * @return <0       Failure.
 *
 * @see @link sockaddr_storage
 * @see @link addr_families
 *
*ad*/
int na_pton(const char * ipstring, struct sockaddr_storage * ipaddress)
{
    struct sockaddr_storage ipv4Addr;
    struct sockaddr_storage ipv6Addr;
#ifdef IPV6_ENABLED
    struct addrinfo * addrInfoPtr;
    struct addrinfo * retAddrInfoPtr;
    struct addrinfo hints;
#endif
 
    const int sockLen = sizeof (struct sockaddr_storage);
    int errorCode;
	unsigned short port;

    NA_ASSERT(ipstring != NULL);
    NA_ASSERT(ipaddress != NULL);

    if ((ipstring == NULL) || (ipaddress == NULL))
        return -1;
  
	port = ipaddress->ss_port;
    memset(ipaddress, 0, sockLen);
    memset(&ipv4Addr,  0, sockLen);
    ipv4Addr.ss_family = AF_INET;
    ipv4Addr.ss_len = sockLen;

    memset(&ipv6Addr,  0, sockLen);
    ipv6Addr.ss_family = AF_INET6;
    ipv6Addr.ss_len = sockLen;

    ipaddress->ss_len = sockLen;    

    errorCode = inet_pton( AF_INET, 
                           ipstring, 
                           (void *) &ipv4Addr.addr.ipv4.sin_addr);

    if (errorCode == 1)  /* An IPV4 Address */
    {
#ifdef IPV6_ENABLED
        tf6AddressToIpv4Mapped((unsigned int)ipv4Addr.addr.ipv4.sin_addr.s_addr, ipaddress);
        ipaddress->ss_family = AF_INET6;     
#else
        memcpy(ipaddress, (void *) &ipv4Addr, sockLen);
        ipaddress->ss_family = AF_INET;     
#endif

    }
    else
    {
#ifdef IPV6_ENABLED
        errorCode = inet_pton( AF_INET6, 
                           ipstring, 
                           (void *) &ipv6Addr.addr.ipv6.sin6_addr);
        if (errorCode == 1)
        {
            memcpy(ipaddress,  &ipv6Addr, sockLen);
            ipaddress->ss_family = AF_INET6;     
	}
        else
	{
            hints.ai_flags = AI_ALL;
            hints.ai_family = AF_INET6;
            errorCode = getaddrinfo(ipstring,
                                  NULL,
                                  &hints,
                                  &retAddrInfoPtr);
            if (errorCode == TM_ENOERROR)
            {
                errorCode = 1;               
                addrInfoPtr = retAddrInfoPtr;
                if (addrInfoPtr->ai_family == AF_INET)
		{
		    struct sockaddr_in * tempSockInPtr = (struct sockaddr_in*)addrInfoPtr->ai_addr;
                    ipaddress->ss_family = AF_INET;     
                    memcpy(&ipaddress->addr.ipv4.sin_addr, 
                           (void *) &tempSockInPtr->sin_addr,
                           sizeof (ipaddress->addr.ipv4.sin_addr));
                }
                else 
                {
		    struct sockaddr_in6 * tempSockIn6Ptr = (struct sockaddr_in6*)addrInfoPtr->ai_addr;
                    ipaddress->ss_family = AF_INET6;     
                    memcpy(&ipaddress->addr.ipv6.sin6_addr,
                           (void *) &tempSockIn6Ptr->sin6_addr,
                            sizeof(ipaddress->addr.ipv6.sin6_addr));
                 }
	    }
            else
                errorCode = -1;
         }
#endif
    }
	ipaddress->ss_port = port;
	return errorCode;
}



/*ad*
 * Converts an IP address from the binary format to the standard text presentation format.
 *
 * @include "sockapi.h"
 * @external
 * @category Networking:SOCKETS:socketapi
 * @since 7.0
 *
 * @param af         Address family of the IP address specified by src. This is either
 *                   AF_INET (IPv4) or AF_INET6 (IPv6).
 * @param src        Points to the IP address in binary format.
 * @param dst        Points to a buffer where the function will store the resulting text string.
 * @param dst_size   Size (in bytes) of the buffer pointed to by dst. For IPv6 addresses, the
 *                   buffer must be at least 46-bytes (i.e. INET6_ADDRSTRLEN). For IPv4 addresses,
 *                   the buffer must be at least 16-bytes (i.e. INET_ADDRSTRLEN).
 *    
 * @return 0        Success.
 * @return <0       Failure.
 *
 * @see @link sockaddr_storage
 * @see @link addr_families
 *
*ad*/
NaStatus na_ntop(int af, struct sockaddr_storage * src, char * dst, int dst_size)
{
    NaStatus rc = NA_STATUS_INVALID_PARAMETER;
    
    NA_ASSERT(src != NULL);
    NA_ASSERT(dst != NULL);
    
    if (src != NULL && dst != NULL)
    {
    if (af == AF_INET)
    {
        if (inet_ntop(src->ss_family, &src->addr.ipv4.sin_addr.s_addr, dst, dst_size) != NULL)
        {
            rc = NA_SUCCESS;
        }
    }
#ifdef IPV6_ENABLED
    else 
    {
        if (inet_ntop(src->ss_family, &src->addr.ipv6.sin6_addr, dst, dst_size) != NULL)
        {
            rc = NA_SUCCESS;
        }
    }    
#endif
    }
    
    return rc;    
}

#endif /* NETWORK_STACK == TRECK */

/*ad*
 *
 * Validates an address (ip address or gateway) for the following:
 * can not be local loopback (leftmost octet 127)
 * can not be multicast (left most octet 224 and above)
 *
 * @category SystemSWServices:general_purpose
 * @include "narmapi.h"
 * @since 7.5
 *
 * @param	ipAddress     		An IP address, or gateway address in unsigned long format
 *
 * @return	VALIDIP				Address is a valid subnettable address
 * @return	NOSUBNETING 		Left most octet of address is 0 - no Subnetting allowed
 * @return  IPORGWISMULTICAST   Left most octet of address is > 223
 * @return  IPORGWISLOCALLOOP   Left most octet of address is 127
 * @return  ZEROOCTETNOTALLOWED Left most octet is 0, Not allowed.
 *
 * @note	We are not looking for leftmost octet set to zero as this is seen as legal.
 *      	If you want to disallow this, remove the #ifdef/#endif pair below.
 *
*ad*/  
int naValidateIpAddress(unsigned long ipAddress)
{
    unsigned long tempBuffer;


    /* leftmost octet must be <= 223, above 223 is multicast */
    tempBuffer = (ipAddress & ISOLATELEFTMOSTOCTET);
 
 	if(tempBuffer > CHECKMULTICAST)
	{
	    return IPORGWISMULTICAST;
	}
    /* left most octet can not be 127 - local loopback address */
	if(tempBuffer == CHECKLOCALLOOP)
	{
	    return IPORGWISLOCALLOOP;
	}
 
 	/* 
 	* There are instances where the leftmost octet can be zero, so we allow it.
 	* Remove the ifdefs if you want to deny these addresses
 	* Warning - As designed, this will also pertain to the gateway address.
 	*/
	#ifdef DENYLEFTMOSTZERO
    /* left most octet can not be 0 */
	if(tempBuffer == FIRSTOCTETZERO)
	{
	   	return ZEROOCTETNOTALLOWED;
	}
	#endif

	/* If the first octet is 0 return 1 so we don't fail subnet mask test later.*/
	/*  Mask must be 0.0.0.0  because these addresses are not subnet-able.*/

	if (tempBuffer == FIRSTOCTETZERO)
	   return NOSUBNETTING;     /*first octet of IP address is 0 */
	else
	   return VALIDIP;	 /*first octet between 1 and 223 - valid subnet-able address */
}

/*ad*
 *
 * Checks the combinations of IP address and subnet mask and gateway and subnet mask for
 * validity.  
 *
 * @param     ipAddress     IP address in unsigned long format
 * @param     subnetMask    Subnet Mask in unsigned long format
 * @param     gateWay       Gateway address in unsigned long format
 *
 * @return     VALIDIP             	IP Address is valid
 * @return     NOSUBNETTING	    	IP Address can not be subnetted.
 * @return     ZEROIPNOTALLOWED    	IP Address 0.0.0.0 is not allowed.
 * @return     IPANDGWMATCH        	IP Address and Gateway Address are the same.
 * @return     IPORGWISMULTICAST   	IP Address or Gatweay Address is > 223.
 * @return     IPORGWISLOCALLOOP   	IP Address or Gatweay Address is 127.
 * @return     ZEROOCTETNOTALLOWED 	Zero is not allowed in leftmost octet of IP Address.
 * @return     ZEROMASKNOTALLOWED  	Subnet mask 0.0.0.0 not allowed.
 * @return     MAXSUBNETEXCEEDED   	Subnet mask can not exceed 255.255.255.252
 * @return     IPISNETWORKID       	IP Address is network ID.
 * @return     IPISBROADCAST       	IP Address is network broadcast address.
 * @return     GWISNETWORKID       	Gateway Address is network ID.
 * @return     GWISBROADCAST       	Gateway Address is network broadcast address.
 * @return     GWNOTONSUBNET       	Gateway Address is not on subnet.
 *
 * @category SystemSWServices:general_purpose
 * @include "narmapi.h"
 * @since 7.0
 *
*ad*/ 
int naValidateNetworkInformation(unsigned long ipAddress, unsigned long subnetMask, unsigned long gateWay)
{
    int callStatus = 0;
	unsigned long tempSubnet = 0; 
	
    /* No all 0 ip addresses */
	if(ipAddress == 0)
	{
	    return ZEROIPNOTALLOWED;
	}

    /* check ipaddress and gateway for multicast, localloopback and first octet 0 */
	callStatus = naValidateIpAddress(gateWay);

	switch(callStatus)
	{
		case VALIDIP:
		case NOSUBNETTING:
			break;
		default:
			return callStatus;
	}

	/* we do the ipAddress second because we use callStatus to check first octet */
	callStatus = naValidateIpAddress(ipAddress);
	
	switch(callStatus)
	{
		case VALIDIP:
		case NOSUBNETTING:
			break;
		default:
			return callStatus;
	}
	
	/* is the subnet address valid */
	
	/* first we need to see if the first octet of the IP Address is 0 */
	/* 0 addresses are not subnet-able so subnet mask and gateway should be 0.0.0.0 */

 	if(callStatus == VALIDIP)   /* we have a valid subnettable address */		 
 	{	 	
 	 	/* make sure we have a non-0 subnet mask */
		if(subnetMask == 0)
    	{
			return ZEROMASKNOTALLOWED;
    	}

	   /* make sure last octet of subnet mask is <= 252 */
	   tempSubnet =	(subnetMask & ISOLATERIGHTMOSTOCTET);
	   if(tempSubnet > CHECKMAXSUBNET)
	   {
	   		return MAXSUBNETEXCEEDED;
	   }
 	    
	   /* weed out network numbers from IP address */
    	if((~subnetMask & ipAddress) == 0)
    	{
			return IPISNETWORKID;
    	}

    	/* weed out broadcast addresses from IP address */
		tempSubnet = (ipAddress & ~subnetMask);
		if(tempSubnet == ~subnetMask)
		{
	    	return IPISBROADCAST;
		}

		/* we allow 0.0.0.0 gateway so don't do these tests if it is 0.0.0.0*/
    	if(gateWay != 0)
		{
    		/* weed out network numbers from gateway address */
       		if((~subnetMask & gateWay) == 0)
    		{
				return GWISNETWORKID;
    		}

    		/* weed out broadcast addresses from gateway address */
			tempSubnet = (gateWay & ~subnetMask);
			if(tempSubnet == ~subnetMask)
			{
	    		return GWISBROADCAST;
			}

			/* Make sure gateway is on this subnet */
			if((ipAddress & subnetMask) !=
				(gateWay & subnetMask))
			{
	    		return GWNOTONSUBNET;
			}  
		}
	}
	else  /* the first octet is 0 - no subnetting allowed */
	{
		if((subnetMask | gateWay) != 0)	
   		{
			return NOSUBNETTING;					   
   		}
	}
 	/* we're all good */
	return VALIDIP;
}

/*ad*
 *
 * Validate that a specified IPv6 address can be configured on an interface. 
 *
 * @param     ipAddrPtr     Pointer to a buffer, containing binary IPv6 address to configure.
 * @param     prefixLen     Prefix length for the specified IPv6 address.
 *
 * @return     0	             	Valid IPv6 address to configure.
 * @return     EINVAL				IPv6 address can't be configured on the interface.
 * @return     EADDRNOTAVAIL    	Attempt to configure the device with a multicast address, the
 *									unspecified address, the loopback address, or an IPv4-only IPv6 address.
 *
 * @category SystemSWServices:general_purpose
 * @include "narmapi.h"
 * @since 7.5
 *
 * @see @link sockaddr_storage
 *
*ad*/ 
int naValidateIpv6Information(struct sockaddr_storage * ipAddrPtr, int prefixLen)
{
	int errorCode; 

	if (! (IN6_IS_ADDR_UNSPECIFIED(&(ipAddrPtr->addr.ipv6.sin6_addr))) )
    {
        errorCode = tf6ValidConfigAddr(
            &(ipAddrPtr->addr.ipv6.sin6_addr), prefixLen);
		
		switch (errorCode)
		{
		case TM_ENOERROR:
			errorCode = 0; 
			break;

		case TM_EADDRNOTAVAIL:
			errorCode = EADDRNOTAVAIL;
			break;

		case TM_EINVAL:
		default:
			errorCode = EINVAL;
			break;
		}
    }
	else 
		errorCode = EADDRNOTAVAIL;

	return errorCode;
}

