/**HEADER********************************************************************
* 
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2008 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: iputil.c$
* $Version : 3.0.7.0$
* $Date    : Jun-30-2009$
*
* Comments:
*
*   This file contains various IP layer functions.
*
*END************************************************************************/

#include <rtcs.h>
#include "rtcs_prv.h"
#include "tcpip.h"
#include "ip_prv.h"

/* Used for most of the IP utility functions */
struct IP_util_struct {
   IP_IF_PTR      ipif;
   _ip_address    ipaddr;
   boolean        bool;
   uint_32        num;
};


/*FUNCTION*-------------------------------------------------------------
*
* Function Name    : IP_get_netmask
* Returned Value   : boolean
* Comments         : Gets the network mask. Returns TRUE if found, or
*                    FALSE if no netmask is found.
*
*END*-----------------------------------------------------------------*/

struct IPIF_getmask_struct {
   IP_IF_PTR      netif;
   _ip_address    address;
   _ip_address    mask;
};

static boolean IP_get_netmask_test
   (
      pointer  node,
      pointer  data
   )
{ /* Body */
   IP_ROUTE_PTR                      route = node;
   struct IPIF_getmask_struct _PTR_  getmask = data;
   IP_ROUTE_DIRECT_PTR               search_ptr;

   if (route->DIRECT) {
      search_ptr = route->DIRECT;
      do {
         if (search_ptr->ADDRESS == getmask->address &&
             search_ptr->DESTIF == getmask->netif)
         {
            getmask->mask = route->NODE.MASK;
            return TRUE;
         } /* Endif */
         search_ptr = search_ptr->NEXT;
      } while(search_ptr != route->DIRECT);
   } /* Endif */

   return FALSE;

} /* Endbody */


boolean IP_get_netmask
   (
      _rtcs_if_handle   ihandle,    /* [IN] Interface */
      _ip_address       address,    /* [IN] IP address */
      _ip_address _PTR_ mask_ptr    /* [OUT] netwask for the IP and interface */
   )
{ /* Body */
   IP_CFG_STRUCT_PTR       IP_cfg_ptr = RTCS_getcfg( IP );
   struct IPIF_getmask_struct    getmask;

   getmask.netif     = ihandle;
   getmask.address   = address;
   getmask.mask      = 1;   /* Invalid mask */

   IPRADIX_findbest(&IP_cfg_ptr->ROUTE_ROOT.NODE, address,
      IP_get_netmask_test, &getmask);

   if (getmask.mask == 1) {
      return FALSE;
   } /* Endif */

   *mask_ptr = getmask.mask;
   return TRUE;

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : IP_get_ipif_addr
*  Returned Value : the ip address
*  Comments       : return the ip address of the interface
*
*END*-----------------------------------------------------------------*/

static boolean IP_get_ipif_addr_test
   (
      _ip_address    node_ip,
      _ip_address    node_mask,
      pointer        node_data,
      pointer        data
   )
{ /* Body */
   struct IP_util_struct _PTR_   testdata = data;
   IP_ROUTE_PTR                  route = node_data;
   IP_ROUTE_DIRECT_PTR           search_ptr;

   /* Make sure there are direct interfaces, with full IP addresses */
   if (!~node_mask && route && route->DIRECT) {
      search_ptr = route->DIRECT;
      do {
         if (search_ptr->NETIF == testdata->ipif) {
            testdata->ipaddr = node_ip;
            return TRUE;
         } /* Endif */
         search_ptr = search_ptr->NEXT;
      } while(search_ptr != route->DIRECT);
   } /* Endif */

   return FALSE;

} /* Endbody */


_ip_address IP_get_ipif_addr
   (
      IP_IF_PTR      ipif        /* [IN] the local interface */
   )
{ /* Body */
   IP_CFG_STRUCT_PTR       IP_cfg_ptr = RTCS_getcfg( IP );
   struct IP_util_struct   testdata;

   testdata.ipif     = ipif;
   testdata.ipaddr   = INADDR_ANY;

   /* Check all nodes of the tree */
   IPRADIX_walk(&IP_cfg_ptr->ROUTE_ROOT.NODE, IP_get_ipif_addr_test, &testdata);

   return testdata.ipaddr;

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : IP_is_local
*  Returned Value : TRUE or FALSE
*  Comments       :
*        Decides whether or not an IP address is local to an interface.
*        if iflocal is null, check all the interfaces.
*
*END*-----------------------------------------------------------------*/

static void IP_is_local_test
   (
      pointer _PTR_  node,
      pointer        data
   )
{ /* Body */
   struct IP_util_struct _PTR_   testdata = data;
   IP_ROUTE_PTR                  route;
   IP_ROUTE_DIRECT_PTR           search_ptr;

   if (*node) {
      route = *node;
      search_ptr = route->DIRECT;
   } /* Endif */

   if (search_ptr) {
      do {
         if ((search_ptr->NETIF == testdata->ipif || testdata->ipif == NULL) &&
             (testdata->ipaddr == search_ptr->ADDRESS))
         {
            testdata->bool = TRUE;
            break;
         } /* Endif */
         search_ptr = search_ptr->NEXT;
      } while(search_ptr != route->DIRECT);
   } /* Endif */

} /* Endbody */


boolean IP_is_local
   (
      IP_IF_PTR      iflocal,    /* [IN] the local interface */
      _ip_address    iplocal     /* [IN] the IP address to test */
   )
{ /* Body */
   IP_CFG_STRUCT_PTR       IP_cfg_ptr = RTCS_getcfg(IP);
   struct IP_util_struct   testdata;

   testdata.ipif     = iflocal;
   testdata.ipaddr   = iplocal;
   testdata.bool     = FALSE;

   /* Will make testdata.bool TRUE if ip is local to interface */
   IPRADIX_insert(&IP_cfg_ptr->ROUTE_ROOT.NODE, iplocal, 0xFFFFFFFF, 0,
      IP_is_local_test, &testdata);

   return testdata.bool;

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : IP_is_direct
*  Returned Value : TRUE or FALSE
*  Comments       :
*        Decides whether or not an IP address can be directly reached
*        through a given local interface.
*
*END*-----------------------------------------------------------------*/

static boolean IP_is_direct_test
   (
      pointer        node_data,
      pointer        data
   )
{ /* Body */
   struct IP_util_struct _PTR_   testdata = data;
   IP_ROUTE_PTR                  route = node_data;

   if (route->DIRECT) {
      IP_ROUTE_DIRECT_PTR direct = route->DIRECT;
      do {
         if ((direct->DESTIF == testdata->ipif || testdata->ipif == NULL)) {
            testdata->bool = TRUE;
            return TRUE;
         } /* Endif */
         direct = direct->NEXT;
      } while(direct != route->DIRECT);
   } /* Endif */

   if (route->VIRTUAL) {
      IP_ROUTE_VIRTUAL_PTR virtual = route->VIRTUAL;
      do {
         if ((virtual->DESTIF == testdata->ipif || testdata->ipif == NULL)) {
            testdata->bool = TRUE;
            return TRUE;
         } /* Endif */
         virtual = virtual->NEXT;
      } while(virtual != route->VIRTUAL);
   } /* Endif */

   return FALSE;
} /* Endbody */


boolean IP_is_direct
   (
      IP_IF_PTR      iflocal,
       /* [IN] the local interface */
      _ip_address    iplocal
       /* [IN] the IP address to test */
   )
{ /* Body */
   IP_CFG_STRUCT_PTR       IP_cfg_ptr = RTCS_getcfg(IP);
   struct IP_util_struct   testdata;

   testdata.ipif     = iflocal;
   testdata.ipaddr   = iplocal;
   testdata.bool     = FALSE;

   IPRADIX_findbest(&IP_cfg_ptr->ROUTE_ROOT.NODE, iplocal,
      IP_is_direct_test, &testdata);

   return testdata.bool;

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : IP_is_gate
*  Returned Value : TRUE or FALSE
*  Comments       :
*        Decides whether or not an IP address is a gateway for another.
*
*END*-----------------------------------------------------------------*/

static boolean IP_is_gate_test
   (
      pointer        node_data,
      pointer        data
   )
{ /* Body */
   struct IP_util_struct _PTR_   testdata = data;
   IP_ROUTE_PTR                  route = node_data;
   IP_ROUTE_INDIRECT_PTR         indirect;

   if (route->INDIRECT) {
      indirect = route->INDIRECT;
      do {
         if (indirect->GATEWAY == testdata->ipaddr) {
            testdata->bool = TRUE;
            return TRUE;
         } /* Endif */
         indirect = indirect->NEXT;
      } while(indirect != route->INDIRECT);
   } /* Endif */

   return FALSE;

} /* Endbody */


boolean IP_is_gate
   (
      _ip_address    gateway,
            /* [IN] the gateway */
      _ip_address    ipremote
            /* [IN] the IP address to test */
   )
{ /* Body */
   IP_CFG_STRUCT_PTR       IP_cfg_ptr = RTCS_getcfg(IP);
   struct IP_util_struct   data;

   data.ipaddr = gateway;
   data.bool = FALSE;

   IPRADIX_findbest(&IP_cfg_ptr->ROUTE_ROOT.NODE, ipremote,
      IP_is_gate_test, &data);

   return data.bool;

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : IP_MTU
*  Returned Value : MTU for destination
*  Comments       :
*        Determines the Maximum Transmission Unit for a destination;
*        returns IP_DEFAULT_MTU if destination is not directly connected.
*
*END*-----------------------------------------------------------------*/

static boolean IP_MTU_test
   (
      pointer        node_data,
      pointer        data
   )
{ /* Body */
   struct IP_util_struct _PTR_   testdata = data;
   IP_ROUTE_PTR                  route = node_data;
   IP_ROUTE_VIRTUAL_PTR          virtual;

   if (route->DIRECT) {
      testdata->num = route->DIRECT->DESTIF->MTU;
      return TRUE;
   } /* Endif */

   virtual = route->VIRTUAL;
   if (virtual) {
      if ((testdata->ipaddr & virtual->SOURCE_MASK) == virtual->SOURCE_NET) {
         if (virtual->DESTIF->MTU_FN) {
            testdata->num = virtual->DESTIF->MTU_FN(virtual->DATA);
         } else {
            testdata->num = virtual->DESTIF->MTU;
         } /* Endif */
         return TRUE;
      } /* Endif */
   } /* Endif */

   return FALSE;

} /* Endbody */


uint_32 IP_MTU
   (
      _ip_address    iplocal,
            /* [IN] the local IP address */
      _ip_address    ipremote
            /* [IN] the remote IP address to test */
   )
{ /* Body */
   IP_CFG_STRUCT_PTR       IP_cfg_ptr = RTCS_getcfg(IP);
   struct IP_util_struct   data;

   data.ipaddr = iplocal;
   data.num    = IP_DEFAULT_MTU;

   IPRADIX_findbest(&IP_cfg_ptr->ROUTE_ROOT.NODE, ipremote,
      IP_MTU_test, &data);

   return data.num;

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : IP_find_if
*  Returned Value : interface handle
*  Comments       :
*        Returns the interface handle for a local IP address.
*
*END*-----------------------------------------------------------------*/

static void IP_find_if_test
   (
      pointer _PTR_  node,
      pointer        data
   )
{ /* Body */
   struct IP_util_struct _PTR_   testdata = data;
   IP_ROUTE_PTR                  route;

   if (*node) {
      route = *node;
      if (route->DIRECT) {
         testdata->ipif = route->DIRECT->NETIF;
      } /* Endif */
   } /* Endif */

} /* Endbody */


IP_IF_PTR IP_find_if
   (
      _ip_address    iplocal
            /* [IN] the IP address to test */
   )
{ /* Body */
   IP_CFG_STRUCT_PTR       IP_cfg_ptr = RTCS_getcfg(IP);
   struct IP_util_struct   data;

   data.ipif     = NULL;
   data.ipaddr   = iplocal;

   IPRADIX_insert(&IP_cfg_ptr->ROUTE_ROOT.NODE, iplocal, 0xFFFFFFFF, 0,
      IP_find_if_test, &data);

   return data.ipif;

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : IP_route_find
*  Returned Value : source IP address
*  Comments       :
*        Determine the source IP address to use when routing to a
*        specific destination.
*
*END*-----------------------------------------------------------------*/

static boolean IP_route_find_test
   (
      pointer  node,
      pointer  data
   )
{ /* Body */
   struct IP_util_struct _PTR_   testdata = data;
   IP_ROUTE_PTR                  route = node;

   /* Check for a direct route. */
   if (route->DIRECT) {
      testdata->ipaddr = route->DIRECT->ADDRESS;
      return TRUE;
   } /* Endif */

   /* Check for a numbered virtual route. */
   if (route->VIRTUAL) {
      IP_ROUTE_VIRTUAL_PTR virtual = route->VIRTUAL;
      do {
         if (virtual->ADDRESS) {
            testdata->ipaddr = virtual->ADDRESS;
            return TRUE;
         } /* Endif */
         virtual = virtual->NEXT;
      } while (virtual != route->VIRTUAL);
   } /* Endif */

   /*
   ** If no direct route was found, search for a gateway.
   */
   if (testdata->bool && route->INDIRECT) {
      IP_ROUTE_INDIRECT_PTR indirect = route->INDIRECT;
      do {
         if (indirect->FLAGS & RTF_UP) {
            testdata->ipaddr = indirect->GATEWAY;
            testdata->bool = FALSE;
            return TRUE;
         } /* Endif */
         indirect = indirect->NEXT;
      } while (indirect != route->INDIRECT);
   } /* Endif */

   return FALSE;
} /* Endbody */


_ip_address IP_route_find
   (
      _ip_address    ipdest,
            /* [IN] the ultimate destination */
      uint_32        flags
            /* [IN] optional flags */
   )
{ /* Body */
   IP_CFG_STRUCT_PTR       IP_cfg_ptr = RTCS_getcfg(IP);
   struct IP_util_struct   data;
   _ip_address             gateway;

   data.ipaddr  = INADDR_ANY;
   if (!flags) {
      data.bool   = TRUE;       /* Check gateways */
   } else {
      data.bool   = FALSE;
   } /* Endif */

   IPRADIX_findbest(&IP_cfg_ptr->ROUTE_ROOT.NODE, ipdest,
      IP_route_find_test, &data);

   /* If we have found a gateway, find source addr for the gateway */
   if (!data.bool) {
      gateway = data.ipaddr;
      data.ipaddr = INADDR_ANY;
      IPRADIX_findbest(&IP_cfg_ptr->ROUTE_ROOT.NODE, gateway,
         IP_route_find_test, &data);
   } /* Endif */

   return data.ipaddr;
} /* Endbody */

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : RTCS_get_enet_handle
*  Returned Value : ethernet handle (pointer)
*  Comments       :
*        Returns the ethernet handle associated with given interface.
*
*END*-----------------------------------------------------------------*/

pointer RTCS_get_enet_handle
   (
      _rtcs_if_handle   ihandle    /* [IN] Interface */
   )
{
   return ((IP_IF_PTR)ihandle)->HANDLE;
}

/* EOF */
