/*
 *            Copyright (c) 1998-2003 by NETsilicon Inc.
 *
 *  This software is copyrighted by and is the sole property of 
 *  NETsilicon.  All rights, title, ownership, or other interests
 *  in the software remain the property of NETsilicon.  This
 *  software may only be used in accordance with the corresponding
 *  license agreement.  Any unauthorized use, duplication, transmission,  
 *  distribution, or disclosure of this software is expressly forbidden.
 *
 *  This Copyright notice may not be removed or modified without prior
 *  written consent of NETsilicon.
 *
 *  NETsilicon, reserves the right to modify this software
 *  without notice.
 *
 *  NETsilicon
 *  411 Waverley Oaks Road                  USA 781.647.1234
 *  Suite 304                               http://www.netsilicon.com
 *  Waltham, MA 02452                       AmericaSales@netsilicon.com
 *
 *************************************************************************
 *
 *  Description
 *  ====================================================================
 *  This file contains the template ACE callback routines.  This code
 *  is intended to be modified by NET+OS developers.
 *
 */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "npttypes.h"
#include "boardParams.h"
#include "tx_api.h"
#include "bsp.h"
#include "gpio.h"
#include "ace.h"
#include "ace_params.h"
#include "errhndlr.h"
#include "starttcp.h"
#include "narmapi.h"
#include "naifconfig.h"
#include "naip_global.h"
#include "dnsc_api.h"
#include "std.h"
#include "netdev.h"
#include "fpublic.h"
#include "fip.h"
#include "nadhcp.h"
#include "aceCallbacks.h"
#include "naaddrconflict.h"


/**
 * ACE callback functions are called by ACE when certain events occur.  The callback
 * functions must take appropriate action when these events occur.  
 * The callback functions are:
 *
 * <list type="unordered">
 *  <item>customizeAceHaveAddress is called when ACE gets an address for an interface
 *        that does not already have one.  The callback must determine whether the
 *        address is acceptable, and, if so, configure the interface with it.</item>
 *  <item>customizeAceHaveAnotherAddress is called when ACE gets an address for an
 *        interface that has already been configured with one.  The callback must
 *        determine if the new address is more desirable than the first one, and, if
 *        so, reconfigured the interface with it.</item>
 *  <item>customizeAceLostAddress is called when the lease for an address has expired
 *        and cannot be renewed.  The callback must unconfigure the interface so that
 *        it no longer uses the address.  It must cause any application code that
 *        has open sockets to close them and reopen them after the interface has been
 *        reconfigured with a new IP address.  In many cases, the simplest and safest
 *        thing for the callback to do is to reset the system.</item>
 *  </list>
 * 
 *
 * Default implementations for these callbacks are provided that handle these events in
 * the way recommended by Netsilicon for typical applications.  You must update these
 * templates for your application's requirements.
 *
 * @external
 * @category ACE:ace_callbacks
 * @since 6.0
 * @overview ACE Callbacks
 */

#if  BSP_ENABLE_ADDR_CONFLICT_DETECTION == TRUE

static NAAddrConflictCallbackType addr_conflict_fn = NULL;
#endif

#define RESET_DELAY     ((BSP_TICKS_PER_SECOND/10) ? (BSP_TICKS_PER_SECOND/10) : 1)

extern int ip_configured;

#ifdef FAST_IP_ENABLED
extern void fastip_ip_down(void);
extern void fastip_ip_up(void);
#endif

/* 
 * The following definitions and variables are used for the template 
 * configuration of interfaces, that use ACE to obtain an IP address.
 */

/* IP address information of the network interface, configured via an ACE callback */
typedef struct 
{
    char        *ifname;            /* Interface name */
    aceAddrInfo addr_info;          /* IP address, mask, def. gateway, DNS servers. */
    WORD32      changed_ip_mask;    /* Changed global IP confiruration when 
                                       configuring this interface */
    
    int num_addr_conflicts;
    int total_addr_conflicts;
} interfaceAddrInfo;

/* Number of network interfaces that use ACE to obtain an IP address */
static int interface_count = 0;

/* IP address information of network interfaces, configured via ACE callbacks */
static interfaceAddrInfo if_addr_info[CONFIG_ACE_MAX_INTERFACES] = {{0}};

/* Single default gateway (from one interface) used in the default static route */
static WORD32  def_gateway = 0;

static int  findInterfaceByName(const char *ifname);
static void removeOldDnsServers(const WORD32 *old_dns_server, const WORD32 *dns_server);

static int      aceInterfaceConfigToProtoTable(aceConfigInterfaceInfo *nif, aceProtocolInfo *proto_info);
static BOOLEAN  staticConfigToProtoInfo(aceProtocolInfo *proto_info, configAceStaticInfo *static_config);
static BOOLEAN  dhcpConfigToProtoInfo(aceProtocolInfo *proto_info, configAceDhcpInfo *dhcp_config);
static BOOLEAN  bootpConfigToProtoInfo(aceProtocolInfo *proto_info, configAceBootpInfo *bootp_config);
static BOOLEAN  autoipConfigToProtoInfo(aceProtocolInfo *proto_info, configAceAutoipInfo *autoip_config);
static BOOLEAN  rarpConfigToProtoInfo(aceProtocolInfo *proto_info, configAceRarpInfo *rarp_config);
static BOOLEAN  pingarpConfigToProtoInfo(aceProtocolInfo *proto_info, configAcePingArpInfo *ping_arp_config);


/* 
 * The following definitions and variables are used for the template 
 * of global IP configuration with values obtained through ACE.
 */

/* Default IP Configuration */
static globalIpConfig default_ip_config = {0};

static void setIpConfig(const globalIpConfig *ip_config);
static void getPresentIpConfig(globalIpConfig *ip_config);

/* Default ACE lock implementation */
static BOOLEAN customizeAceCreateLock(void);
static void customizeAceLock(void);
static void customizeAceUnlock(void);


/**
 * Obtains IP address information for the network interface 
 * configured with the ACE callback.
 *
 * @since   6.0
 * @external
 * @category ACE:ace_callbacks
 *
 *
 * @param ifname        Null-terminated string that identifies the network interface.
 * @param addr_info     Points to the structure to be filled in with IP address.
 *                      Information on output. Defined in ace.h.
 * @return  TRUE        Obtained IP address information.
 * @return  FALSE       IP address has not been configured for this interface.
 */
BOOLEAN customizeAceGetInterfaceAddrInfo(const char * ifname, aceAddrInfo * addr_info)
{
    BOOLEAN result = FALSE;
    int index;

    NA_ASSERT(ifname    != NULL);
    NA_ASSERT(addr_info != NULL);

    if (ifname != NULL && addr_info != NULL)
    {
        customizeAceLock();

        index = findInterfaceByName(ifname);
        if (index >= 0 && if_addr_info[index].addr_info.ipaddr != 0)
        {
            *addr_info = if_addr_info[index].addr_info;
            result = TRUE;
        }

        customizeAceUnlock();
    }
    return result;
}

/**
 * Registered by the template implementation of customizeStartAce to be called
 * by ACE when ACE gets or renewes an IP address on an interface. 
 *
 * This routine needs to determine whether the address (and other configuration information) 
 * should be used. If so, this routine configures the interface with the IP address by calling 
 * customizeAceConfigureInterface and returns TRUE. 
 *
 * The routine returns FALSE if the address is unacceptable.
 *
 * @since   6.0
 * @external
 * @category ACE:ace_callbacks
 *
 *
 * @param ifname            Null-terminated string that identifies the network interface.
 * @param protocol          The protocol used to acquire the address.  Values
 *                          are defined in ace_params.h.
 * @param addr_event        The event that triggered this callback.  Values
 *                          are defined in ace.h.
 * @param addr_info         Pointer to IP address information, extracted from the 
 *                          proto_event_info. Defined in ace.h
 * @param proto_event_info  Pointer to protocol specific data that describes the information
 *                          received by that protocol.
 * @param proto_event_info_size Size of the proto_event_info buffer in bytes.
 *
 * @return  TRUE        Interface has been configured with this IP address.
 * @return  FALSE       Has not used this IP address.
 */
BOOLEAN customizeAceHaveAddress(const char * ifname, int protocol, aceAddrEventType addr_event, aceAddrInfo * addr_info, const void * proto_event_info, size_t proto_event_info_size)
{
    char ip_addr[20];
    globalIpConfig ip_config;

    ip_config.set_mask = 0;

    customizeAceLock();

    if (protocol == ACE_PROT_BOOTP || protocol == ACE_PROT_DHCP)
        ace_get_bootp_dhcp_ip_options(protocol, proto_event_info, &ip_config);

    customizeAceConfigureInterface(ifname, addr_info, &ip_config);
    
    NAInet_toa(addr_info->ipaddr, ip_addr);
    NA_DBPF("ACE:  Have IP address on interface %s: %s\n", ifname, ip_addr);
    printf("ACE:  Have IP address on interface %s: %s\n", ifname, ip_addr);
    customizeAceUnlock();
    
    return TRUE;
}


/**
 * Registered by the template implementation of customizeStartAce
 * to be called by ACE when ACE gets an IP address on an interface after the
 * interface has already been configured. 
 *
 * If this routine decides to replace an existing IP address with the new one it 
 * should configure it by calling customizeAceConfigureInterface and return TRUE. 
 * If customizeAceHaveAnotherAddress decides to discard this IP address it should return FALSE. 
 *
 * Changing the IP address is very dangerous. You must close down the current connections and 
 * sockets of any applications that have open TCP sockets.
 *
 * NetSilicon recommends that this function return FALSE.
 *
 * In order to change customizeAceHaveAnotherAddress to replace the existing IP address with 
 * the new one the code of this function has to be changed to be similar to the 
 * customizeAceHaveAddress code.
 *
 * @since   6.0
 * @external
 * @category ACE:ace_callbacks
 *
 *
 * @param ifname            Null-terminated string that identifies the network interface
 * @param protocol          The protocol used to acquire the address. Values
 *                          are defined in ace_params.h.
 * @param addr_event        The event that triggered this callback. Values
 *                          are defined in ace.h.
 * @param addr_info         Pointer to IP address information, extracted from the 
 *                          proto_event_info. Defined in ace.h.
 * @param proto_event_info  Pointer to protocol specific data that describes the information
 *                          received by that protocol.
 * @param proto_event_info_size Size of the proto_event_info buffer in bytes.
 *
 * @return  TRUE        Interface has been configured with this IP address.
 * @return  FALSE       Has not used this IP address.
 */
BOOLEAN customizeAceHaveAnotherAddress(const char * ifname, int protocol, aceAddrEventType addr_event, aceAddrInfo * addr_info, const void * proto_event_info, size_t proto_event_info_size)
{
    char ip_addr[20];
   
    customizeAceLock();

    NAInet_toa(addr_info->ipaddr, ip_addr);
    NA_DBPF("ACE: Have 2nd IP address interface %s %s.\n", ifname, ip_addr);
    customizeAceUnlock();

    return FALSE;
}


/**
 * Registered by the template implementation of customizeStartAce
 * to be called by ACE when the IP address in use on an interface has
 * been lost. 
 *
 * It is possible to remove the IP address from the interface
 * and set another one. NetSilicon does not recomend this because you must shut 
 * down all TCP connections and close the sockets of any applications that have 
 * open TCP sockets, and reopen them after you set the new IP address. There is 
 * no built-in mechanism in BSD Sockets for notifying applications
 * of this problem, and applications and the stack will not work correctly
 * if applications attempt to continue to use old TCP connections or sockets.
 *
 * NetSilicon recommends that you first shut down the application as gracefully 
 * as possible, and then reset the unit. Note that all TCP connections will be dead,
 * so it will not be possible to send or receive data over TCP without an IP address.
 *
 * @since   6.0
 * @external
 * @category ACE:ace_callbacks
 *
 *
 * @param ifname            Null-terminated string that identifies the network interface
 * @param protocol          identifies the protocol used to acquire the address.  Values
 *                          are defined in ace_params.h.
 * @param addr_event        Identifies the event that triggered this callback.  Values
 *                          are defined in ace.h.
 */
void customizeAceLostAddress(const char * ifname, int protocol, aceAddrEventType addr_event)
{
    customizeAceLock();

    NA_DBPF("ACE: Lost IP Address on interface %s.\n", ifname);
    if (protocol == ACE_PROT_AUTOIP)
    {
        customizeAceClearInterfaceConfiguration(ifname);
    }
    else
    {
        NA_DBPF("ACE: Resetting board.\n");
        tx_thread_sleep(RESET_DELAY);           /* give printf time to print before resetting*/
        customizeReset();
    }
    
    customizeAceUnlock();
}

/**
 * Call this function to stop ACE and un-configure the IP address for the interface.
 *
 * @since   6.0
 * @external
 * @category ACE:ace_callbacks
 *
 *
 * @param ifname            Null-terminated string that identifies the network interface
 *
 * @return 0        No error.
 * @return -1       Error.
 *
 */
int customizeStopAce(const char * ifname)
{
    int result;
    
    customizeAceLock();

    result = aceStop(ifname);
    if (result == 0)
        result = customizeAceClearInterfaceConfiguration(ifname);
    
    customizeAceUnlock();

    return result;
}

#if  BSP_ENABLE_ADDR_CONFLICT_DETECTION == TRUE

/**
 * This routine is registered by the template implementation of customizeStartAce
 * to be called by ACE if the IP address conflict occurs during the initial IP 
 * address configuration.
 *
 * This routine calls application-level IP address conflict callback of a NAAddrConflictCallbackType
 * type and reconfigures ACE for the interface according to the action, returned by the 
 * application-level callback.
 *
 * @since   6.2
 * @external
 * @category ACE:ace_callbacks
 *
 * @see @link ACE
 *
 * @param ifname            Null-terminated string that identifies the network interface
 * @param ipaddr            IP address that caused address conflict
 * @param protocol          Protocol which received the IP address 
 * @param ip_configured     TRUE - IP address is already configured on this virtual 
 *                          interface. This can occur if some of the protocols had   
 *                          ACE_NEVER_SHUTDOWN type
 */

void customizeAceAddrConflict(const char * ifname, WORD32 ipaddr, int protocol, BOOLEAN ip_configured) 
{
    NAAddrConflictActionType  result = NAContinueOnAddrConflict;
    static aceProtocolInfo    proto_info[ACE_MAX_PROTOCOLS];
    static aceConfigInfo      ace_config;
    aceConfigInterfaceInfo    *if_config = NULL;
    int index;
    NAAddrConflictCallbackDataType cb_data;
    int num_protocols;

    memset(proto_info, 0, sizeof *proto_info);

    /* Find the interface and update conflict number */
    customizeAceLock();
    index = findInterfaceByName(ifname);

    if (index < 0 || index >= CONFIG_ACE_MAX_INTERFACES)
        goto ret;

    if_addr_info[index].num_addr_conflicts++;
    if_addr_info[index].total_addr_conflicts++;

    cb_data.num_conflicts = if_addr_info[index].num_addr_conflicts;
    customizeAceUnlock();

    cb_data.ip_configured = ip_configured;
    cb_data.protocol      = protocol;

    /* Call the application-level callback */
    if (addr_conflict_fn)
        result = addr_conflict_fn(ifname, ipaddr, &cb_data);
    
    /* Retrieve ACE configuration for this interface, if needed */
    if (result != NAContinueOnAddrConflict && 
        result != NAStopOnAddrConflict)
    {
        if (customizeAceGetConfig(&ace_config) == BP_SUCCESS)
            if_config = customizeAceFindInterfaceConfig(ifname, &ace_config);

        if (if_config == NULL)
            customizeErrorHandler(ERROR_ACE_FAILURE, ERROR_SUBCODE_BAD_ACE_CONFIG);
    }
    /* For NAGetStaticAddrOnConflict action, just add static protocol with startup delay 0*/
    if (result == NAGetStaticAddrOnConflict)
    {
        result = NAAddProtocolOnConflict;
        cb_data.add_protocol = ACE_PROT_STATIC;
    }

    /* Reconfigure ACE according to the action */

    switch(result)
    {
    case NAStopOnAddrConflict:      /* Stop ACE on interface */
        customizeAceLock();
        aceStop(ifname);
        customizeAceUnlock();
        goto ret;

    case NAAddProtocolOnConflict:       /* Add one prptpcol on conflict */
         if (cb_data.add_protocol == protocol) /* Don't add a protocol, that caused conflict */
            goto ret;

        /* Convert protocol configuration to ACE format */ 
        switch (cb_data.add_protocol)
        {
        case ACE_PROT_DHCP:
            if_config->dhcp_config.isEnabled = TRUE;
            if (dhcpConfigToProtoInfo(&proto_info[0], &if_config->dhcp_config) == FALSE)
                goto ret;
            break;

        case ACE_PROT_BOOTP:
            if_config->bootp_config.isEnabled = TRUE;
            if (bootpConfigToProtoInfo(&proto_info[0], &if_config->bootp_config) == FALSE)
                goto ret;
            break;

        case ACE_PROT_PING_ARP:
            if_config->ping_arp_config.isEnabled = TRUE;
            if (pingarpConfigToProtoInfo(&proto_info[0], &if_config->ping_arp_config) == FALSE)
                goto ret;
            break;

        case ACE_PROT_RARP:
            if_config->rarp_config.isEnabled = TRUE;
            if (rarpConfigToProtoInfo(&proto_info[0], &if_config->rarp_config) == FALSE)
                goto ret;
            break;

        case ACE_PROT_AUTOIP:
            if_config->autoip_config.isEnabled = TRUE;
            if (autoipConfigToProtoInfo(&proto_info[0], &if_config->autoip_config) == FALSE)
                goto ret;
            break;

        case ACE_PROT_STATIC:
            if_config->static_config.isEnabled = TRUE;
            if (staticConfigToProtoInfo(&proto_info[0], &if_config->static_config) == FALSE)
                goto ret;
            break;

        default:
             goto ret;
        }
        /* Add a protocol at runtime with the startup delay 0 */
        proto_info[0].proto_start_info.delay_before_start = 0; /* Set stratrup delay to 0 */
        customizeAceLock();
        aceAddProtocolAtRuntime(ifname, &proto_info[0]);    /* add a protocol */
        customizeAceUnlock();
        goto ret;

   case NAGetNetworkAddrOnConflict:
        if_config->static_config.isEnabled = FALSE;  /* disable STATIC to use network protocols */
        break;

    case NAUpdateOnAddrConflict:
        break;

    case NAContinueOnAddrConflict:      /* no need to reconfigure */
    default:
        goto ret;
    }
    
    /* Update ACE on Conflict */
    /* Don't enable the protocol that caused conflict */

    switch(protocol)
    {
    case ACE_PROT_BOOTP:	
         if_config->bootp_config.isEnabled = FALSE;
         break;

    case ACE_PROT_PING_ARP:	
         if_config->ping_arp_config.isEnabled = FALSE;
         break;

    case ACE_PROT_RARP:		
         if_config->rarp_config.isEnabled = FALSE;
         break;

    case ACE_PROT_STATIC:	
         if_config->static_config.isEnabled = FALSE;
         break;

    case ACE_PROT_DHCP:
         if_config->dhcp_config.isEnabled = FALSE;
         break;

    default:
        break;
    }
    customizeAceLock();

    /* Stop ACE on interface */
    aceStop(ifname);
    /* Convert configuration for protocols to ACE format */
    num_protocols = aceInterfaceConfigToProtoTable(if_config, proto_info);

    /* Restart ACE */
    aceStart(ifname, &proto_info[0], num_protocols, customizeAceHaveAddress, 
             customizeAceHaveAnotherAddress, customizeAceLostAddress);
    customizeAceUnlock();

ret:
}
#endif

/**
 * Called by netosStartTCP to start ACE. The customizeStartAce function performs these steps:
 * <list type="unordered">
 * <item> 1. Reads ACE configuration data from NVRAM.</item>
 * <item> 2. Calls aceInitialize to initialize data structures inside ACE.</item>
 * <item> 3. Registers ACE-level and application-level IP address conflict callbacks.</item>
 * <item> 4. Extracts the protocol specific information for each interface from the NVRAM structure.</item>
 * <item> 5. Calls NAIfconfigBringDeviceUp to start the device for each enabled interface.</item>
 * <item> 6. Calls aceStart for each enabled interface to start the process of getting an IP address.</item>
 * </list>
 *
 * You need to customize this routine if you want to add support for a custom protocol 
 * or change the callback functions.
 *
 * If you want to use a hard-coded configuration for ACE, rather than storing this information in 
 * in NVRAM, it is better to modify the code in aceParams.c than to change the code in this routine.
 *
 * @since       6.0
 * @external
 * @category ACE:ace_callbacks 
 *
 * @return 0        No error.
 * @return -1       Error.
 */

int customizeStartAce(void)
{
    static aceProtocolInfo protoInfo[CONFIG_ACE_MAX_INTERFACES][ACE_MAX_PROTOCOLS];
    static aceConfigInfo aceConfig;
    int result, faceIndex, protoCount = 0, ifCount = 0;
    
    customizeAceCreateLock();

    result = customizeAceGetConfig(&aceConfig);
 
    if (result != BP_SUCCESS)
    {
        customizeErrorHandler(ERROR_ACE_FAILURE, ERROR_SUBCODE_BAD_ACE_CONFIG);
    }
    result = aceInitialize();

#if  BSP_ENABLE_ADDR_CONFLICT_DETECTION == TRUE
    aceRegisterAddrConflictCallback(customizeAceAddrConflict);
    NARegisterAddrConflictCallback(NAAddrConflict);
#endif
    
    ip_configured = IP_STATE_RESTARTING;

    getPresentIpConfig(&default_ip_config);

    for (faceIndex = 0; (faceIndex < CONFIG_ACE_MAX_INTERFACES) && (result == 0); faceIndex++)
    {
        aceConfigInterfaceInfo *nif = &aceConfig.ace_interface_info[faceIndex];

        protoCount = aceInterfaceConfigToProtoTable(nif, &protoInfo[ifCount][0]);

        if (protoCount > 0)
        {

            char *pname = malloc(strlen(nif->ifname) + 1);
            if (pname == NULL)
            {
                result = ENOMEM;
                break;
            }
            memcpy(pname, nif->ifname, strlen(nif->ifname) + 1);

            customizeAceLock();

            if_addr_info[interface_count].ifname = pname;
            interface_count++;

            result = NAIfconfigBringDeviceUp(nif->ifname);
            if (result == 0)
            {
                 result = aceStart(nif->ifname, &protoInfo[ifCount][0], protoCount, 
                                    customizeAceHaveAddress,
                                    customizeAceHaveAnotherAddress,
                                    customizeAceLostAddress);
            }
            customizeAceUnlock();
            
            ifCount++;
        }
    }
    return result;
}

/**
 * Called in customizeAceHaveAddress and customizeAceHaveAnotherAddress to set an
 * IP address and other configuration, obtained by ACE.
 *
 * @since   6.0
 * @external
 * @category ACE:ace_callbacks
 *
 * @param ifname            Null-terminated string that identifies the network interface.
 * @param addr_info         Pointer to IP address information for this interface. 
 *                          Defined in ace.h.
 * @param ip_config         Pointer to global IP configuration for the unit. Defined in 
 *                          ace_params.h.
 *
 * @return 0      No error.
 * @return -1     Interface does not exist.
 * @return "> 0"    Error number, codes are in errno.h.
 */
int customizeAceConfigureInterface(const char  * ifname, aceAddrInfo * addr_info, globalIpConfig * ip_config)
{
    int result, index, i;
    aceAddrInfo  *old_addr_info = NULL;
    char ip_addr[20];

    NA_ASSERT(ifname    != NULL);
    NA_ASSERT(addr_info != NULL);

    /* Configure IP address */
    result = NAIfconfigSetIpAddress(ifname, addr_info->ipaddr, addr_info->netmask, addr_info->mtu);
    if (result != 0)
	goto customizeAceConfigureInterfaceRet;
    
    /* Change IP address for Fast IP */
#ifdef FAST_IP_ENABLED    
    fastip_ip_up();                 /* Update IP addr for Fast IP */
#endif
    
    /*
     * Add a route for this interface's subnet.
     * This routes all IP addresses for the subnet to this interface,
     * instead of the default gateway's interface (if different).
     */
	if (ip_add_static_route(ifname, addr_info->ipaddr & addr_info->netmask, 
			addr_info->netmask, addr_info->ipaddr, 1, UPD_ROUTE) != 0)
	{
		 NAInet_toa(addr_info->ipaddr & addr_info->netmask, ip_addr);
		 NA_DBPF("ACE:  ip_add_static_route failed for %s, subnet %s", ifname, ip_addr);
 	}
        
    /* Add an interface route for the interface with Auto IP address. 
       This will allow route packets to a routable non-Auto IP subnet.
       This interface route can be replaced with the calls to add routes 
       subnets, this Auto IP unit should communicate with, like:
       ip_add_static_route( ifname, <subnet to route to>, <subnet mask to route to>, 0, 1, ADD_ROUTE); 
    */
    if (addr_info->protocol == ACE_PROT_AUTOIP)
    {
        ip_add_static_route(ifname, 0, 0, 0, 1, ADD_ROUTE);
    }

    if ((index = findInterfaceByName(ifname)) < 0)
        goto customizeAceConfigureInterfaceRet;

    old_addr_info = &if_addr_info[index].addr_info;

    /* 
 	 * This interface was default gateway, but now gone, so remove it.
 	 * This will leave us without a default gateway until another interface obtains one.
 	 */
    if (def_gateway != 0 && old_addr_info->def_gateway == def_gateway &&
        addr_info->def_gateway != def_gateway)
    {
    	ip_add_static_route(ifname, 0, 0, def_gateway, 1, DEL_ROUTE);
        def_gateway = 0;
    }

    /* Don't have a default gateway yet, set this one as default */
    if (def_gateway == 0 && addr_info->def_gateway != 0)
    {
        if (ip_add_static_route(ifname, 0, 0, addr_info->def_gateway, 1, UPD_ROUTE) != 0)
        {
             NAInet_toa(addr_info->def_gateway, ip_addr);
             NA_DBPF("ACE:  ip_add_static_route failed for %s, gateway %s", ifname, ip_addr);
             addr_info->def_gateway = 0;
        }
        def_gateway = addr_info->def_gateway;
        if (def_gateway)
        {
 			NAInet_toa(def_gateway, ip_addr);
            NA_DBPF("ACE:  Default gateway set on interface %s: %s\n", ifname, ip_addr);
        }
    }

    /* Add DNS servers */
    removeOldDnsServers(&old_addr_info->name_server_address[0], &addr_info->name_server_address[0]);
    for (i = 0; i < ACE_MAX_DNS_SERVERS; i++)
    {
        if (addr_info->name_server_address[i] != 0)
            DNSAddServer(addr_info->name_server_address[i]);
    }

    /* Store new address information for the interface */
    *old_addr_info = *addr_info;

    /* Update global IP configuration */

    /* 
     * Restore to defaults the IP values that have been configured
     * through this intefrace previously, but not this thime
     */  
    if (default_ip_config.set_mask)
    {
        globalIpConfig restore_ip_config = default_ip_config;
        restore_ip_config.set_mask = if_addr_info[index].changed_ip_mask;

        if (ip_config)
            restore_ip_config.set_mask &= ~ip_config->set_mask;

        setIpConfig(&restore_ip_config);
        if_addr_info[index].changed_ip_mask = 0;
    }
    /* Set IP values, that has been returned as DHCP or BOOTP options */
    if (ip_config && ip_config->set_mask)
    {
         setIpConfig(ip_config);
         if_addr_info[index].changed_ip_mask = ip_config->set_mask;
    }

    /* set ip_configured to IP_STATE_CONFIGURED */
    ip_configured = IP_STATE_CONFIGURED;
	
    customizeAceConfigureInterfaceRet:    
    return result;
}

/**
 * Called in customizeAceHaveAddress and customizeStopAce to remove the IP address and 
 * other configuration, previously obtained by ACE and set by 
 * customizeAceConfigureInterface function.
 *
 * customizeAceClearInterfaceConfiguration leaves the associated device running.
 *
 * @since   6.0
 * @external
 * @category ACE:ace_callbacks
 *
 * @param ifname            Null-terminated string that identifies the network interface.
 *
 * @return 0      No error.
 * @return -1     Interface does not exist.
 * @return "> 0"    Error number, codes are in errno.h.
 */
int customizeAceClearInterfaceConfiguration(const char * ifname)
{
    aceAddrInfo  *addr_info;  
    int result, index, i;

#ifdef FAST_IP_ENABLED
    fastip_ip_down();   /* Disable Fast IP */
#endif

    NA_ASSERT(ifname    != NULL);

    result = NAIfconfigSetIpAddress(ifname, 0, 0, 0);

    index = findInterfaceByName(ifname);
    if (index >= 0)
    {
        addr_info = &if_addr_info[index].addr_info;
        if (addr_info->def_gateway != 0 && addr_info->def_gateway == def_gateway)
        {
           ip_add_static_route(ifname, 0, 0, def_gateway, 1, DEL_ROUTE);
           def_gateway = 0;

           for (i = 0; i < interface_count; i++)
           {
                WORD32 addr = if_addr_info[i].addr_info.ipaddr;
                if (i != index && addr != 0 && if_addr_info[i].addr_info.protocol == ACE_PROT_AUTOIP)
                {
                    /* Add an interface route for the interface with Auto IP address. 
                       This will allow route packets to a routable non-Auto IP subnet.
                       This can be replaced with the call to add a route to a specific subnet.
                    */
                    ip_add_static_route(if_addr_info[i].ifname, 0, 0, 0, 1, ADD_ROUTE);
                }
		    }

        }
        for (i = 0; i < ACE_MAX_DNS_SERVERS; i++)
        {
            if (addr_info->name_server_address[i] != 0)
                DNSRemoveServer(addr_info->name_server_address[i]);
        }
        memset(addr_info, 0, sizeof *addr_info);

        if (if_addr_info[index].changed_ip_mask)
        {
            globalIpConfig ip_config = default_ip_config;
            ip_config.set_mask = if_addr_info[index].changed_ip_mask;

            setIpConfig(&ip_config);
            if_addr_info[index].changed_ip_mask = 0;
        }
    }
  
    return result;
}

/* 
 * The following routines are used for the template 
 * configuration of interfaces, that use ACE to obtain an IP address.
 */
static int findInterfaceByName(const char *ifname)
{
    int index = -1;

    if (ifname != NULL)
    {
        for (index = 0; index < interface_count; index++)
        {
            if (if_addr_info[index].ifname && 
                strncmp(ifname, if_addr_info[index].ifname, NETOS_MAX_IF_NAMELEN) == 0)
            break;
        }
        if (index == interface_count)
            index = -1;
    }
    return index;
}


static void removeOldDnsServers(const WORD32 *old_dns_server, const WORD32 *dns_server)
{
    int i, j;

    for (i = 0; i < ACE_MAX_DNS_SERVERS; i++)
    {
        if (old_dns_server[i])
        {
           for (j = 0; j < ACE_MAX_DNS_SERVERS; j++)
           {
               if (old_dns_server[i] == dns_server[j])
                   break;
           }
           if (j >= ACE_MAX_DNS_SERVERS)
                DNSRemoveServer(old_dns_server[i]);
        }
    }
}

/* 
 * The following routines are used for the template 
 * of global IP configuration with values obtained through ACE.
 */

/*
 * This routine is obtains global IP configuration values, used at present
 */
static void getPresentIpConfig(globalIpConfig *ip_config)
{
    if (ip_config)
    {
        ip_config->default_ttl       = NAIpGetDefaultIpTtl();
        ip_config->default_tcp_ttl   = NAIpGetDefaultTcpTtl();
        ip_config->ka_interval       = NAIpGetKaInterval();
        ip_config->ip_forwarding     = NAIpGetIpForwarding();
        ip_config->ka_garbage        = NA_IP_KA_GARGAGE_DEFAULT;
        ip_config->set_mask          = GBL_IP_SET_ALL;    
    }
}

/*
 * This routine is used to update global IP configuration.
 */
static void setIpConfig(const globalIpConfig *ip_config)
{
    if (ip_config)
    {
        if (ip_config->set_mask & GBL_SET_DEF_IP_TTL)
            NAIpSetDefaultIpTtl(ip_config->default_ttl);

        if (ip_config->set_mask & GBL_SET_DEF_TCP_TTL)
            NAIpSetDefaultTcpTtl(ip_config->default_tcp_ttl);
               
        if (ip_config->set_mask & GBL_SET_KA_INTERVAL)
            NAIpSetKaInterval(ip_config->ka_interval);
       
        if (ip_config->set_mask & GBL_SET_KA_GARBAGE)
            NAIpSetKaGarbage(ip_config->ka_garbage);
       
        if (ip_config->set_mask & GBL_SET_IP_FORWARDING)
            NAIpSetIpForwarding(ip_config->ip_forwarding);
    }
}

/* Default ACE lock implementation */
static TX_MUTEX ace_lock;

static BOOLEAN customizeAceCreateLock(void)
{
    return tx_mutex_create(&ace_lock, "ace lock", TX_INHERIT) == TX_SUCCESS;
}

static void customizeAceLock(void)
{
    tx_mutex_get(&ace_lock, TX_WAIT_FOREVER);  
}

static void customizeAceUnlock(void)
{
    tx_mutex_put(&ace_lock);  
}

static BOOLEAN staticConfigToProtoInfo(aceProtocolInfo *proto_info, configAceStaticInfo *static_config)
{
    BOOLEAN result = FALSE;

    if (static_config->isConfigValid && static_config->isEnabled)
    {
        memcpy(&proto_info->proto_start_info, &static_config->startInfo, sizeof proto_info->proto_start_info);
        if (static_config->auto_assign)
        {
            proto_info->proto_start_info.delay_before_start = 0;
        }
        proto_info->proto_specific_info = static_config;
        proto_info->proto_specific_info_size = sizeof *static_config;
        proto_info->proto_event_info_size    = sizeof (configAceStaticInfo);
        proto_info->start_protocol_fn = ace_start_static;
        proto_info->stop_protocol_fn = ace_stop_static;
        proto_info->get_addr_fn = ace_get_static_addr_info;
        proto_info->release_addr_fn = NULL;
        proto_info->detect_addr_conflict = TRUE;
        result = TRUE;
    }
    return result;
}

static BOOLEAN dhcpConfigToProtoInfo(aceProtocolInfo *proto_info, configAceDhcpInfo *dhcp_config)
{
    BOOLEAN result = FALSE;

    if (dhcp_config->isConfigValid && dhcp_config->isEnabled)
    {
        memcpy(&proto_info->proto_start_info, &dhcp_config->startInfo, sizeof proto_info->proto_start_info);
        proto_info->proto_specific_info = dhcp_config;
        proto_info->proto_specific_info_size = sizeof *dhcp_config;
        proto_info->proto_event_info_size    = ace_dhcp_event_info_size;
        proto_info->start_protocol_fn = ace_start_dhcp;
        proto_info->stop_protocol_fn = ace_stop_dhcp;
        proto_info->get_addr_fn = ace_get_dhcp_addr_info;
        proto_info->release_addr_fn = ace_release_dhcp_addr;
        if (dhcp_config->arp_reply_timeout >= 0)
            proto_info->detect_addr_conflict = FALSE;
        else
            proto_info->detect_addr_conflict = TRUE;
        result = TRUE;
    }
    return result;
}

static BOOLEAN bootpConfigToProtoInfo(aceProtocolInfo *proto_info, configAceBootpInfo *bootp_config)
{
    BOOLEAN result = FALSE;

    if (bootp_config->isConfigValid && bootp_config->isEnabled)
    {
        memcpy(&proto_info->proto_start_info, &bootp_config->startInfo, sizeof proto_info->proto_start_info);
        proto_info->proto_specific_info = bootp_config;
        proto_info->proto_specific_info_size = sizeof *bootp_config;
        proto_info->proto_event_info_size    = ace_bootp_event_info_size;
        proto_info->start_protocol_fn = ace_start_bootp;
        proto_info->stop_protocol_fn = ace_stop_bootp;
        proto_info->get_addr_fn = ace_get_bootp_addr_info;
        proto_info->release_addr_fn = NULL;
        proto_info->detect_addr_conflict = TRUE;
        result = TRUE;
    }
    return result;
}

static BOOLEAN autoipConfigToProtoInfo(aceProtocolInfo *proto_info, configAceAutoipInfo *autoip_config)
{
    BOOLEAN result = FALSE;

    if (autoip_config->isConfigValid && autoip_config->isEnabled)
    {
        memcpy(&proto_info->proto_start_info, &autoip_config->startInfo, sizeof proto_info->proto_start_info);
        proto_info->proto_specific_info = autoip_config;
        proto_info->proto_specific_info_size = sizeof *autoip_config;
        proto_info->proto_event_info_size    = sizeof (aceAutoIpEventInfo);
        proto_info->start_protocol_fn = ace_start_autoip;
        proto_info->stop_protocol_fn = ace_stop_autoip;
        proto_info->get_addr_fn = ace_get_autoip_addr_info;
        proto_info->release_addr_fn = NULL;
        proto_info->detect_addr_conflict = FALSE;
        result = TRUE;
    }
    return result;
}

static BOOLEAN rarpConfigToProtoInfo(aceProtocolInfo *proto_info, configAceRarpInfo *rarp_config)
{
    BOOLEAN result = FALSE;

    if (rarp_config->isConfigValid && rarp_config->isEnabled)
    {
        memcpy(&proto_info->proto_start_info, &rarp_config->startInfo, sizeof proto_info->proto_start_info);
        proto_info->proto_specific_info = rarp_config;
        proto_info->proto_specific_info_size = sizeof *rarp_config;
        proto_info->proto_event_info_size    = sizeof (aceRarpEventInfo);
        proto_info->start_protocol_fn = ace_start_rarp;
        proto_info->stop_protocol_fn = ace_stop_rarp;
        proto_info->get_addr_fn = ace_get_rarp_addr_info;
        proto_info->release_addr_fn = NULL;
        proto_info->detect_addr_conflict = TRUE;
        result = TRUE;
    }
    return result;
}

static BOOLEAN pingarpConfigToProtoInfo(aceProtocolInfo *proto_info, configAcePingArpInfo *ping_arp_config)
{
    BOOLEAN result = FALSE;

    if (ping_arp_config->isConfigValid && ping_arp_config->isEnabled)
    {
        memcpy(&proto_info->proto_start_info, &ping_arp_config->startInfo, sizeof proto_info->proto_start_info);
        proto_info->proto_specific_info = NULL;
        proto_info->proto_specific_info_size = 0;
        proto_info->proto_event_info_size    = sizeof (acePingArpEventInfo);
        proto_info->start_protocol_fn = ace_start_ping_arp;
        proto_info->stop_protocol_fn = ace_stop_ping_arp;
        proto_info->get_addr_fn = ace_get_ping_arp_addr_info;
        proto_info->release_addr_fn = NULL;
        proto_info->detect_addr_conflict = TRUE;
        result = TRUE;
    }
    return result;
}

static int aceInterfaceConfigToProtoTable(aceConfigInterfaceInfo *nif, aceProtocolInfo *proto_info)
{
    int protoCount = 0;

    if (nif && proto_info && (nif->isConfigValid) && (nif->isEnabled))
    {
        if (staticConfigToProtoInfo(&proto_info[protoCount], &nif->static_config) == TRUE)
           protoCount++;

        if ((!nif->static_config.isConfigValid) || (!nif->static_config.isEnabled) || (!nif->static_config.auto_assign))
        {
            if (dhcpConfigToProtoInfo(&proto_info[protoCount], &nif->dhcp_config) == TRUE)
                protoCount++;
 
            if (bootpConfigToProtoInfo(&proto_info[protoCount], &nif->bootp_config) == TRUE)
                protoCount++;
 
 
            if (autoipConfigToProtoInfo(&proto_info[protoCount], &nif->autoip_config) == TRUE)
                protoCount++;

            if (rarpConfigToProtoInfo(&proto_info[protoCount], &nif->rarp_config) == TRUE)
                protoCount++;


            if (pingarpConfigToProtoInfo(&proto_info[protoCount], &nif->ping_arp_config) == TRUE)
                protoCount++;
        }
    }
    return protoCount;
}


#if  BSP_ENABLE_ADDR_CONFLICT_DETECTION == TRUE

/**
 * customizeStartACE calls this function to registers an IP address conflict callback, 
 * which is called if an IP address conflict occurs during initial IP address configuration. 
 *
 * @param conflict_fn    Pointer to a Ip address conflict callback, 
 *                       can be NULL.
 *
 * @since 6.2
 * @external
 * @category Interface_Config
 */
void NARegisterAddrConflictCallback(NAAddrConflictCallbackType conflict_fn)
{
    addr_conflict_fn = conflict_fn;
}

#endif

