/**
 * ============================================================================
 * = COPYRIGHT
 *              INTEL CORPORATION PROPRIETARY INFORMATION
 *   This software is supplied under the terms of a license agreement or
 *   nondisclosure agreement with Intel Corporation and may not be copied 
 *   or disclosed except in accordance with the terms in that agreement.
 *      Copyright (C) 2000-2001 Intel Corporation. All rights reserved.
 *
 * = PRODUCT
 *      Intel(r) IXA SDK 3.0 for the IXP2X00 Network Processor, Release 6
 *
 * = FILENAME
 *      ix_cc_eth_tx_pkt_hdlr.c
 *
 * = DESCRIPTION
 *      This file defines various packet handler routines.
 *
 * = CHANGE HISTORY
 *      11/21/2002 - Created.
 *
 * ============================================================================
 * $Id: ix_cc_eth_tx_pkt_hdlr.c,v 1.32 2003/10/20 16:47:58 ktseng Exp $
 */

/**
 * System defined include files required.
 */
#include "ix_cc_error.h"

#include "ix_types.h"
#include "ix_error.h"
#include "ix_cc.h"
#include "bindings.h"
#include "ix_cc_microengines_bindings.h"
#include "ix_netmacros.h"
#include "ix_cc_properties.h"

#include "ix_rm.h"
#include "ix_ossl.h"

#ifndef IX_EXCLUDE_CCI
#include "ix_cci.h"
#endif

#include "cc/ix_cc_msup.h"



/**
 * User defined include files required.
 */
#include "cc/ix_cc_eth_tx.h"
#include "cc/internal/ix_cc_eth_tx_int.h"
#include "cc/ix_cc_arp.h"


#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif

#endif

ix_error _ix_cc_eth_tx_add_nbr_sol_l2_hdr( ix_buffer_handle, ix_hw_buffer_meta *, ix_uint8 *, void *);

/**
 * NAME: ix_cc_eth_tx_pkt_handler
 * 
 * DESCRIPTION: This function is the common packet handler routine for
 *              Ethernet Tx CC.
 * 
 * @Param: arg_hPacket - IN: exception packet.
 * @Param: arg_ExceptionCode - IN: exception code.
 * @Param: arg_pContext - IN: pointer to the component context structure.
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */  
ix_error ix_cc_eth_tx_pkt_handler(
    ix_buffer_handle arg_hPacket,  
    ix_uint32 arg_ExceptionCode,
    void* arg_pContext)
{
  
    ix_buffer_handle hRtnPkt = (ix_buffer_handle)IX_NULL_BUFFER_HANDLE;  
    ix_error err = IX_SUCCESS;
    BOOL checkRtnPkt = FALSE;
    ix_uint32 rtnPktType;
    ix_uint32 outputCommId;
    ix_hw_buffer_meta *meta;
    ix_uint8 *pData;
    ix_uint8 *pIpAddr;



#ifdef IX_DEBUG

    /* Check whether the arg_hPacket is invalid */
    if ((ix_buffer_handle)IX_NULL_BUFFER_HANDLE == arg_hPacket)
    {
        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL packet handle")));
    }

    /* Check whether arg_pContext is invalid */
    if (NULL == arg_pContext)
    {
        /* Free the ix_buffer */
        ix_rm_buffer_free_chain(arg_hPacket);

        return(IX_ERROR_WARNING(IX_CC_ERROR_NULL,
                                ("NULL context argument")));
    }

#endif /* IX_DEBUG */    

    /* Process the exception packet */
    switch(arg_ExceptionCode)
    {
        case IX_ARP_PACKET:

            /**
             * Hand the ARP packet to ARP library for processing. 
             * ARP frees the input buffer when it returns.  In case of 
             * error, no packet will be returned from ARP library.
             */
            IX_ERROR_CR(ix_cc_arp_process_arp_pkts(arg_hPacket, 
                                                   &hRtnPkt,
                                                   &rtnPktType));

            /** 
             * For the case of success, Eth Tx CC needs to check 
             * the returned packet which could be either an ARP 
             * reply or a previously held IP packet.
             */
            checkRtnPkt = TRUE;

            /* Check return packet type */
            if (rtnPktType == IX_CC_ARP_REPLY)
            {
                outputCommId = IX_CC_ETH_TX_ARP_PKT_OUTPUT;
            }
            else if (rtnPktType == IX_CC_IP)
            {
                outputCommId = IX_CC_ETH_TX_COMMON_PKT_OUTPUT;
            }
            else
            {
                /* Drop the unknown returned packet */
                if (hRtnPkt != (ix_buffer_handle)IX_NULL_BUFFER_HANDLE)
                {
                    ix_rm_buffer_free_chain(hRtnPkt);
#ifdef IX_DEBUG
                    ix_ossl_message_log("Unexpected packet type returned from ARP.\n");
#endif
                }

         	return (err);
            }

            break;

        case IX_NOL2_ADDR_IP_PACKET:

            /* Retrieve the meta data. */
            IX_ERROR_C(ix_rm_buffer_get_meta(arg_hPacket, (void **)&meta), err);
        
            if (err != IX_SUCCESS) 
            {
                /* Free the buffer */
                ix_rm_buffer_free_chain(arg_hPacket);

                /* Return the original error from RM get meta data */
                return(err);
            }

            /* Get the address to the packet data */	     
            IX_ERROR_C(ix_rm_buffer_get_data(arg_hPacket, 
                                             (void **)&pData),
                       err);

            if (err != IX_SUCCESS) 
            {
                /* Free the buffer */
                ix_rm_buffer_free_chain(arg_hPacket);

                /* Return the original error from RM get data */
                return(err);
            }

            /* Adjust the pointer to the start of the packet using the offset in the meta data */
            pData = pData + IX_NTOH16(meta->m_Offset);

            /**
             * There are two special cases need to be checked first before the 
             * packet can be sent to IPv4 ARP library for processing.
             * These are checking for IPv6 packets and packets for locally 
             * connected hosts.
             */

            /* Check if the packet is an IPv6 packet. */
            if (((*pData & IX_CC_ETH_TX_IP_VER_MASK) >> IX_CC_ETH_TX_IP_VER_SFT) == IX_CC_ETH_TX_IP_VER_IPV6)
            {
                /**
                 * Check if the packet is an Neighbor solicitaion packet. If it is, then 
                 * compose a MAC header and send it out for transmission.
                 */
                pIpAddr = pData + IX_CC_ETH_TX_DEST_IP_OFFSET_IPV6;

                if ( (((ix_uint16)(*pIpAddr) << 8) | 
                      (ix_uint16)(*(pIpAddr + 1)))
                     == IX_CC_ETH_TX_IPV6_NBR_SOL_MCAST_ADDR)
		{
                    IX_ERROR_C(_ix_cc_eth_tx_add_nbr_sol_l2_hdr(arg_hPacket, meta, pData, arg_pContext),
                               err);
                    if (err != IX_SUCCESS) 
                    {
                        /* Free the buffer */
                        ix_rm_buffer_free_chain(arg_hPacket);

                        /* Return the error */
                        return(err);
                    }

                    checkRtnPkt = TRUE;
                    hRtnPkt = arg_hPacket;
                    outputCommId = IX_CC_ETH_TX_COMMON_PKT_OUTPUT;
                    break;
                }
		    
                /* Otherwise it's an IPv6 packet and send it to IPv6 CC for L2 address resolution */
                IX_ERROR_CT(ix_rm_message_send(IX_CC_ETH_TX_IPV6_PKT_OUTPUT, 
                                               arg_hPacket, 
                                               IX_CC_IPV6_MSG_PERFORM_ADDR_RESOLUTION),
                            err,
                            IX_CC_ERROR_SEND_FAIL,
                            IX_ERROR_LEVEL_WARNING);

                if (err != IX_SUCCESS)
                {
                    /* Free the packet buffer */
                    ix_rm_buffer_free_chain(arg_hPacket);
                }

                return(err);
            }

            /**
             * Retrieve the L2 index from the meta data.
             * If the L2 index is 0, it indicates the packet is destined
             * to a directed connected host and a message needs to be sent to FP 
             * module for the creation of a new L2 index. For other cases, 
             * the packet is sent to ARP library for the resolution of 
             * L2 address.
             */
            if (IX_NTOH16(meta->m_NextHopID) == 0)
            {
               ix_cc_fpm_direct_host_exception_data fpmData;

                /* Get the packet destination IP address */
                fpmData.dest_ip = ((ix_uint32)(*(pData + IX_CC_ETH_TX_DEST_IP_OFFSET + 0)) << 24) | 
                                  ((ix_uint32)(*(pData + IX_CC_ETH_TX_DEST_IP_OFFSET + 1)) << 16) | 
                                  ((ix_uint32)(*(pData + IX_CC_ETH_TX_DEST_IP_OFFSET + 2)) << 8) | 
                                  (ix_uint32)(*(pData + IX_CC_ETH_TX_DEST_IP_OFFSET + 3));

                /* Get the output port ID */
                fpmData.port_id = IX_NTOH16(meta->m_OutputPort);

                /* Free the buffer */
                ix_rm_buffer_free_chain(arg_hPacket);

                /* Send a message to FP module and return */
                return(ix_cc_msup_send_msg(IX_CC_ETH_TX_FP_MODULE_MSG_OUTPUT,
                                           IX_NEW_DIRECT_HOST_MSG_ID,
                                           (void *)&fpmData,
                                           sizeof(ix_cc_fpm_direct_host_exception_data)));
            }

            /**
             * Hand the exception IP packet to ARP library for processing. 
             */
            checkRtnPkt = TRUE;
            IX_ERROR_C(ix_cc_arp_resolve_l2_addr(arg_hPacket,&hRtnPkt),
                       err);

            if (err == IX_SUCCESS) 
            {
                /**
                 * This is the case when look-up is successful and the
                 * L2 table got synchronized with ARP Cache. The input IP
                 * packet is returned to Eth Tx CC through hRtnPkt for 
                 * transmission.
                 */
                outputCommId = IX_CC_ETH_TX_COMMON_PKT_OUTPUT;
            }
            else if (IX_ERROR_GET_CODE(err) == IX_CC_ERROR_ENTRY_NOT_FOUND)
            {
                /**
                 * This is the case when the look-up failed.  The
                 * input IP packet is held by ARP and an ARP request
                 * packet is returned to Eth Tx CC for transmission.
                 */
                outputCommId = IX_CC_ETH_TX_ARP_PKT_OUTPUT;
            }
            else
            {
                /**
                 * For all other cases, the ARP resolve operation failed.
                 * Input IP packet is dropped by ARP and no other packet 
                 * is returned by ARP.
                 */
                return(err);
            }

            break;

        default:

            /* Free the input buffer */
            ix_rm_buffer_free_chain(arg_hPacket);

            return(IX_ERROR_WARNING(IX_CC_ERROR_UNDEFINED_EXCEP, 
                                    ("Undefined exception code")));
            break;
    }

    /**
     * Check if there is any new packet returned from ARP library. 
     * Send the packet to the appropriate output based on the packet
     * type.
     */
    if ((checkRtnPkt == TRUE) && (hRtnPkt != (ix_buffer_handle)IX_NULL_BUFFER_HANDLE))
    {
        IX_ERROR_CR(_ix_cc_eth_tx_send_packet(hRtnPkt, 
                                              outputCommId));
    }

    return(IX_SUCCESS);

} /* ix_cc_eth_tx_pkt_handler */




/**
 * NAME: _ix_cc_eth_tx_send_packet
 * 
 * DESCRIPTION: This function sends the input packet to one of the 
 *              component's outputs based on the output comm id.  If there 
 *              is any error from RM packet send funtion, the function 
 *              will free the packet buffer and return IX_CC_ERROR_SEND_FAIL 
 *              error code to the caller. (In case of failure of freeing
 *              the buffer, an OSSL message will be logged.)
 * 
 * @Param: arg_hPacket - IN: the packet to be sent.
 * @Param: arg_OutputCommId - IN: output communication ID 
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.
 */  
ix_error _ix_cc_eth_tx_send_packet(
    ix_buffer_handle arg_hPacket,
    ix_uint32 arg_OutputCommId)
{
    ix_error err;

    if (arg_OutputCommId == IX_CC_ETH_TX_ARP_PKT_OUTPUT)
    {
        /* Send the packet */
        IX_ERROR_CT(ix_rm_message_send(arg_OutputCommId, 
                                      arg_hPacket, 
                                      0),
                    err,
                    IX_CC_ERROR_SEND_FAIL,
                    IX_ERROR_LEVEL_WARNING);
    }
    else 
    {
        /* Send the packet */
        IX_ERROR_CT(ix_rm_packet_send(arg_OutputCommId, 
                                      arg_hPacket, 
                                      0),
                    err,
                    IX_CC_ERROR_SEND_FAIL,
                    IX_ERROR_LEVEL_WARNING);
    }

    if (err != IX_SUCCESS)
    {
        /* Free the packet buffer */
        ix_rm_buffer_free_chain(arg_hPacket);

        /* Return the send error. */
        return(err);
    }

    return(IX_SUCCESS);

} /* _ix_cc_eth_tx_send_packet */



/**
 * NAME: _ix_cc_eth_tx_arp_send_packet
 * 
 * DESCRIPTION: This function gets registered into the ARP library 
 *              during ARP library initialization.  It is used by ARP 
 *              library to send ARP request packets when ARP is deleting
 *              a dynamic entry due to entry aging.  In case of error, 
 *              the function frees the ix_buffer.
 * 
 * @Param: arg_hPacket - IN: the packet to be sent.
 * 
 * @Return: void.
 */  
void _ix_cc_eth_tx_arp_send_packet(
    ix_buffer_handle arg_hPacket)
{

    /* Call the _ix_cc_eth_tx_send_packet() and ignore the return code */
    _ix_cc_eth_tx_send_packet(arg_hPacket, IX_CC_ETH_TX_ARP_PKT_OUTPUT);

} /* _ix_cc_eth_tx_arp_send_packet */



/**
 * NAME: _ix_cc_eth_tx_add_nbr_sol_l2_hdr
 * 
 * DESCRIPTION: This function adds l2 header into the input IP packet.
 *              The destination MAC address will be composed as 
 *              a multicast Ethernet address of the format 
 *              33:33:XX:XX:XX:XX, where the last 32 bits are the last 
 *              32 bits of the IPv6 destination address. 
 * 
 * @Param: arg_hPacket - IN: exception packet.
 * @Param: arg_pMeta - IN: pointer to the packet meta data.
 * @Param: arg_pData - IN: pointer to the current packet data.
 * @Param: arg_pContext - IN: pointer to the component context structure.*
 * 
 * @Return: IX_SUCCESS if successful or a valid ix_error for failure.*
 */  
ix_error _ix_cc_eth_tx_add_nbr_sol_l2_hdr(
    ix_buffer_handle arg_hPacket,
    ix_hw_buffer_meta *arg_pMeta,
    ix_uint8 *arg_pData,
    void *arg_pContext)
{

    ix_ether_hdr* pEthHdr = NULL;
    ix_uint32 cell_count = 0;
    ix_uint32 ipAddr;
    ix_uint16 temp;
    ix_uint16 x;
    ix_uint8 *pData;
 
    /* Obtain the address for storing the MAC header */
    pData = arg_pData - IX_CC_ETH_TX_SIZE_MAC_HDR;
    pEthHdr = (ix_ether_hdr *)(pData);

    /* Compose the multicast Ethernet address destination address */
    *(pData + 0) = 0x33;
    *(pData + 1) = 0x33;
    *(pData + 2) = *(arg_pData + IX_CC_ETH_TX_DEST_IP_OFFSET_LAST_32BIT_IPV6);
    *(pData + 3) = *(arg_pData + IX_CC_ETH_TX_DEST_IP_OFFSET_LAST_32BIT_IPV6 + 1);
    *(pData + 4) = *(arg_pData + IX_CC_ETH_TX_DEST_IP_OFFSET_LAST_32BIT_IPV6 + 2);
    *(pData + 5) = *(arg_pData + IX_CC_ETH_TX_DEST_IP_OFFSET_LAST_32BIT_IPV6 + 3);

    /* Obtains local MAC address based on the port ID in meta data */
    IX_ERROR_CR(_ix_cc_eth_tx_get_port_data(arg_pContext,
                                            arg_pMeta->m_OutputPort,
                                            (ix_ether_addr *)(pData + 6),
                                            &ipAddr));

    /* Copy protocol field */
    pEthHdr->protFld = IX_HTON16(IX_CC_ETH_TX_MAC_PROT_IPV6);

    /* Re-calculate the packet size */
    x = arg_pMeta->m_PacketSize + IX_CC_ETH_TX_SIZE_MAC_HDR;
    temp = IX_RM_MEM_UINT16_WRITE(&(arg_pMeta->m_PacketSize), IX_HTON16(x));

    /* Re-calculate the buffer size */
    x = arg_pMeta->m_BufferSize + IX_CC_ETH_TX_SIZE_MAC_HDR;
    temp = IX_RM_MEM_UINT16_WRITE(&(arg_pMeta->m_BufferSize), IX_HTON16(x));

    /* Reset cell count */
    cell_count = ix_cc_calculate_cell_count((ix_uint32)(x), 1);
    ix_rm_buffer_cell_count_set(&arg_hPacket, cell_count);

    /* Set next hop id in meta to -1 so ublocks don't lookup L2 table */ 
    temp = IX_RM_MEM_UINT16_WRITE(&(arg_pMeta->m_NextHopID), IX_HTON16(-1));
    
    /* Re-adjust data offset in the meta-data */
    x = arg_pMeta->m_Offset - IX_CC_ETH_TX_SIZE_MAC_HDR;
    temp = IX_RM_MEM_UINT16_WRITE(&(arg_pMeta->m_Offset), IX_HTON16(x));

    return(IX_SUCCESS);

} /* _ix_cc_eth_tx_add_nbr_sol_l2_hdr */

