/*****************************************************************************
* Copyright (c) 2005 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
*
*****************************************************************************/
#include "boardParams.h"
#include "iam.hh"
#include "starttcp.h"
#include "bsp_net.h"
#include "bsp_api.h"
#include <dhcpv6.h>

/* #define CUSTOMIZE_DHCP_UPDATE_REQ */

static const char *iam_method_string[] = {"STATIC", "DHCP", "AUTOIP", \
                                          "USER","","AUTO-CONFIGURED", "DHCPv6", "STATICv6"};
static const char *iam_ip_version_string[] = {"IPv4", "IPv4", "IPv4", \
                                              "IPv4","","IPv6", "IPv6", "IPv6"};

#if defined(AUTODOC_VERSION)
/*ad*
 * This list comprises IPv4 acquisition methods IAM attempts to use to acquire an IPv4
 * address; however, the methods actually used depend on NVRAM settings. This list 
 * is passed into @link naIamStart in function @link customizeIamStart.
 * The order of the list determines the priority assigned to the methods with the above 
 * definition  making static the highest priority, followed by DHCPv4 and Auto IP. 
 * Priority determines how IAM treats acquired addresses. For example, the above priority
 * scheme ensures that static addresses will always be configured even when a previously
 * acquired DHCPv4 or Auto IP address has been configured on an interface. In this case,
 * the lower priority address is removed from the interface and replaced with the newly
 * acquired static address. Consequently, when a static address is configured first on an
 * interface and a lower priority address is obtained after the fact, the lower priority
 * address is ignored. The default method callbacks do not use any context arguments. 
 * A user defined method callback can be included in this list in the desired priority
 * slot along with the NA_IAM_METHOD_USER identifier. The last
 * element of the list has to be set to NA_IAM_NULL_METHOD_CB to mark its end.
 *
 * @external
 * @include "iam.hh"
 * @category Networking:IAM:callbacks
 * @since 7.0
 *
*ad*/   
static NaIamMethodInfo_t method_list[] = \
            {{NA_IAM_DEFAULT_STATIC_METHOD_CB, NULL, NA_IAM_METHOD_STATIC},
             {NA_IAM_DEFAULT_DHCP_METHOD_CB,   NULL, NA_IAM_METHOD_DHCP},
             {NA_IAM_DEFAULT_AUTOIP_METHOD_CB, NULL, NA_IAM_METHOD_AUTOIP},
             {NA_IAM_NULL_METHOD_CB,           NULL, NA_IAM_METHOD_NONE}};  
#endif
   
static NaIamMethodInfo_t method_list[] = \
            {
#ifdef NA_ARP_PING_ENABLED
             {NA_IAM_DEFAULT_USER_METHOD_CB, NULL, NA_IAM_METHOD_USER},
#endif
             {NA_IAM_DEFAULT_STATIC_METHOD_CB, NULL, NA_IAM_METHOD_STATIC},
             {NA_IAM_DEFAULT_DHCP_METHOD_CB,   NULL, NA_IAM_METHOD_DHCP},
             {NA_IAM_DEFAULT_AUTOIP_METHOD_CB, NULL, NA_IAM_METHOD_AUTOIP},
             {NA_IAM_NULL_METHOD_CB,           NULL, NA_IAM_METHOD_NONE}};                

/* Number of interfaces we have to configure
 * before starting the application layer. */
#if BSP_WANT_ETHERNET && BSP_WANT_WIRELESS
#define NUM_INTERFACES 2
/* We assume that one is enabled, otherwise we will
 * never get this far in code execution. */
#else
#define NUM_INTERFACES 1
#endif

/*
 * This structure is used to determine when all interfaces have been
 * configured. This is needed in order to prevent the application
 * layer from starting before the interfaces are minimally configured.
 * Minimal configuration means one configured IP address per interface,
 * two, one v4 and one v6, addresses if running a dual stack.
 */ 
static struct {
    void *interface;    /*Interface handle*/
    int  num_addrs;     /*number of v4 addresses configured*/
#ifdef IPV6_ENABLED
    int  num_v6_addrs;  /*number of v6 addresses configured */
#endif
} addrs_cfg[NUM_INTERFACES];

extern int naIamGetDns(const char * ifName, struct sockaddr_storage * dnsAddr, int is_primary);

/* Return index of interface, used to access addrs_cfg array. */ 
static int addr_cfg_get_if_index(void * interface)
{
    static int init = 0;
    int i = 0;

    if (!init)
    {
        memset(addrs_cfg, 0, sizeof addrs_cfg);
        init = 1;
    }        

    /* First, attempt to find existing interface. */
    for (i = 0; i < NUM_INTERFACES; i++)
    {
        if (addrs_cfg[i].interface == interface)
        {
            goto exit;
        }
    }

    /* If we didn't find an interface, add it to
     * next available spot. */
    for (i = 0; i < NUM_INTERFACES; i++)
    {
        if (addrs_cfg[i].interface == NULL)
        {
            addrs_cfg[i].interface = interface;
            goto exit;
        }
    }
exit:
    return i;
}    

/* Add ip address to interface. */
static void addr_cfg_acquired(void * interface, int family)
{
    int i;

    i = addr_cfg_get_if_index(interface);

#if IPV6_ENABLED
    if (family == AF_INET6)
        addrs_cfg[i].num_v6_addrs++;
    else
#endif
        addrs_cfg[i].num_addrs++;
}

/* Remove ip address from interface. */ 
static void addr_cfg_lost(void * interface, int family)
{
    int i;
     
    i = addr_cfg_get_if_index(interface);

#if IPV6_ENABLED
    if (family == AF_INET6)
        addrs_cfg[i].num_v6_addrs--;
    else
#endif
        addrs_cfg[i].num_addrs--;
}

/*Return 1 if interface is configured, 0 otherwise. */
static int addr_cfg_is_configured(void *interface)
{
    int result = 1;
    int interfaceIndex = addr_cfg_get_if_index(interface);
    
    result &= addrs_cfg[interfaceIndex].num_addrs ? 1 : 0;
#if IPV6_ENABLED
    result &= addrs_cfg[interfaceIndex].num_v6_addrs ? 1 : 0;
#endif
    
    return result; 
}


/*ad*
 * This function is the default user callback invoked by IAM for address
 * events. It may be called from the TCP/IP stack's timer task context when
 * address conflict detection is enabled or an asynchronous event such as
 * a DHCP lease expiration is received, hence the processing time spent in this
 * function should be kept to an accepted minimum.
 *
 * The default implementation of this function accepts all acquired addresses
 * and does not configure any IPv4 addresses found to be in initial conflict by returning
 * NA_IAM_STATUS_ERROR. In addition, this function accepts the first DHCP offer received
 * by the DHCP client and does not stop ongoing conflict detection when either
 * an IPv4 or IPv6 address is found to be in conflict after being configured on an interface.
 * The customization of this function is left up to the user. 
 *
 * IAM calls this function for the following events:
 *
 * <list type="unordered">
 *	 <item>@link NA_IAM_ADDR_CONFIGURED - IPv4 and IPv6. The interface/multihome has been configured with an acquired
 *	                                      address. For IPv6, the address passed Duplicate Address Detection(DAD) and 
 *                                        has been configured on the interface.</item>
 *	 <item>@link NA_IAM_ADDR_LOST -  The interface/multihome has lost its configured IP address. For IPv4, the 
 *                                   address is removed when the callback returns. For IPv6, this
 *                                   event is called when a stateless auto-configured address has been
 *                                   invalidated and removed.</item>
 *	 <item>@link NA_IAM_ADDR_CONFLICT - The configured IPv4/IPv6 address has been found to be in conflict with 
 *	                                 another node on the network. Returning NA_IAM_STATUS_ERROR disables 
 *                                   ongoing Address Conflict Detection(ACD) for the IPv4 address. Auto IP always
 *                                   enables ongoing ACD.</item>
 *   <item>@link NA_IAM_ADDR_INITIAL_CONFLICT - The acquired IPv4/IPv6 address has been found to be in conflict with 
 *	                                 another node on the network. An acquired IP address is one that has
 *									 not been used to configure an interface/multihome. For IPv4, this event can
 *									 only be generated if initial ACD has been enabled for an acquisition method 
 *									 If NA_IAM_STATUS_SUCCESS is returned for IPv4
 *                                   this address is used to configure the interface in spite of being in conflict.
 *                                   Returning NA_IAM_STATUS_ERROR ignores the IPv4 address and does not configure it.
 *                                   For IPv6, this address cannot be used to configure the interface and this 
 *                                   event is informational only. DAD is always enabled for IPv6 and if the
 *                                   auto-configured address in conflict is a link-local scope address, IAM generates
 *                                   a random interface ID to form a new IPv6 link-local address as part of its
 *                                   auto-recovery mechanism, which ensures that a valid link-local address is 
 *                                   always generated. Due to the implementation of the DHCPv4 client, initial ACD
 *                                   is always performed for DHCP and an address in conflict is never allowed to
 *                                   be configured, hence this event will never be reported for DHCPv4.
 *                                   Auto IP also always performs initial ACD and like DHCP Auto IP will never report
 *                                   this event; however, unlike DHCP, Auto IP generates random addresses to
 *                                   recover from initial conflicts, similarly to the auto-configured IPv6 link-local
 *                                   auto-recovery mechanism described above.   
 *                                   </item>
 *   <item>@link NA_IAM_ADDR_OFFER - The DHCPv4 client has received an offer that can be accepted by returning 
 *                                   NA_IAM_STATUS_SUCCESS or rejected by returning NA_IAM_STATUS_ERROR.
 *                                   If user wants to update the hostname and/or FQDN options based on the OFFER 
 *                                   this is the right spot to do so. Client can use Treck API function 
 *                                   tfDhcpUserSet() to update above mentioned options. The sample code snippet 
 *                                   for this is shown below.
 *                                  
 *    <example>          
 * 
 *        #define CUSTOMIZED_HOST_NAME   "DigiClient"
 *        #define CUSTOMIZED_FQDN        "DigiClient.digi.com" 
 *
 *        if (tfDhcpUserSet (interface, multihome, TM_DHCPF_HOST_NAME, 0, 
 *                           (unsigned char *)CUSTOMIZED_HOST_NAME, 
 *                           strlen(CUSTOMIZED_HOST_NAME)) != TM_ENOERROR)
 *        {
 *            printf("Failed to update hostname option\n");
 *        }
 *
 *                       
 *        if (tfDhcpUserSet (interface, multihome, 
 *                           TM_DHCPF_FQDN_ENABLE | TM_DHCPF_FQDN_ASCII, 0,
 *                           (unsigned char *)CUSTOMIZED_FQDN, 
 *                           strlen(CUSTOMIZED_FQDN)) != TM_ENOERROR)
 *        {
 *            printf("Failed to update FQDN option\n");
 *        }
 *        
 *    </example>
 *                                   </item>
 *   <item>@link NA_IAM_ADDR_LINK_DOWN - The network interface has gone down.  This may be caused by a network
 *                                       switch failure, by a user pulling the Ethernet cable, or by a problem
 *                                       on the wireless access point.  It is possible that the IP address assigned
 *                                       to the interface will no longer be valid when the interface comes back
 *                                       up.  Therefore the address acquisition process will be restarted when the
 *                                       interface comes back up.  If the gateway assigned to this interface was
 *                                       the default gateway, then the default gateway will be switched to the gateway
 *                                       on the next highest priority interface.  See 
 *                                       @link Networking:Interface_Config:naIfConfigCreateInterface for details. </item>
 *          
 * </list>     
 *
 * @external
 * @include "iam.hh"
 * @category Networking:IAM:callbacks 
 * @since 7.0
 *
 * @param ifname        nul-terminated interface name string in the "ethx:x" format.    
 * @param method        IAM aquisition method.
 * @param event         IAM acquisition event.
 * @param acquiredCfg   Configuration acquired by IAM.
 * @param userData      User context data pointer passed into @link naIamStart. 
 *
 * @return NA_IAM_STATUS_SUCCESS    Success.
 * @return NA_IAM_STATUS_ERROR      Error.     
*ad*/
NaIamStatus_t customizeIamEventCallback(const char * ifname, int method, int event, NaIamAcquiredCfg_t * acquiredCfg, void * userData)
{

    NaIamStatus_t status = NA_IAM_STATUS_SUCCESS; /* Assume success. */
    static int configured = 0;
    static int callbackInstalled = 0;
    char addrString[MAX_IP_ADDR_STR_LEN];
    char af;
    char *ip_addr;
    void * interface;
    unsigned multihome;

    naIfConfigGetInterfaceHandle(ifname, &interface, &multihome);
        
    (void)userData;

    switch(event)
    {
        case NA_IAM_ADDR_CONFIGURED: /* Acquired IP address. */
        {                
#ifdef IPV6_ENABLED
            /* Both stateless auto-configured IPv6 addresses and
             * stateful DHCPv6 configured addresses are supported.
             */
            if (method == NA_IAM_METHOD_IPV6_AUTOCFG || 
				method == NA_IAM_METHOD_DHCPV6		 ||
				method == NA_IAM_METHOD_STATICV6)
            {
                /* Register the IPv6 address. */
                addr_cfg_acquired(interface, AF_INET6);
                af = AF_INET6;
                ip_addr = (char *)&acquiredCfg->ipAddress.addr.ipv6.sin6_addr;
            }
            else 
#endif
            {
                /* Register the IPv4 address. */
                addr_cfg_acquired(interface, AF_INET);
                af = AF_INET; 
                ip_addr = (char *)&acquiredCfg->IPV4_ADDR(ipAddress);
            }

            /* Convert address to ASCII. */
            inet_ntop(af, ip_addr, addrString, MAX_IP_ADDR_STR_LEN);
 
            printf("IAM:Got %s %s address %s on interface %s\n",
                         iam_method_string[method], iam_ip_version_string[method], 
                         addrString, ifname);

            if (!configured) /* Do only once. */
            {
                /* Determine if interface is configured.*/
                if (addr_cfg_is_configured(interface))
                {
                    /* Let the ROOT task know that we have configured all required
                     * ip addresses on an interface, which allows network 
                     * applications to come up. We require both v4 and v6
                     * addresses if running in dual stack mode. */
                    NASetIPConfigureState(naIpStateConfigured);
                    configured = 1;
                }
            }      
            /*
             * This is a little questionable.  We allow the system to
             * come up with a statically assigned IP address, even if the Ethernet 
             * cable is disconnected.  That's what the if statment above does.
             * Then, we install the Ethernet link callback in the code below.  If
             * the cable is disconnected, then the callback will disable the 
             * interface immediately.  The effect is to let the system come up with
             * a statically allocated IP address even if the cable is pulled, but 
             * then disable the network interface, which makes sure the correct
             * default gateway is setup if we also have a wireless interface.
             */
            if (strncmp(ifname, BP_ETH_INTERFACE, strlen(BP_ETH_INTERFACE)) == 0)
            {
                if ((addr_cfg_is_configured(interface)) && (callbackInstalled == 0))
                {
                    iamInstallCallback(interface); 
                    callbackInstalled = 1;
                }
            }
#ifdef IPV6_ENABLED
#ifdef TM_6_USE_DHCP 
            if (method == NA_IAM_METHOD_DHCPV6)
            {
                NaStatus errorCode = 0;
                NADhcpv6UserBootEntry * dhcpv6UserBootEntry = NULL;

                /* confirm validity of leased addresses and configuration information received from DHCPv6 server */
#if 0           /* call to naDhcpv6Confirm() is commented out to reduce printf() messages when NET+OS starts up */
                errorCode = naDhcpv6Confirm(interface, 0, 0);
                if (errorCode)
                {
                    printf("customizeIamEventCallback: naDhcpv6Confirm() failed with error %u\n", (unsigned int)errorCode);
                    status = NA_IAM_STATUS_ERROR;
                }
#endif
                /* Retrieve DHCPv6 related information received */
                errorCode = naDhcpv6GetBootEntry(interface, 0, 0, &dhcpv6UserBootEntry);
                if (errorCode)
                {
                    printf("customizeIamEventCallback: naDhcpv6GetBootEntry() failed with error %u\n", (unsigned int)errorCode);
                    status = NA_IAM_STATUS_ERROR;
                }

                /*
                 * TBD print messages to display the DHCPv6 information the DHCPv6 server send us.
                 */

                /* Free the memory used to store the DHCPv6 related information received */
                errorCode = naDhcpv6FreeBootEntry(dhcpv6UserBootEntry, 0);
                if (errorCode)
                {
                    printf("customizeIamEventCallback: naDhcpv6FreeBootEntry() failed with error %u\n", (unsigned int)errorCode);
                    status = NA_IAM_STATUS_ERROR;
                }
            }
#endif
#endif
        }
        break;

        case NA_IAM_ADDR_LOST: /* Lost IP address. */
        {
            BOOLEAN unspec; // have lost 0 IP address, because naIamDisable was called?
#ifdef IPV6_ENABLED
            if (method == NA_IAM_METHOD_IPV6_AUTOCFG || 
				method == NA_IAM_METHOD_DHCPV6		 ||
				method == NA_IAM_METHOD_STATICV6)
            {
                unspec = IN6_IS_ADDR_UNSPECIFIED(&(acquiredCfg->ipAddress.addr.ipv6.sin6_addr));
                if (!unspec)
                {
                    /* Register lost IPv6 address. */
                    addr_cfg_lost(interface, AF_INET6); 
                    af = AF_INET6;
                    ip_addr = (char *)&acquiredCfg->ipAddress.addr.ipv6.sin6_addr;
                }
            }
            else
#endif
            {
                unspec = acquiredCfg->IPV4_ADDR(ipAddress) == 0;
                if (!unspec)
                {
                    /* Register lost IPv6 address. */
                    addr_cfg_lost(interface, AF_INET);
                    af = AF_INET; 
                    ip_addr = (char *)&acquiredCfg->IPV4_ADDR(ipAddress);
                }
            }
            
            if (!unspec)
            {
                /* Convert address to ASCII. */
                inet_ntop(af, ip_addr, addrString, MAX_IP_ADDR_STR_LEN);

                printf("IAM - lost %s %s address %s on interface %s\n",
                           iam_method_string[method], iam_ip_version_string[method], addrString, ifname);
            }
        }            
        break;

        /* This event is informational only for IPv6 as all auto-configured 
         * addresses are never configured if found to be in initial conflict.
         * In the case of an auto-configured link-local scope IPv6 address
         * found to be in initial conflict, a random EUI64 Interface ID is
         * generated by IAM to form a new link-local address for the interface
         * after this function returns. */ 
        case NA_IAM_ADDR_INITIAL_CONFLICT:/* Initial IP address conflict.*/
        {
#ifdef IPV6_ENABLED
            if (method == NA_IAM_METHOD_IPV6_AUTOCFG)   
            {
                af = AF_INET6;
                ip_addr = (char *)&acquiredCfg->ipAddress.addr.ipv6.sin6_addr;
            }
            else
#endif
            {
                af = AF_INET; 
                ip_addr = (char *)&acquiredCfg->IPV4_ADDR(ipAddress);
            }

            inet_ntop(af, ip_addr, addrString, MAX_IP_ADDR_STR_LEN);

            printf("IAM - %s %s address %s on interface %s is in initial conflict!\n",
                                    iam_method_string[method], iam_ip_version_string[method], addrString, ifname);

            /* If the IPv6 auto-configured link-local scope address is in 
             * conflict, a random EUI64 interface ID will be generated to configure
             * a new link local address. Other IPv6 auto-configured addresses are
             * are never configured when they're in initial conflict.*/
#ifdef IPV6_ENABLED 
            if (method == NA_IAM_METHOD_IPV6_AUTOCFG &&
                IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)ip_addr))
            {
                printf("      A random EUI64 Interface ID will be generated.\n");  
            }
            else
#endif
            if (af == AF_INET)
            {
                printf("      This address will not be configured. \n");  
                /* By returning an error, the address in conflict is not 
                 * configured on the interface. */
                status = NA_IAM_STATUS_ERROR;
            }
            else
            {
                printf("      This auto-configured address will not be used. \n");
            }     
        }            
        break;

        case NA_IAM_ADDR_CONFLICT:/* Ongoing IP address conflict.*/
        {
#ifdef IPV6_ENABLED
            if (method == NA_IAM_METHOD_IPV6_AUTOCFG)   
            {
                af = AF_INET6;
                ip_addr = (char *)&acquiredCfg->ipAddress.addr.ipv6.sin6_addr;
            }
            else
#endif
            {
                af = AF_INET; 
                ip_addr = (char *)&acquiredCfg->IPV4_ADDR(ipAddress);
            }

            inet_ntop(af, ip_addr, addrString, MAX_IP_ADDR_STR_LEN);

            printf("IAM - %s %s address %s on interface %s is in conflict!\n",
                            iam_method_string[method], iam_ip_version_string[method], 
                            addrString, ifname);

			if (method == NA_IAM_METHOD_AUTOIP)
				naIamRelease(ifname, NA_IAM_METHOD_AUTOIP);
				
#ifdef NA_ARP_PING_ENABLED
			if (method == NA_IAM_METHOD_USER)
				naIamRelease(ifname, NA_IAM_METHOD_USER);
#endif
        }            
        break;

        case NA_IAM_ADDR_OFFER: /* DHCP offer*/
            /* We always Accept the first DHCP offer. This is where an offer
             * may be parsed and inspected. Returning NA_IAM_STATUS_ERROR
             * rejects the offer. */ 
#ifdef CUSTOMIZE_DHCP_UPDATE_REQ
#define CUSTOMIZED_HOST_NAME   "DigiClient"
#define CUSTOMIZED_FQDN        "DigiClient.digi.com"  
        if (strncmp(ifname, BP_ETH_INTERFACE, strlen(BP_ETH_INTERFACE)) == 0)
        {
            char fqdn[23];

            strcpy(&fqdn[3], CUSTOMIZED_FQDN);
            fqdn[0]  = 1; /* flag */
            fqdn[22] = '\0'; /* null terminated to indicate not partial */
                        
            /* update host name */            
            if (naIfConfigSetDhcpOption (interface, multihome, TM_DHCPEXT_HOST_NAME, 
                                         CUSTOMIZED_HOST_NAME, (unsigned char) strlen(CUSTOMIZED_HOST_NAME)) != NASTATUS_SUCCESS)
            {
                status = NA_IAM_STATUS_ERROR;
                break;
            }

            /* update FQDN */            
            if (naIfConfigSetDhcpOption (interface, multihome, TM_DHCPEXT_FQDN, 
                               fqdn,  23) != NASTATUS_SUCCESS)
            {
                status = NA_IAM_STATUS_ERROR;
                break;
            }
        }
#endif
        break;
        
        case NA_IAM_ADDR_LINK_DOWN: /* Someone pulled the Ethernet cable */
        {                           
            /*
             * The safest thing to do is to tell the stack the interface has been disconnected.
             * Unfortunately, this has a nasty side effect:  We lose the IP configuration for that
             * interface and have to restart the IP address acquisition process from scratch when 
             * the link comes up again.  Other options are to do nothing, or to just switch the 
             * default router.
             */
            return NA_IAM_DISCONNECT_INTERFACE;
        }
 
    }

    return status;
}

      
/*ad*
 * This function is called from the ROOT task to start IAM on network interfaces
 * enabled in NVRAM. Default acquisition methods, Static, DHCP, and Auto IP are started
 * for each interface in this order if they are enabled in NVRAM. The order in which
 * these methods are started determines their priority, hence Static always has the
 * highest priority, followed by DHCP and AutoIP. This means that when a Static address
 * is obtained for an interface/multihome pair after a DHCP or an Auto IP address has been
 * used to configure the interface, the configured address will be unconfigured and the Static
 * address will be used to configure it instead. 
 *
 * This function calls @link naIamStart with the @link customizeIamEventCallback callback function
 * as the "app_cb" parameter argument. @link customizeIamEventCallback is the default application callback used
 * with IAM and it is called by IAM for events such as address acquisition or address lost
 * events. See the description of this function for its default implementation details,
 * which can be modified as needed. 
 *
 * The "user_data" parameter of @link naIamStart is given a NULL argument since the default
 * implementation of @link customizeIamEventCallback does not use this context data pointer passed into
 * this function in the "userData" parameter. The "flag" and "buffersPerFrameCount" parameters of
 * @link naIamStart are passed in arguments retrieved by calling @link naIfConfigGetInterfaceParams.
 * @link naIamStart should always be called with configuration parameters returned by
 * @link naIfConfigGetInterfaceParams as they are platform specific and should not be modified by the 
 * application.
 *
 * When running in dual stack mode, @link naIamStart begins the acquisition of a stateless
 * auto-configured link-local IPv6 address for each physical interface automatically; however,
 * at least one acquisition method for IPv4 must be started as well even if it is disabled in 
 * NVRAM. See @link naIamStart for a detailed description of its usage.             
 *
 * @external
 * @include "iam.hh"
 * @category Networking:IAM:iam_params 
 * @since 7.0
 *
 * @return 0       Success.
 * @return -1      Error.     
*ad*/      
int customizeIamStart(void) 
{
    NaIamParamsInfo_t * iam_info;
    int result = 0, j = 0;
    NaIfInterfaceConfigureParams_t cfg_params;

    iam_info = (NaIamParamsInfo_t *)malloc(sizeof *iam_info);
    if (iam_info == NULL)
    {
        printf("customizeIamStart: malloc() failed!\n");
        result = -1;
        goto exit;
    }

    /* Read the whole IAM NVRAM structure */
    if (customizeIamGetConfig(iam_info) == BP_FAILURE)
    {
        printf("customizeIamStart: customizeIamGetConfig() failed!\n");
        result = -1;
        goto exit;
    }

    for (j = 0; j < NA_IAM_INTERFACE_MAX; j++)
    {
#if	BSP_ETH_WIFI_BRIDGE
		/* if bridge is enabled, iam is only interested in BP_EWB_INTERFACE */
        if (strncmp(iam_info->iamParams[j].ifname, BP_ETH_INTERFACE,
				strlen(BP_ETH_INTERFACE)) == 0)  
		{
        	strncpy(iam_info->iamParams[j].ifname, BP_EWB_INTERFACE,
				strlen(BP_EWB_INTERFACE));
		}
        if (strncmp(iam_info->iamParams[j].ifname, BP_WLN_INTERFACE,
				strlen(BP_WLN_INTERFACE)) == 0)  
		{
            iam_info->iamParams[j].isEnabled = FALSE;
		}
#else
    #if !BSP_WANT_ETHERNET            
        if (strncmp(iam_info->iamParams[j].ifname, BP_ETH_INTERFACE, strlen(BP_ETH_INTERFACE)) == 0)  
            iam_info->iamParams[j].isEnabled = FALSE;
    #endif
    #if !BSP_WANT_WIRELESS
        if (strncmp(iam_info->iamParams[j].ifname, BP_WLN_INTERFACE, strlen(BP_WLN_INTERFACE)) == 0)  
            iam_info->iamParams[j].isEnabled = FALSE;
    #endif
#endif	/* not BSP_ETH_WIFI_BRIDGE */
        /* Only start IAM on an interface that has been enabled for IAM. */ 
        if (iam_info->iamParams[j].isEnabled)
        {
            /* Retrieve interface specific parameters. */
            if (naIfConfigGetInterfaceParams(iam_info->iamParams[j].ifname,
                                             &cfg_params) != NASTATUS_SUCCESS)
            {
                printf("customizeIamStart: naIfConfigGetInterfaceParams failed!\n");
                result = -1;
                goto exit;
            }        
                
            if (naIamStart(iam_info->iamParams[j].ifname,
                           method_list,
                           cfg_params.if_config_flags,
                           cfg_params.buffers_per_frame,  
                           NULL,
                           customizeIamEventCallback) != NA_IAM_STATUS_SUCCESS)
            {
                printf("customizeIamStart: naIamStart() failed for interface %s\n",
                                                     iam_info->iamParams[j].ifname);
                result = -1;
                goto exit;
            }
        }
    }

exit:
    if (iam_info)
    {
        free(iam_info);
    }

    return result;
}


/*ad*
 * This function retrieves IPv4/6 addresses and other relevant configuration parameters
 * assigned to the network interface specified by ifname. Any nonexistent
 * addresses are zeroed out by this function. For IPv6, there may be 
 * more than one auto-configured address on a single interface and a link-local
 * address must always exist in order for that IPv6 interface to be configured. The
 * link-local address is always the first element of the "autoCfgInfo.addr" array and will
 * always have a prefix length of 64. 
 * 
 * NET+OS by default configures static or DHCP IPv4 address on "eth0" or "wln0" (home 0),
 * Auto IP address on "eth0:0" or "wln0:0" (home 1), Static IPv6 address on 
 * "eth0:1" or "wln0:1" (home 2), DHCPv6 address on "eth0:7" or "wln0:7" (home 8),
 * and auto-configured IPv6 addresses on "eth0:3" to "eth0:6" or "wln0:3" to "wln0:6"
 * (homes 4 to 7).
 *
 * For example, if this function is called with ifname "eth0:3" (home index 4), 
 * addrInfo.v4Info.ipAddress would have a IPv4 address, configured eth0:3 (no address
 * by default). addrInfo.v6Info.ipAddress would have an IPv6 link-local autoconfigured
 * address, and the same address would be repeated in addrInfo->v6Info.autoCfgInfo.addr[0].
 * addrInfo->v6Info.autoCfgInfo.addr[1], addrInfo->v6Info.autoCfgInfo.addr[2], and
 * addrInfo->v6Info.autoCfgInfo.addr[3] would have auto-configuredIPv6 addresses, 
 * if Ipv6 routers are reachable on the network.
 *
 * If an IPv4 address is configured on this interface home, addrInfo->method field has 
 * an IPv4 IAM acquisition method, otherwise addrInfo->method has an IPv6 IAM acquisition 
 * method. If neither IPv4 nor Ipv6 address is configured on this interface home, 
 * addrInfo->method is NA_IAM_METHOD_NONE.
 *
 *
 * @note In order to find out if the interface home has a configured IPv4 address, 
 *       it's necessary to check that the IPv4 address value is not 0 in addition to 
 *       checking the return code of the customizeIamGetIfAddrInfo:
 *
 *  if (customizeIamGetIfAddrInfo("eth0", &addrInfo) == NA_IAM_STATUS_SUCCESS &&
 *      addrInfo.v4Info.IPV4_ADDR(ipAddress) != 0)
 *
 * @external
 * @include "iam.hh"
 * @category Networking:IAM:iam_params 
 * @since 7.0
 *
 * @see NA_IAM_STATICV6_INDEX
 * @see NA_IAM_DHCPV6_INDEX
 * @see customizeIamGetIfAddress
 * @see customizeIamGetIfconfig
 *
 * @param ifname        nul-terminated interface name string in the "ethx:x" format.
 * @param addrInfo      Pointer to a caller allocated interface address information
 *                      structure filled in by this function.
 *
 * @return NA_IAM_STATUS_SUCCESS             Success.
 * @return NA_IAM_STATUS_IF_NOT_CONFIGURED   Interface does not have a configured ip address.
 * @return NA_IAM_STATUS_ERROR               Invalid argument(s).     
*ad*/
NaIamStatus_t customizeIamGetIfAddrInfo(const char * ifname, NaIamIfAddrInfo_t * addrInfo)
{
    void *interfaceHandle = NULL;
    NaIamStatus_t status = NA_IAM_STATUS_SUCCESS;
#ifdef IPV6_ENABLED
    unsigned i;
#endif
    unsigned ip_configured = 0;
    unsigned home_index;

    if ((ifname == NULL) || (addrInfo == NULL))
    {
        status = NA_IAM_STATUS_ERROR;
        goto exit;
    }

    if (naIfConfigGetInterfaceHandle(ifname, &interfaceHandle, &home_index) != NASTATUS_SUCCESS)
    {
        printf("customizeIamGetIfAddrInfo: naIfConfigGetInterfaceHandle failed!\n");
        status = NA_IAM_STATUS_ERROR;
        goto exit;
    }
    
    memset(addrInfo, 0, sizeof *addrInfo);

    /* No useful return values. */
    tfGetIfMtu(interfaceHandle, &(addrInfo->mtu));       


    if (naIamGetCurrentMethod(ifname, &(addrInfo->method))
                                          != NA_IAM_STATUS_SUCCESS)
    {
        addrInfo->method = NA_IAM_METHOD_NONE;
    }
    else
    {
        ip_configured = 1;                          
        /* First get the IPv4 address if one exists. No useful return values. */
        tfNgGetIpAddress(interfaceHandle, &(addrInfo->v4Info.ipAddress), AF_INET, home_index);
        tfGetNetMask(interfaceHandle, &(addrInfo->v4Info.subnetMask),
                     (unsigned char)home_index);

        if (tfGetIfGateway(interfaceHandle,
                           &(addrInfo->v4Info.defaultGateway.addr.ipv4.sin_addr.s_addr),
                           0) == TM_ENOERROR)
        {
            addrInfo->v4Info.defaultGateway.ss_family = AF_INET;
        }
    }
    
    /* These could be v4 or 6 adrresses. */

//    tfNgDnsGetServer(&(addrInfo->primaryDnsServer), TM_DNS_PRI_SERVER);
//    tfNgDnsGetServer(&(addrInfo->secondaryDnsServer), TM_DNS_SEC_SERVER);           

    naIamGetDns(ifname, &(addrInfo->primaryDnsServer), TM_DNS_PRI_SERVER);
    naIamGetDns(ifname, &(addrInfo->secondaryDnsServer), TM_DNS_SEC_SERVER);

#ifdef IPV6_ENABLED

    /* Get IPv6 information if it exists. There is a possibility of not even having 
     * a link local address due to duplicate address detection. No need to check return values. */

    tfNgGetIpAddress(interfaceHandle, &(addrInfo->v6Info.ipAddress), AF_INET6, home_index);

    tfNgGetPrefixLen(interfaceHandle, &(addrInfo->v6Info.prefixLen), AF_INET6, home_index);
    tf6GetDefaultGatewayTunnel(&(addrInfo->v6Info.gatewayTunnel));

	if (addrInfo->method == NA_IAM_METHOD_NONE)
	{ 
		addrInfo->method = naIamGetCurrentMethodV6(ifname);
	}
 
    /* There is a total of (3 * NA_MAX_HOMES_PER_IF), or 12, v6 addresses per interface
     * with the first NA_MAX_HOMES_PER_IF being manually configured
     * the next NA_MAX_HOMES_PER_IF auto-configured, and the next NA_MAX_HOMES_PER_IF 
	 * could be configured by DHCPv6. We copy the latter NA_MAX_HOMES_PER_IF
     * here. If they don't exist, we just return zero'd out members.*/
    for (i = 0; i < NA_MAX_HOMES_PER_IF; i++)
    {   
        tfNgGetIpAddress(interfaceHandle, &(addrInfo->v6Info.autoCfgInfo.addr[i].ipAddress),
                         AF_INET6, i + NA_MAX_HOMES_PER_IF);

        tfNgGetPrefixLen(interfaceHandle, &(addrInfo->v6Info.autoCfgInfo.addr[i].prefixLen),
                         AF_INET6, i + NA_MAX_HOMES_PER_IF);
    }

    /* We must always have a link local address in order for a v6 interface to be considered 
     * configured. A non-zero prefix lenght is enough to verify this since it will always 
     * be 64 if configured. */ 
    if (addrInfo->v6Info.autoCfgInfo.addr[0].prefixLen != 0)
    {
        ip_configured = 1;
    }

#endif

    /* Must have at least one configured address to succeed. */
    if (ip_configured == 0)
    {
        status = NA_IAM_STATUS_IF_NOT_CONFIGURED;
    }

exit: 
    return status;   
}

static NaIamStatus_t customizeIamGetIfHomeAddress(void *interfaceHandle, int addrFamily, unsigned home, NaIamIfAddress_t * addr)
{
	NaIamStatus_t status = NA_IAM_STATUS_SUCCESS;

	addr->homeIndex = home;

	if (tfNgGetIpAddress(interfaceHandle, &addr->ipAddress, addrFamily, home) != TM_ENOERROR)
	{
		addr->method = NA_IAM_METHOD_NONE;
		status = NA_IAM_STATUS_IF_NOT_CONFIGURED;
        goto exit;
	}
	if (addrFamily == AF_INET)
	{
        tfGetNetMask(interfaceHandle, &addr->subnet.ipv4SubnetMask, (unsigned char)home);
		addr->method = naIamGetCurrentHomeMethod(interfaceHandle, home);

		if (addr->method == NA_IAM_METHOD_NONE || addr->IPV4_ADDR(ipAddress) == 0)
		{
			addr->method = NA_IAM_METHOD_NONE;
			status = NA_IAM_STATUS_IF_NOT_CONFIGURED;
		}
	}
	else
	{
		unsigned char prefixLen;
		tfNgGetPrefixLen(interfaceHandle, &prefixLen, AF_INET6, home);
		addr->subnet.ipv6PrefixLen = prefixLen;
		addr->method = naIamGetCurrentHomeMethodV6(interfaceHandle, home);
	}

exit: 
    return status; 
}

/*ad*
 * This function retrieves IPv4 or IPv6 address configured on the
 * specified network interface home.
 * 
 * NET+OS by default configures static or DHCP IPv4 address on "eth0" or "wln0" (home 0),
 * Auto IP address on "eth0:0" or "wln0:0" (home 1), Static IPv6 address on 
 * "eth0:1" or "wln0:1" (home 2), DHCPv6 address on "eth0:7" or "wln0:7" (home 8),
 * and auto-configured IPv6 addresses on "eth0:3" to "eth0:6" or "wln0:3" to "wln0:6"
 * (homes 4 to 7).
 *
 * @external
 * @include "iam.hh"
 * @category Networking:IAM:iam_params 
 * @since 7.0
 *
 * @see NA_IAM_STATICV6_INDEX
 * @see NA_IAM_DHCPV6_INDEX
 * @see customizeIamGetIfconfig
 *
 * @param ifname        nul-terminated interface name string in the "ethx:x" format.
 * @param addrFamily	AF_INET or AF_INET6.
 * @param addr		    Pointer to a caller allocated buffer for IP address information
 *                      for this network interface home.
 *
 * @return NA_IAM_STATUS_SUCCESS             Success.
 * @return NA_IAM_STATUS_IF_NOT_CONFIGURED   Interface home does not have a configured IP address.
 * @return NA_IAM_STATUS_ERROR               Invalid argument(s).     
*ad*/
NaIamStatus_t customizeIamGetIfAddress(const char * ifname, int addrFamily, NaIamIfAddress_t * addr)
{
    void *interfaceHandle = NULL;
    NaIamStatus_t status = NA_IAM_STATUS_SUCCESS;
    unsigned home_index;

    if (ifname == NULL || addr == NULL || (addrFamily != AF_INET && addrFamily != AF_INET6))
    {
        status = NA_IAM_STATUS_ERROR;
        goto exit;
    }

	memset(addr, 0, sizeof *addr);

    if (naIfConfigGetInterfaceHandle(ifname, &interfaceHandle, &home_index) != NASTATUS_SUCCESS)
    {
        printf("customizeIamGetIfAddrInfo: naIfConfigGetInterfaceHandle failed!\n");
        status = NA_IAM_STATUS_ERROR;
        goto exit;
    }
	status = customizeIamGetIfHomeAddress(interfaceHandle, addrFamily, home_index, addr);

exit: 
    return status;   
}

/*ad*
 * This function retrieves IP configururation for on the specified network interface.
 * 
 *
 * @external
 * @include "iam.hh"
 * @category Networking:IAM:iam_params 
 * @since 7.0
 *
 * @see customizeIamGetIfAddress
 *
 * @param ifname        nul-terminated interface name string.
 * @param config	    Pointer to a caller allocated buffer for IP configuration information
 *                      for this network interface.
 *
 * @return NA_IAM_STATUS_SUCCESS            Success.
 * @return NA_IAM_STATUS_ERROR              Invalid argument(s).  
 * @return NA_IAM_STATUS_IF_NOT_CONFIGURED  Network interface not configured. 
*ad*/
NaIamStatus_t customizeIamGetIfconfig(const char * ifname, NaIamIfIpCfg_t * config)
{
    void *interfaceHandle = NULL;
    NaIamStatus_t status = NA_IAM_STATUS_SUCCESS;
	unsigned long default_gateway;
    unsigned idx;

    if (ifname == NULL || config == NULL)
    {
        status = NA_IAM_STATUS_ERROR;
        goto exit;
    }

	memset(config, 0, sizeof *config);

	if (naIfConfigGetInterfaceHandle(ifname, &interfaceHandle, &idx) != NASTATUS_SUCCESS)
    {
        printf("customizeIamGetIfAddrInfo: naIfConfigGetInterfaceHandle failed!\n");
        status = NA_IAM_STATUS_IF_NOT_CONFIGURED;
        goto exit;
    }
	tfGetIfMtu(interfaceHandle, &config->mtu);       

	for (idx = 0; idx < NA_MAX_HOMES_PER_IF; idx++)
    {  
		customizeIamGetIfHomeAddress(interfaceHandle, AF_INET, idx, &config->addrV4[idx]);
	}
	for (idx = 0; idx < NA_MAX_HOMES_PER_IF; idx++)
    {  
		customizeIamGetIfHomeAddress(interfaceHandle, AF_INET6, NA_MAX_HOMES_PER_IF + idx, &config->autoCfgV6[idx]);
	}
	
	customizeIamGetIfHomeAddress(interfaceHandle, AF_INET6, NA_IAM_STATICV6_INDEX, &config->staticV6);
	customizeIamGetIfHomeAddress(interfaceHandle, AF_INET6, NA_IAM_DHCPV6_INDEX, &config->dhcpV6);

	/* These could be v4 or 6 adrresses. */
	//TODO: - diff indexes for eth & wln */

//    tfNgDnsGetServer(&config->primaryDnsServer, TM_DNS_PRI_SERVER);
//    tfNgDnsGetServer(&config->secondaryDnsServer, TM_DNS_SEC_SERVER);  

    naIamGetDns(ifname, &(config->primaryDnsServer), TM_DNS_PRI_SERVER);
    naIamGetDns(ifname, &(config->secondaryDnsServer), TM_DNS_SEC_SERVER);

	if ((tfGetIfGateway(interfaceHandle, &default_gateway, 0)) != 0)
	{
		default_gateway = 0;
	}
    config->defaultV4Gateway.ss_family = AF_INET;
	config->defaultV4Gateway.ss_len = sizeof(struct sockaddr_in);
	config->defaultV4Gateway.addr.ipv4.sin_addr.s_addr = default_gateway;

	 tf6GetDefaultGatewayTunnel(&config->gatewayTunnel);
exit: 
    return status;   
}

/*ad*
 * This function is called by IAM in order to retrieve IAM settings such as the static IP address
 * or DHCP options in order to carry out an operation. This 
 * function may be modified to retrieve settings from a location other than NVRAM. Currently,
 * settings retrieved for IAM are read from NVRAM. 
 *
 * The default acquisition methods call this function when they receive the 
 * NA_IAM_COMMAND_INITIALIZE and NA_IAM_COMMAND_ACQUIRE commands and when the
 * TCP/IP stack notifies them of asynchronous events such as ARP probing expiration,
 * initial address conflicts, or address lease expiration as can occur in DHCP.     
 *
 * @external
 * @category Networking:IAM:callbacks
 * @since 7.0
 *
 * @param ifname      nul-terminated interface name in the "ethx:x" format.
 * @param method      Acquisition method for which IAM settings are requested.
 * @param cfg         Pointer to the settings structure filled in by this function.   
 *
 * @return NA_IAM_STATUS_SUCCESS    Success
 * @return NA_IAM_STATUS_ERROR      Unable to retrieve IAM settings.
*ad*/
NaIamStatus_t customizeIamGetSettingsCallback(const char * ifname, int method, NaIamCfg_t * cfg)
{ 
    NaIamStatus_t status = NA_IAM_STATUS_SUCCESS;

    NA_ASSERT((ifname != NULL) && (cfg != NULL)); 
   
    if ((ifname == NULL) || (cfg == NULL))
    {
        status = NA_IAM_STATUS_ERROR;
        goto done;
    }     
    
    /* Retrieve IAM NVRAM settings for IAM.*/
    switch(method)
    {
        case NA_IAM_METHOD_STATIC:
        {
            NaIamStaticParams_t staticParams;

            if (customizeIamGetStaticConfig(ifname, &staticParams) != BP_SUCCESS)
            {
                status = NA_IAM_STATUS_ERROR; 
                goto done;
            }

            cfg->cfg.staticCfg.isEnabled    = staticParams.isEnabled;
            cfg->cfg.staticCfg.ipAddress    = staticParams.ipAddress;
            cfg->cfg.staticCfg.subnetMask   = staticParams.subnetMask;
            cfg->cfg.staticCfg.gateway      = staticParams.gateway;
            cfg->cfg.staticCfg.primaryDns   = staticParams.primaryDns;
            cfg->cfg.staticCfg.secondaryDns = staticParams.secondaryDns;
            cfg->cfg.staticCfg.initialAcd   = BSP_ENABLE_ADDR_CONFLICT_DETECTION;
            cfg->cfg.staticCfg.ongoingAcd   = BSP_ENABLE_ONGOING_ADDR_CONFLICT_DETECTION;          
        }
        break;

        case NA_IAM_METHOD_DHCP:
        {
            NaIamDhcpParams_t dhcpParams;

            if (customizeIamGetDhcpConfig(ifname, &dhcpParams) != BP_SUCCESS)
            {
                status = NA_IAM_STATUS_ERROR; 
                goto done;
            }

            cfg->cfg.dhcpCfg.isEnabled  = dhcpParams.isEnabled;
            memcpy(cfg->cfg.dhcpCfg.optionsBuffer,
                   dhcpParams.optionsBuffer,
                   dhcpParams.optionsLength);
            cfg->cfg.dhcpCfg.optionsLength = dhcpParams.optionsLength;
            cfg->cfg.dhcpCfg.ongoingAcd = 
                           BSP_ENABLE_ONGOING_ADDR_CONFLICT_DETECTION;

                    
        }
        break;

        case NA_IAM_METHOD_AUTOIP:
        {
            NaIamAutoipParams_t autoipParams;

            if (customizeIamGetAutoipConfig(ifname, &autoipParams) != BP_SUCCESS)
            {
                status = NA_IAM_STATUS_ERROR; 
                goto done;
            }
            
            cfg->cfg.autoipCfg.isEnabled = autoipParams.isEnabled; 
             
        }
        break;

#ifdef NA_ARP_PING_ENABLED
        case NA_IAM_METHOD_USER:
        {
            NaIamUserParams_t userParams;

            if (customizeIamGetUserConfig(ifname, &userParams) != BP_SUCCESS)
            {
                status = NA_IAM_STATUS_ERROR; 
                goto done;
            }
            
            cfg->cfg.userCfg.isEnabled    = userParams.isEnabled;
            cfg->cfg.userCfg.ipAddress    = userParams.ipAddress;
            cfg->cfg.userCfg.subnetMask   = userParams.subnetMask;
            cfg->cfg.userCfg.gateway      = userParams.gateway;
            cfg->cfg.userCfg.primaryDns   = userParams.primaryDns;
            cfg->cfg.userCfg.secondaryDns = userParams.secondaryDns;
            cfg->cfg.userCfg.initialAcd   = BSP_ENABLE_ADDR_CONFLICT_DETECTION;
            cfg->cfg.userCfg.ongoingAcd   = BSP_ENABLE_ONGOING_ADDR_CONFLICT_DETECTION; 
             
        }
        break; 
#endif

        default:
            NA_ASSERT(0);
            status =  NA_IAM_STATUS_ERROR;
            goto done;        
    }

    /* These are common to all acquisition methods. */
    cfg->probeInterval         = BSPDfltEthProbeIntervalMS;
    cfg->numProbes             = BSPDfltEthNumAddrProbes;
    cfg->numAnnouncements      = BSPDfltEthNumAddrAnnounce;  
    cfg->announcementsInterval = BspDfltEthAnnounceIntervalMS; 

done:
    return status;
}

/*ad*
 * This function is called by IAM in order to retrieve IPv4 ACD settings, defined 
 * in bsp_net.h.
 *
 * At present is used only by Auto IP, when it receives the 
 * the NA_IAM_COMMAND_ACQUIRE command. 
 *
 * @external
 * @category Networking:IAM:callbacks
 * @since 7.5
 *
 * @param ifname      nul-terminated interface name in the "ethx:x" format.
 * @param method      Acquisition method for which IAM settings are requested.
 * @param cfg         Pointer to the IPv4 ACD settings structure filled in by this function.   
 *
 * @return NA_IAM_STATUS_SUCCESS    Success
 * @return NA_IAM_STATUS_ERROR      Unable to retrieve IAM settings.
*ad*/
NaIamStatus_t customizeIamGetV4AcdSettingsCallback(const char * ifname, int method, NaIamV4AcdConfig_t * cfg)
{
	(void) ifname;
	(void) method;

	NA_ASSERT(cfg != NULL); 
   
    if (cfg == NULL)
    {
        return NA_IAM_STATUS_ERROR;
    }     

    cfg->probeIntervalMin	= BSPDfltEthProbeIntervalMS;
	cfg->probeIntervalMax   = NaBspDfltEthProbeIntervalMaxMS;
    cfg->numProbes          = BSPDfltEthNumAddrProbes;
	cfg->probeWait			= NaBspDfltEthProbeWaitMS;
    cfg->numAnnouncements   = BSPDfltEthNumAddrAnnounce;  
    cfg->announceInterval	= BspDfltEthAnnounceIntervalMS; 
	cfg->announceWait		= NaBspDfltEthAnnounceWaitMS;
	cfg->rateLimitInterval  = NaBspDfltEthRateLimitInterval;
	cfg->defendInterval		= NaBspDfltEthDefendInterval;
	cfg->maxConflicts		= NaBspDfltEthMaxConflicts;

	return NA_IAM_STATUS_SUCCESS;
}

/*
 * Private function used by the IAM library to install IAM's Ethernet
 * link monitoring callback.  We cannot implement this code inside of
 * the IAM library because it depends on the BSP_WANT_ETHERNET #define,
 * which is only valid when the BSP is compiled.
 * (Ed. Note - the NABspWantEthernet variable in bsp_api.c can be used.)
 */
void iamInstallEthernetLinkCallback(NaLinkStatusCallbackFn_t callbackFn)
{
    (void) callbackFn;
#if BSP_WANT_ETHERNET
    naEthRegisterLinkStatusCallback(callbackFn);
#endif
}

#ifdef NA_ARP_PING_ENABLED
/*
 * This function is called by tfIcmpPingArpCallback() in tricmp.c in order to set the user IP
 * Address, SubnetMask and Default Gateway after receiving a valid Ping packet.     
 *
 * @external
 * @category Networking:IAM:callbacks
 * @since 7.4
 *
 * @param ifname             nul-terminated interface name in the "ethx:x" format.
 * @param destIPAddress      IP AddressAcquisition method for which IAM settings are requested.
 * @param SubnetMask         Subnet Mask
 * @param defgw              Default Gateway
 */
void customizeIamArpPingCallback(char const * ifname, unsigned long destIPAddress,
                                 unsigned long SubnetMask, unsigned long defgw)
{
    // NaIamParams_t       *interfaceConfig = NULL;
    static int          iam_arp_ping_callback = FALSE;
    int                 method;
    NaIamUserParams_t   user_config;

    while (iam_arp_ping_callback)
    {
        tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(2500));
    }

    iam_arp_ping_callback = TRUE;

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

    customizeIamGetUserConfig(ifname, &user_config);

    if (user_config.IPV4_ADDR(ipAddress) == destIPAddress &&
        user_config.subnetMask == SubnetMask &&
        user_config.IPV4_ADDR(gateway) == defgw &&
        user_config.isEnabled == TRUE)
    {
        goto done; /* New IP address in progress */
    }
      
    user_config.IPV4_ADDR(ipAddress) = destIPAddress;
    user_config.subnetMask = SubnetMask;
    user_config.IPV4_ADDR(gateway) = defgw;
    user_config.isEnabled = TRUE;

    customizeIamSetUserConfig(ifname, &user_config);

    naIamGetCurrentMethod(ifname, &method);

    switch (method)
    {
        case NA_IAM_METHOD_STATIC:
            naIamDisable(ifname, (int)method);
            naIamEnable(ifname, (int)NA_IAM_METHOD_USER);
            break;
        case NA_IAM_METHOD_DHCP:
            naIamDisable(ifname, (int)method);
            naIamEnable(ifname, (int)NA_IAM_METHOD_USER);
            break;
        case NA_IAM_METHOD_USER:
            naIamDisable(ifname, (int)NA_IAM_METHOD_USER);
            naIamEnable(ifname, (int)NA_IAM_METHOD_USER);
            break;
        case NA_IAM_METHOD_NONE:
            naIamDisable(ifname, (int)method);
            naIamEnable(ifname, (int)NA_IAM_METHOD_USER);
            break;
    }

done: 

    iam_arp_ping_callback = FALSE;
}
#endif
