/**
 * ============================================================================
 * = 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) 2002 Intel Corporation. All rights reserved.
 *
 * = PRODUCT
 *      Intel(r) IXA SDK 3.0 for the IXP2000 Network Processor
 *
 *
 *
 * = MODULE
 *      IPv4 Forwarder Core Component
 *
 * = FILENAME
 *      ipv4_fwdr.c
 *
 * = DESCRIPTION
 *   This file contains implementation of forwarding functions-
 *   for forwarding of packets coming from IPv4 microblock
 *   and other Core Components   
 *
 *
 *
 *
 * = CREATION TIME
 *      8/11/2002 9:30:24 PM
 *
 * = CHANGE HISTORY
 *
 * ============================================================================
 */

#define IX_ERROR_FILE_IDENT "$Id: ipv4_fwdr.c,v 1.40 2003/12/12 19:25:49 ttschlue Exp $"

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

/**
 * User defined include files required.
 */
#include "definitions.h"
#include "ix_cc_error.h"
#include "cc/ipv4/internal/ipv4_fwdr.h"
/**
 * Preprocessor symbols and macros used in this file.
 */

/**
 * Types definitions whose scope is limited to this file.
 */

/**
 * Variable declarations global to this file only.  Externs are followed by
 * static variables.
 */

/**
 * Extern function prototypes.
 */

/**
 * Static function prototypes.
 */

/**
 * Function definitions.
 */

/**
 * NAME: _ix_cc_ipv4_duplicate_first_packet_buffer
 *
 * DESCRIPTION:This function duplicates the first buffer in a packet
 * to be used later as the basis for creating an ICMP redirect message.
 * The original buffer can then be retransmitted back to the sender.
 * The packet data is not copied because only the meta is needed.             
 * 
 * @Param:  - IN - arg_hBuffer - a buffer handle
 *  containing the packet that needs to be 
 *  forwarded  
 *		 
 * @Param:  - INOUT  - None
 * @Param:  - OUT - arg_hOutBuffer - the buffer handle 
 *  containing the duplicated packet  
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error 
 *          token for failure.
 */
ix_error _ix_cc_ipv4_duplicate_first_packet_buffer(
    ix_buffer_handle	arg_hBuffer,
    ix_buffer_handle	*arg_hOutBuffer)
{
    ix_uint32 result = 0;
    ix_hw_buffer_meta	*pDestHwMeta, *pSourceHwMeta;
    ix_uint8	*pDestData, *pSourceData;
    ix_error err = IX_SUCCESS;

    IX_ERROR_CR(ix_rm_buffer_alloc(g_pIpv4Context->hBufferFreeList,arg_hOutBuffer));

    ix_rm_buffer_cell_count_get(arg_hBuffer, &result);
    ix_rm_buffer_cell_count_set(arg_hOutBuffer, result);

    IX_ERROR_CGT(ix_rm_buffer_get_meta(arg_hBuffer, (void**)&pSourceHwMeta), err,
                 IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING, label_1);
    IX_ERROR_CGT(ix_rm_buffer_get_meta(*arg_hOutBuffer, (void**)&pDestHwMeta), err,
                 IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING, label_1);
    ix_ossl_memcpy(pDestHwMeta, pSourceHwMeta, sizeof(ix_hw_buffer_meta));
    IX_ERROR_CGT(ix_cc_hw_get_packet_data(arg_hBuffer, (void**)&pSourceData), err,
                 IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING, label_1);
    IX_ERROR_CGT(ix_cc_hw_get_packet_data(*arg_hOutBuffer, (void**)&pDestData), err,
                 IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING, label_1);
    ix_ossl_memcpy(pDestData, pSourceData, IX_NTOH16(pSourceHwMeta->m_BufferSize));

    return IX_SUCCESS;
label_1:
    ix_rm_buffer_free_chain(*arg_hOutBuffer);
    *arg_hOutBuffer = IX_NULL_BUFFER_HANDLE;
    return err;
}


/**
 * NAME: _ix_cc_ipv4_forward
 *
 * DESCRIPTION:This function performs forwarding
 * of packets coming from IPv4 microblock 
 * other than Stack Driver Core
 * Component
 * 
 *             
 * 
 * @Param:  - IN - arg_hBuffer - a buffer handle
 *  containing the packet that needs to be 
 *  forwarded  
 *		 
 * @Param:  - INOUT  - None
 * @Param:  - OUT - None  
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error 
 *          token for failure.
 */

ix_error _ix_cc_ipv4_forward (ix_buffer_handle arg_hBuffer)
{
    
    void *pData; 
    ix_hw_buffer_meta *pHwMeta; 
    ix_uint16 len, ingressPort;
    ix_uint32 ingressPortStatus = 0, egressPortStatus = 0;        
    ix_cc_ipv4_header ipHdr;
    ix_uint32 dropped, nexthopIpAddress;
    ix_cc_rtmv4_next_hop_info nexthopInfo;
    ix_error err = IX_SUCCESS;
    ix_uint16 l2Index;
    ix_uint16 port;
    
    /* extract IP header */
    err = ix_cc_hw_get_packet_data(arg_hBuffer, &pData);
    /** Attempt to free the buffer if get data fails*/
    if (err)
    {
        
        ix_rm_buffer_free_chain(arg_hBuffer);
        return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);        
           
    }
   

    /** Use ix_rm_buffer_get_meta to get ifnum */
    err = ix_rm_buffer_get_meta( arg_hBuffer, (void **)&pHwMeta);
    if (err)
    {
        
        ix_rm_buffer_free_chain(arg_hBuffer);
        return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,IX_ERROR_LEVEL_WARNING);
        
    }
    /* Note m_InputPort is ix_uint16 */
    ingressPort = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pHwMeta->m_InputPort));

#ifdef DUMP_TO_SCREEN    
    ix_ossl_message_log( "IPv4 Forwarder got a packet from port %d\n",
      ingressPort );
#endif
    /** Storing len in host byte order **/
    len = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pHwMeta->m_PacketSize));

    /* Increment statistics for incoming datagrams */
    g_pIpv4Context->ipv4FwdStats.ipv4CorestatsIncomingDatagrams++;


#ifdef RFC1812_SHOULD
    /* if length of packet is less than minimum IP header
    length, then generate ICMP parameter problem message*/
    if ( len <  MIN_IP_LEN ) 
    {
        /* Increment statistics for number of packets with
        invalid header */
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsInvalidHeader++;
        err = _ix_cc_ipv4_icmp_param_problem( arg_hBuffer, 
        IX_ICMP_PARAM_TOTALLENGTH );
        if (err)
        {
            
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,IX_ERROR_LEVEL_WARNING);
        
        }
        return IX_SUCCESS;
    }
#endif /* RFC1812_SHOULD */

    (void)_ix_cc_ipv4_init_ip_header( (ix_uint8 *)(pData),&ipHdr);
    
    /* Validate IP header - RFC1812 checks */
    IX_ERROR_CR(_ix_cc_ipv4_IPheader_validate( &ipHdr, arg_hBuffer,
       &dropped ));
    
    if ( dropped )
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log( "Invalid IP header, dropping\n" );
#endif		
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsInvalidHeader++;
        
        ix_rm_buffer_free_chain(arg_hBuffer);
		return IX_SUCCESS;
    }
    /* Perform RFC1812 address validation */
    IX_ERROR_CR(_ix_cc_ipv4_validate_IP_addresses(&ipHdr, arg_hBuffer,
         &dropped ));
    
    if ( dropped )
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log( "Invalid IP address, dropping\n" );
#endif
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsInvalidAddress++;
     
		/* buffer has been freed in validate address fn **/
        return IX_SUCCESS;
    }
    
    /* If IP header length > 5, then IP options are present */
    if ( ipHdr.headerLength > MIN_IP_LEN / 4 )
    {
        err = _ix_cc_ipv4_parse_ip_options( &ipHdr, arg_hBuffer,
                        &dropped );
        if (dropped)
        {
             
#ifdef DUMP_TO_SCREEN
            ix_ossl_message_log( "Invalid IP Options, dropping\n" );
#endif
            /** Free the buffer **/
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_SUCCESS;
        }
    }

    g_pIpv4Context->ipv4FwdStats.ipv4CorestatsForwardingAttempted++;	
    
    /* Perform RTM lookup **/
    err = ix_cc_rtmv4_lookup(g_pIpv4Context->hRtm, IX_NTOH32(ipHdr.destAddress),
            &nexthopInfo);
    
    /* If route entry is invalid, send destination unreachable
    error message to source */
    if (nexthopInfo.l2Index == IX_CC_RTMV4_L2INDEX_NO_ROUTE)
    {
		 
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsNorouteFound++;
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log("Forwarding fn:Unreachable Host."
        " Lookup Failed for ip %x\n", ipHdr.destAddress);
#endif
		 
        err = _ix_cc_ipv4_icmp_dest_unreachable( arg_hBuffer,
                                     IX_ICMP_UNREACH_HOST, 0);
        if (err)
        {
            
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,
               IX_ERROR_LEVEL_WARNING);
             
        }
        return IX_SUCCESS;

    }
    /* If route entry indicates local delivery, send the packet
    to stack */
    else if(nexthopInfo.flags & IPV4_NH_FLAGS_LOCAL_BIT)
    {

        /*send to stack */
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsLocalDelivery++;
		
        /** The only meta data information that needs 
        ** to be filled up in buffer is input port information 
        ** before sending packet to stack but that will be filled up
        ** by Rx microblock **/
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log("Local delivery\n" );
#endif
     
        /* Send the packet to Stack driver CC through high priority
         queue */
        err = ix_rm_message_send(
           IX_CC_IPV4_PKT_HIGH_PRIORITY_STKDRV_OUTPUT,
            arg_hBuffer, 0);
        if (err)
        {
            /** Free the buffer and return error to caller **/
            
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_ERROR_NEW(IX_CC_ERROR_SEND_FAIL,
               IX_ERROR_LEVEL_WARNING);            

        }            
	
				
    }
    else if ( nexthopInfo.flags & IPV4_NH_FLAGS_DROP_BIT )
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log( "Route entry indicates drop target\n" );
#endif
        
        ix_rm_buffer_free_chain(arg_hBuffer);
       
    }
    else
    {
        /*
         * If TTL has expired, generate ICMP time Exceeded error message
         */
        if (ipHdr.ttl <= 1)
        {
            err = _ix_cc_ipv4_icmp_time_exceeded(arg_hBuffer);
            if (err)
            {
                ix_rm_buffer_free_chain(arg_hBuffer); 
                return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,
                                    IX_ERROR_LEVEL_WARNING);
            }            
            return IX_SUCCESS;
        }
        ipHdr.ttl--;
        /*
         * Fill up the buffer handle with L2Index as a result of lookup
         */
        l2Index = (ix_uint16)nexthopInfo.l2Index;
        pHwMeta->m_NextHopID = IX_HTON16(l2Index);
        port = (ix_uint16)nexthopInfo.portID;
        /* Fill out buffer meta with output port **/
        pHwMeta->m_OutputPort = IX_HTON16(port);
        pHwMeta->m_FabricPort = (ix_uint8)nexthopInfo.bladeID;        
        ipHdr.checksum = 0;
        /* Calculate checksum for IP header */
        ipHdr.checksum = _ix_cc_ipv4_calculate_checksum((ix_uint8*)&ipHdr, 
                                                       ipHdr.headerLength * 4);
        /* Copy the IP header onto buffer */
        ix_ossl_memcpy(pData, &ipHdr, ipHdr.headerLength * 4);
        
        if (_ix_cc_ipv4_get_port_status(ingressPort,
                                        &ingressPortStatus) != IX_SUCCESS ||
            _ix_cc_ipv4_get_port_status((ix_uint16)nexthopInfo.portID,
                                        &egressPortStatus)  != IX_SUCCESS)
        {
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);
        }
        if ((ingressPortStatus == IX_CC_IPV4_DISABLE_PORT_STATUS) ||
            (egressPortStatus == IX_CC_IPV4_DISABLE_PORT_STATUS))
        {
#ifdef DUMP_TO_SCREEN
            ix_ossl_message_log( "Port disabled destination unreachable\n" );
#endif
            err = _ix_cc_ipv4_icmp_dest_unreachable(arg_hBuffer,
                                                    IX_ICMP_UNREACH_HOST, 0);
            if (err != IX_SUCCESS)
            {
                ix_rm_buffer_free_chain(arg_hBuffer);
                return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,
                                    IX_ERROR_LEVEL_WARNING);
            }
            return IX_SUCCESS;
        }
        /*
         * Now to check for redirect: Check if ingress port is same as output
         * port ingressPort is in host order (16 bits)
         * nexthopInfo.portID is in host order (32 bits)
         */
        if ((ingressPort == (ix_uint16)nexthopInfo.portID) &&
            (g_Ipv4RegistryData.localBladeId == nexthopInfo.bladeID))
        {

            /*
             * We have to send the original packet back when sending 
             * an ICMP redirect error. 
             */
            ix_buffer_handle hDupBuffer;
#ifdef DUMP_TO_SCREEN
            ix_ossl_message_log( "Redirect\n" );
#endif
            err = _ix_cc_ipv4_duplicate_first_packet_buffer(arg_hBuffer, &hDupBuffer);
            if (err == IX_SUCCESS)
            {
                /* nexthopIpAddress will be in host byte order */
                nexthopIpAddress = nexthopInfo.ipAddr;
                err = _ix_cc_ipv4_icmp_redirect(hDupBuffer, nexthopIpAddress);
                if (err)
                {
                    ix_rm_buffer_free_chain(hDupBuffer);
                }
            }
        }
        /* Send the packet to next target */
        IX_ERROR_CR(_ix_cc_ipv4_pass_packet(arg_hBuffer));
    }
    return IX_SUCCESS;
} /* _ix_cc_ipv4_forward() */


/**
 * NAME: _ix_cc_ipv4_forward_pkts_from_stkdrvr
 *
 * DESCRIPTION:This function forwards the packets 
 *             received from stack driver
 *             
 *             
 * 
 * @Param:  - IN - arg_hBuffer - a buffer handle containing
 *            the packet that needs to be 
 *            forwarded  
 *		 
 * @Param:  - INOUT  - None
 * @Param:  - OUT - None  
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error token for 
 *          failure.
 */                    
ix_error _ix_cc_ipv4_forward_pkts_from_stkdrvr(
                           ix_buffer_handle arg_hBuffer)
{
    
    void *pData; 
    ix_uint32 ingressPortStatus = 0, egressPortStatus = 0;     
    ix_hw_buffer_meta *pHwMeta; 
    ix_uint32 dropped;
    ix_uint16 ingressPort;
    ix_cc_ipv4_header ipHdr;
    ix_cc_rtmv4_next_hop_info nexthopInfo;
    ix_error err = IX_SUCCESS;
    ix_uint16 l2Index;
    ix_uint16 port;
    /* For packets coming from stack driver CC, IPv4 Forwarder
    * Core Component does not
    * need to perform header validation. It does not need 
    *to decrement TTL, it needs to
    * do source directed broadcast check on source address**/
    /** For packets coming from stack, IPv4 Fwdr Core Component
    ** will not generate ICMP messages 
    ** to avoid conflict with ICMP messages generated by 
    ** TCP/IP stack 
    **/	
    
    /* extract data from buffer handle */
    err = ix_cc_hw_get_packet_data(arg_hBuffer, &pData);
    if (err)
    {
        
        ix_rm_buffer_free_chain(arg_hBuffer);
        return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,
 						IX_ERROR_LEVEL_WARNING);
        
    }
      

    /** Use ix_rm_buffer_get_meta to get input interface number */
    err = ix_rm_buffer_get_meta( arg_hBuffer, (void **)&pHwMeta);
    if (err)
    {
        
		ix_rm_buffer_free_chain(arg_hBuffer);
		return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,
 						IX_ERROR_LEVEL_WARNING);
        
    }
    /* IngressPort value is in host byte order */
    ingressPort = IX_NTOH16(IX_RM_MEM_UINT16_READ(
        &pHwMeta->m_InputPort));
#ifdef DUMP_TO_SCREEN    
    ix_ossl_message_log( "IPv4 Forwarder got a packet from"
      " stack driver Core Component from port  %x\n",
         ingressPort );
#endif /* DUMP_TO_SCREEN */    

    g_pIpv4Context->ipv4FwdStats.ipv4CorestatsIncomingDatagrams++;
    

    (void)_ix_cc_ipv4_init_ip_header( (ix_uint8 *)(pData),&ipHdr);
    IX_ERROR_CR(_ix_cc_ipv4_validate_IP_addresses(&ipHdr, arg_hBuffer, 
     &dropped));
    
    if ( dropped )
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log( "Invalid IP address, dropping\n" );
#endif /* DUMP_TO_SCREEN */

        
        ix_rm_buffer_free_chain(arg_hBuffer);
        return IX_SUCCESS;
    }
    
    /*
     * IP Strict Source Route option was processed in
     * the protocol stack and there is no need to process it here
     * Such an attempt would cause an ICMP parameter problem error.
     */
    
    /* Increment the statistics for number of packets for which
    forwarding was attempted */
    g_pIpv4Context->ipv4FwdStats.ipv4CorestatsForwardingAttempted++;

    /* Perform a route lookup on destination IP address */

    err = ix_cc_rtmv4_lookup(g_pIpv4Context->hRtm,
    IX_NTOH32(ipHdr.destAddress), &nexthopInfo);
    
    /*Check if route entry is invalid */
    if (nexthopInfo.l2Index == IX_CC_RTMV4_L2INDEX_NO_ROUTE)
    {
		g_pIpv4Context->ipv4FwdStats.ipv4CorestatsNorouteFound++;
#ifdef DUMP_TO_SCREEN		
		ix_ossl_message_log("Forwarding fn:Unreachable Host."
			"Lookup Failed for ip %x\n", ipHdr.destAddress);
#endif /* DUMP_TO_SCREEN */
        
		ix_rm_buffer_free_chain(arg_hBuffer );
        return IX_SUCCESS;	
    }
    /** It is not anticipated that packets coming from stack can 
    ** again be meant for local delivery **/
    /** This check is for just in case situation **/
    else if(nexthopInfo.flags & IPV4_NH_FLAGS_LOCAL_BIT)
    {

        /*send to stack */
		g_pIpv4Context->ipv4FwdStats.ipv4CorestatsLocalDelivery++;
#ifdef DUMP_TO_SCREEN
		ix_ossl_message_log("No of pkts for local delivery = %d\n",
			g_pIpv4Context->ipv4FwdStats.ipv4CorestatsLocalDelivery);
		/** The only meta data information that needs 
		** to be filled up in buffer to be used by L2 on Egress
        ** side is input port information but that will be filled up
		** by Rx microblock 
        **/
		ix_ossl_message_log("Local delivery\n" );
#endif /* DUMP_TO_SCREEN */
	
        /* Send the packet to Stack driver CC through high
        priority queue */
        err = ix_rm_message_send(
          IX_CC_IPV4_PKT_HIGH_PRIORITY_STKDRV_OUTPUT,
            arg_hBuffer, 0);
    	/* If error free the buffer **/
        if (err)
        {
            
            ix_rm_buffer_free_chain(arg_hBuffer);
			return IX_ERROR_NEW(IX_CC_ERROR_SEND_FAIL, 
                          IX_ERROR_LEVEL_WARNING);

        }
        				
    }
    else if ( nexthopInfo.flags & IPV4_NH_FLAGS_DROP_BIT )
    {
#ifdef DUMP_TO_SCREEN        
		ix_ossl_message_log( "Route entry indicates drop target\n" );
#endif /* DUMP_TO_SCREEN */
        		
		ix_rm_buffer_free_chain(arg_hBuffer );
    }
    else
    {
        
        if ( ipHdr.ttl < 1 )
        {
            /* Free the Buffer **/
	        
			ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_SUCCESS;            
	    
        }
        else /** If TTL has not expired **/
        {
            /** Fill out the buffer handle with nexthop ID and
            ** output port ID as a result of lookup **/
            l2Index = (ix_uint16)nexthopInfo.l2Index;
            pHwMeta->m_NextHopID = IX_HTON16(l2Index);
            port = (ix_uint16)nexthopInfo.portID;
	        pHwMeta->m_OutputPort = IX_HTON16(port);
            pHwMeta->m_FabricPort = (ix_uint8)nexthopInfo.bladeID;
            ipHdr.checksum = 0;
            /* Calculate checksum for IP header */
            ipHdr.checksum = _ix_cc_ipv4_calculate_checksum( (ix_uint8 *)&ipHdr, 
            ipHdr.headerLength * 4 );

            
            ix_ossl_memcpy(pData, &ipHdr, ipHdr.headerLength * 4);
            
			/* Check if ingress port is disabled */
      
			err = _ix_cc_ipv4_get_port_status(ingressPort, 
             &ingressPortStatus);
			if (err != IX_SUCCESS)
			{
                ix_rm_buffer_free_chain(arg_hBuffer);
				return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL, 
					IX_ERROR_LEVEL_WARNING);

			}	

			err = _ix_cc_ipv4_get_port_status(
            (ix_uint16)nexthopInfo.portID,
             &egressPortStatus);
			if (err != IX_SUCCESS)
			{
                ix_rm_buffer_free_chain(arg_hBuffer);
				return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL, 
					IX_ERROR_LEVEL_WARNING);

			}	


            /* If either ingress or egress port is disabled, drop the buffer */
            if ( egressPortStatus == IX_CC_IPV4_DISABLE_PORT_STATUS)
                                  
            {
#ifdef DUMP_TO_SCREEN
                ix_ossl_message_log( "Port disabled destination unreachable\n" );
#endif /* DUMP_TO_SCREEN */
				
				
				ix_rm_buffer_free_chain(arg_hBuffer);
                return IX_SUCCESS;                               
	
            }
           
    
		
            IX_ERROR_CR(_ix_cc_ipv4_pass_packet( arg_hBuffer ));
            
        }
    }
    return IX_SUCCESS;
}


/**
 * NAME: _ix_cc_ipv4_forward_pkts_from_cc
 *
 * DESCRIPTION:This function performs forwarding
 * of packets coming from Core Components other 
 * than Stack Driver Core Component
 *              
 * 
 * @Param:  - IN - arg_hBuffer - a buffer handle
 *  containing the packet that needs to be 
 *  forwarded  
 *		 
 * @Param:  - INOUT  - None
 * @Param:  - OUT - None  
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error 
 *          token for failure.
 */

ix_error _ix_cc_ipv4_forward_pkts_from_cc(ix_buffer_handle arg_hBuffer)
{
    
    void *pData; 
    ix_hw_buffer_meta *pHwMeta; 
    ix_uint16 len, ingressPort;
    ix_uint32 ingressPortStatus = 0, egressPortStatus = 0;        
    ix_cc_ipv4_header ipHdr;
    ix_uint32 dropped, nexthopIpAddress;
    ix_cc_rtmv4_next_hop_info nexthopInfo;
    ix_error err = IX_SUCCESS;
    ix_uint16 l2Index;
    ix_uint16 port;
    
    /* extract IP header */
    err = ix_cc_hw_get_packet_data(arg_hBuffer, &pData);
    /** Attempt to free the buffer if get data fails*/
    if (err)
    {
        
        ix_rm_buffer_free_chain(arg_hBuffer);
        return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);        
           
    }
   

    /** Use ix_rm_buffer_get_meta to get ifnum */
    err = ix_rm_buffer_get_meta( arg_hBuffer, (void **)&pHwMeta);
    if (err)
    {
        
        ix_rm_buffer_free_chain(arg_hBuffer);
        return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,IX_ERROR_LEVEL_WARNING);
        
    }
    /* Note m_InputPort is ix_uint16 */
    ingressPort = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pHwMeta->m_InputPort));

#ifdef DUMP_TO_SCREEN    
    ix_ossl_message_log( "IPv4 Forwarder got a packet from port %d\n",
      ingressPort );
#endif
    /** Storing len in host byte order **/
    len = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pHwMeta->m_PacketSize));

    /* Increment statistics for incoming datagrams */
    g_pIpv4Context->ipv4FwdStats.ipv4CorestatsIncomingDatagrams++;


#ifdef RFC1812_SHOULD
    /* if length of packet is less than minimum IP header
    length, then generate ICMP parameter problem message*/
    if ( len <  MIN_IP_LEN ) 
    {
        /* Increment statistics for number of packets with
        invalid header */
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsInvalidHeader++;
        err = _ix_cc_ipv4_icmp_param_problem( arg_hBuffer, 
        IX_ICMP_PARAM_TOTALLENGTH );
        if (err)
        {
            
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,IX_ERROR_LEVEL_WARNING);
        
        }
        return IX_SUCCESS;
    }
#endif /* RFC1812_SHOULD */

    (void)_ix_cc_ipv4_init_ip_header( (ix_uint8 *)(pData),&ipHdr);
    
    /* Validate IP header - RFC1812 checks */
    IX_ERROR_CR(_ix_cc_ipv4_IPheader_validate( &ipHdr, arg_hBuffer,
       &dropped ));
    
    if ( dropped )
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log( "Invalid IP header, dropping\n" );
#endif		
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsInvalidHeader++;
        
        ix_rm_buffer_free_chain(arg_hBuffer);
		return IX_SUCCESS;
    }
    /* Perform RFC1812 address validation */
    IX_ERROR_CR(_ix_cc_ipv4_validate_IP_addresses(&ipHdr, arg_hBuffer,
         &dropped ));
    
    if ( dropped )
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log( "Invalid IP address, dropping\n" );
#endif
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsInvalidAddress++;
     
		/* buffer has been freed in validate address fn **/
        return IX_SUCCESS;
    }
   

    g_pIpv4Context->ipv4FwdStats.ipv4CorestatsForwardingAttempted++;	
    
    /* Perform RTM lookup **/
    err = ix_cc_rtmv4_lookup(g_pIpv4Context->hRtm, IX_NTOH32(ipHdr.destAddress),
            &nexthopInfo);
    
    /* If route entry is invalid, send destination unreachable
    error message to source */
    if (nexthopInfo.l2Index == IX_CC_RTMV4_L2INDEX_NO_ROUTE)
    {
		 
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsNorouteFound++;
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log("Forwarding fn:Unreachable Host."
        " Lookup Failed for ip %x\n", ipHdr.destAddress);
#endif
		 
        err = _ix_cc_ipv4_icmp_dest_unreachable( arg_hBuffer,
                                     IX_ICMP_UNREACH_HOST, 0);
        if (err)
        {
            
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,
               IX_ERROR_LEVEL_WARNING);
             
        }
        return IX_SUCCESS;

    }
    /* If route entry indicates local delivery, send the packet
    to stack */
    if(nexthopInfo.flags & IPV4_NH_FLAGS_LOCAL_BIT)
    {

        
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsLocalDelivery++;
		
        /** The only meta data information that needs 
        ** to be filled up in buffer is input port information 
        ** before sending packet to stack but that will be filled up
        ** by Rx microblock **/
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log("Local delivery\n" );
#endif
     
        /* Send the packet to Stack driver CC through high priority
         queue */
        err = ix_rm_message_send(
           IX_CC_IPV4_PKT_HIGH_PRIORITY_STKDRV_OUTPUT,
            arg_hBuffer, 0);
        if (err)
        {
            /** Free the buffer and return error to caller **/
            
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_ERROR_NEW(IX_CC_ERROR_SEND_FAIL,
               IX_ERROR_LEVEL_WARNING);            

        }            

        return IX_SUCCESS;	
				
    }

    if ( nexthopInfo.flags & IPV4_NH_FLAGS_DROP_BIT )
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log( "Route entry indicates drop target\n" );
#endif
        
        ix_rm_buffer_free_chain(arg_hBuffer);
        return IX_SUCCESS;
    }

    pHwMeta->m_NextHopIDType = nexthopInfo.l2IndexType;

    if (nexthopInfo.l2IndexType != NHID_TYPE_IPV4)
    {
        pHwMeta->m_NextHopID     = IX_HTON16((ix_uint16)nexthopInfo.l2Index);
        pHwMeta->m_OutputPort    = IX_HTON16((ix_uint16)nexthopInfo.portID);
        pHwMeta->m_FabricPort = (ix_uint8)nexthopInfo.bladeID;
        /* Send the packet to next target */
        IX_ERROR_CR(_ix_cc_ipv4_pass_packet(arg_hBuffer));
        return IX_SUCCESS;
    }


   /*
    * If TTL has expired, generate ICMP time Exceeded error message
    */
    if (ipHdr.ttl <= 1)
    {
        err = _ix_cc_ipv4_icmp_time_exceeded(arg_hBuffer);
        if (err)
        {
            ix_rm_buffer_free_chain(arg_hBuffer); 
            return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,
                                IX_ERROR_LEVEL_WARNING);
        }            
        return IX_SUCCESS;
    }
         
    /* If IP header length > 5, then IP options are present */
    if ( ipHdr.headerLength > MIN_IP_LEN / 4 )
    {
        err = _ix_cc_ipv4_parse_ip_options( &ipHdr, arg_hBuffer,
                    &dropped );
        if (dropped)
        {
             
#ifdef DUMP_TO_SCREEN
           ix_ossl_message_log( "Invalid IP Options, dropping\n" );
#endif
           /** Free the buffer **/
           ix_rm_buffer_free_chain(arg_hBuffer);
           return IX_SUCCESS;
        }
    }
    ipHdr.ttl--;

   /*
    * Fill up the buffer handle with L2Index as a result of lookup
    */
    l2Index = (ix_uint16)nexthopInfo.l2Index;
    pHwMeta->m_NextHopID = IX_HTON16(l2Index);
    port = (ix_uint16)nexthopInfo.portID;
    /* Fill out buffer meta with output port **/
    pHwMeta->m_OutputPort = IX_HTON16(port);
       
    ipHdr.checksum = 0;
    /* Calculate checksum for IP header */
    ipHdr.checksum = _ix_cc_ipv4_calculate_checksum((ix_uint8*)&ipHdr, 
                                                   ipHdr.headerLength * 4);
    /* Copy the IP header onto buffer */
    ix_ossl_memcpy(pData, &ipHdr, ipHdr.headerLength * 4);
        
    if (_ix_cc_ipv4_get_port_status(ingressPort,
                                   &ingressPortStatus) != IX_SUCCESS ||
        _ix_cc_ipv4_get_port_status((ix_uint16)nexthopInfo.portID,
                                    &egressPortStatus)  != IX_SUCCESS)
    {
        ix_rm_buffer_free_chain(arg_hBuffer);
        return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);
    }
    if ((ingressPortStatus == IX_CC_IPV4_DISABLE_PORT_STATUS) ||
        (egressPortStatus == IX_CC_IPV4_DISABLE_PORT_STATUS))
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log( "Port disabled destination unreachable\n" );
#endif
        err = _ix_cc_ipv4_icmp_dest_unreachable(arg_hBuffer,
                                                IX_ICMP_UNREACH_HOST, 0);
        if (err != IX_SUCCESS)
        {
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,
                                IX_ERROR_LEVEL_WARNING);
        }
        return IX_SUCCESS;
    }
    /*
    * Now to check for redirect: Check if ingress port is same as output
    * port ingressPort is in host order (16 bits)
    * nexthopInfo.portID is in host order (32 bits)
    */
    if ((ingressPort == (ix_uint16)nexthopInfo.portID) &&
        (g_Ipv4RegistryData.localBladeId == nexthopInfo.bladeID))
    {
                        
            ix_buffer_handle hDupBuffer;
#ifdef DUMP_TO_SCREEN
            ix_ossl_message_log( "Redirect\n" );
#endif
            err = _ix_cc_ipv4_duplicate_first_packet_buffer(arg_hBuffer, &hDupBuffer);
            if (err == IX_SUCCESS)
            {
                /* nexthopIpAddress will be in host byte order */
                nexthopIpAddress = nexthopInfo.ipAddr;
                err = _ix_cc_ipv4_icmp_redirect(hDupBuffer, nexthopIpAddress);
                if (err)
                {
                    ix_rm_buffer_free_chain(hDupBuffer);
                }
            }

    }
         
   /* Send the packet to next target */
    IX_ERROR_CR(_ix_cc_ipv4_pass_packet(arg_hBuffer));

    return IX_SUCCESS;
} /* _ix_cc_ipv4_forward_pkts_from_cc() */



/**
 * NAME: _ix_cc_ipv4_process_options
 *
 * DESCRIPTION:This function performs forwarding
 * of packets coming from IPv4 microblock with Options
 * exception
 * 
 *             
 * 
 * @Param:  - IN - arg_hBuffer - a buffer handle
 *  containing the packet that needs to be 
 *  forwarded  
 *		 
 * @Param:  - INOUT  - None
 * @Param:  - OUT - None  
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error 
 *          token for failure.
 */

ix_error _ix_cc_ipv4_process_options (ix_buffer_handle arg_hBuffer)
{
    
    void *pData; 
    ix_hw_buffer_meta *pHwMeta; 
    ix_cc_ipv4_header ipHdr;
    ix_uint32 dropped;
    ix_error err = IX_SUCCESS;
    ix_uint16 l2Index;
    ix_uint16 len;    
    /* extract IP header */
    err = ix_cc_hw_get_packet_data(arg_hBuffer, &pData);
    /** Attempt to free the buffer if get data fails*/
    if (err)
    {
        
        ix_rm_buffer_free_chain(arg_hBuffer);
        return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);        
           
    }
   
    
    /* Increment statistics for incoming datagrams */
    g_pIpv4Context->ipv4FwdStats.ipv4CorestatsIncomingDatagrams++;

      
    /* Check if the packet coming from microblock has NHID==-1 */
    /* If so, generate ICMP error message */
    err = ix_rm_buffer_get_meta( arg_hBuffer, (void **)&pHwMeta);
    if (err)
    {
        
        ix_rm_buffer_free_chain(arg_hBuffer);
        return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,IX_ERROR_LEVEL_WARNING);
        
    }
    l2Index = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pHwMeta->m_NextHopID));
    
    if (l2Index == IX_CC_RTMV4_L2INDEX_NO_ROUTE)
    {
		 
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsNorouteFound++;
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log("Forwarding fn:Unreachable Host."
        " Lookup Failed for ip %x\n", ipHdr.destAddress);
#endif
		 
        err = _ix_cc_ipv4_icmp_dest_unreachable( arg_hBuffer,
                                     IX_ICMP_UNREACH_HOST, 0);
        if (err)
        {
        
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,
               IX_ERROR_LEVEL_WARNING);
             
        }
        return IX_SUCCESS;

    }
    /** Storing len in host byte order **/
    len = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pHwMeta->m_PacketSize));
#ifdef RFC1812_SHOULD
    /* if length of packet is less than minimum IP header
    length, then generate ICMP parameter problem message*/
    if ( len <  MIN_IP_LEN ) 
    {
        /* Increment statistics for number of packets with
        invalid header */
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsInvalidHeader++;
        err = _ix_cc_ipv4_icmp_param_problem( arg_hBuffer, 
        IX_ICMP_PARAM_TOTALLENGTH );
        if (err)
        {
            
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,IX_ERROR_LEVEL_WARNING);
        
        }
        return IX_SUCCESS;
    }
#endif /* RFC1812_SHOULD */

    (void)_ix_cc_ipv4_init_ip_header( (ix_uint8 *)(pData),&ipHdr);
    
    /* Validate IP header - RFC1812 checks */
    IX_ERROR_CR(_ix_cc_ipv4_IPheader_validate( &ipHdr, arg_hBuffer,
       &dropped ));
    
    if ( dropped )
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log( "Invalid IP header, dropping\n" );
#endif		
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsInvalidHeader++;
        
        ix_rm_buffer_free_chain(arg_hBuffer);
		return IX_SUCCESS;
    }
    /* Perform RFC1812 address validation */
    IX_ERROR_CR(_ix_cc_ipv4_validate_IP_addresses(&ipHdr, arg_hBuffer,
         &dropped ));
    
    if ( dropped )
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log( "Invalid IP address, dropping\n" );
#endif
        g_pIpv4Context->ipv4FwdStats.ipv4CorestatsInvalidAddress++;
     
		/* buffer has been freed in validate address fn **/
        return IX_SUCCESS;
    }
    
    /* If IP header length > 5, then IP options are present */
    if ( ipHdr.headerLength > MIN_IP_LEN / 4 )
    {
        err = _ix_cc_ipv4_parse_ip_options( &ipHdr, arg_hBuffer,
                        &dropped );
        if (dropped)
        {
             
#ifdef DUMP_TO_SCREEN
            ix_ossl_message_log( "Invalid IP Options, dropping\n" );
#endif
            /** Free the buffer **/
            ix_rm_buffer_free_chain(arg_hBuffer);
            return IX_SUCCESS;
        }
    }

    g_pIpv4Context->ipv4FwdStats.ipv4CorestatsForwardingAttempted++;	
    
    /*
     * If TTL has expired, generate ICMP time Exceeded error message
     */
    if (ipHdr.ttl <= 1)
    {
        err = _ix_cc_ipv4_icmp_time_exceeded(arg_hBuffer);
        if (err)
        {
            ix_rm_buffer_free_chain(arg_hBuffer); 
            return IX_ERROR_NEW(IX_CC_ERROR_INTERNAL,
                                    IX_ERROR_LEVEL_WARNING);
        }            
        return IX_SUCCESS;
    }
    ipHdr.ttl--;
    ipHdr.checksum = 0;
    /* Calculate checksum for IP header */
    ipHdr.checksum = _ix_cc_ipv4_calculate_checksum((ix_uint8*)&ipHdr, 
                                                       ipHdr.headerLength * 4);
    /* Copy the IP header onto buffer */
    ix_ossl_memcpy(pData, &ipHdr, ipHdr.headerLength * 4);
        
            
    /* Send the packet to next target */
    IX_ERROR_CR(_ix_cc_ipv4_pass_packet(arg_hBuffer));

    return IX_SUCCESS;
} /* _ix_cc_ipv4_process_options() */


