/**
 * ============================================================================
 * = 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) 2003 Intel Corporation. All rights reserved.
 *
 * = PRODUCT
 *      Intel(r) IXA SDK 3.1 for the IXP2000 series Network Processor
 *
 *
 *
 *
 * = MODULE
 *      IPv4 Forwarder Core Component
 *
 * = FILENAME
 *      ip_options.c
 *
 * = DESCRIPTION
 *   This file contains the implementation of IP options
 *   parsing function and also the implementation of IP options
 *   handling functions   
 *
 *
 *
 *
 * = CREATION TIME
 *      7/20/2002 1:56:24 PM
 *
 * = CHANGE HISTORY
 *
 * ============================================================================
 */

#define IX_ERROR_FILE_IDENT "$Id: ip_options.c,v 1.25 2003/11/04 21:36:30 rranjeet Exp $"

/**
 * System defined include files required.
 */

/**
 * User defined include files required.
 */
#include "ix_cc_error.h"
#include "cc/ipv4/internal/ip_options.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.
 */

/**
 * Function definitions.
 */

/**
 * NAME: _ix_cc_ipv4_parse_ip_options
 *
 * DESCRIPTION: This function is called when the packet has IP options
 *              It appropriately modifies the header of the packet
 *              and decides if the packet is to be dropped or forwarded.
 *              The actual dropping and forwarding of the packet 
 *              is caller's responsibility. The options that are
 *              handled are IP Record Route Option, IP timestamp
 *              option and Strict and Loose Source Route Option
 * 
 * @Param:  - IN arg_hBuffer - the actual IP packet 
 *		
 *            IN arg_pIphdr - Pointer to IP header
 * @Param:  - INOUT  - None
 * @Param:  - OUT - arg_pDrop - On return set to 1 if the packet 
 *                               should be dropped
 *                             On return if set to 0, the packet
 *                              should be forwarded  
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error token 
 *          for failure.
 *          arg_pDrop should be set accordingly on return
 */
ix_error _ix_cc_ipv4_parse_ip_options( 
                                      ix_cc_ipv4_header *arg_pIpHdr,
                                      ix_buffer_handle arg_hBuffer,
                                      ix_uint32 *arg_pDrop)
{
    ix_cc_ipv4_options_hdr *pOpt = NULL;
    ix_uint32 optLen = 0;
    ix_uint32 rrDone = 0, tsDone = 0, ssrrDone = 0, lsrrDone = 0;
    ix_uint32 forward;
    ix_uint32 cnt;
    ix_uint8 *cp;
    *arg_pDrop = 1;
    
    /* check how much is the length of IP header more than the minimum 
    ** length **/
    cnt  = (arg_pIpHdr->headerLength << 2) - MIN_IP_LEN;


    /** Start from the IP header at the point where MIN_IP_LEN ends **/
    cp   = (ix_uint8 *)arg_pIpHdr + MIN_IP_LEN;

    /* Iterate through the Options header to parse IP options
    and handle those */
    for (; cnt > 0; cnt -= optLen, cp += optLen)
    {
        
        (ix_uint8 *)pOpt = cp;
	
        /** EOL indicates the end of all options in IP header ***/
        if (pOpt->code == IX_CC_IPV4_IPO_EOL)
        {

            /*Parse IPOptions: EOL Encountered*/
            break;
        }
        
        /** NOP may be used between options **/
        if (pOpt->code == IX_CC_IPV4_IPO_NOP)
        {
            /* NOP in option */
            optLen = 1;	
        }
        else
        {
            optLen = pOpt->len;
            if (optLen <= 0 || optLen > cnt)
            {
                
                IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                                         IX_ICMP_PARAM_OPTION ));
                return IX_SUCCESS;
	
            }
        }

        switch (pOpt->code)
        {
        case IX_CC_IPV4_IPO_RR:
        if (++rrDone > 1)
        {
	
            /*Parse IP Options
            Multiple Record Route Option */		
            IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                                  IX_ICMP_PARAM_OPTION ));
            
            return IX_SUCCESS;
	   
        }
        IX_ERROR_CR(_ix_cc_ipv4_record_route_option(pOpt, arg_pIpHdr,
          arg_hBuffer, &forward)); 
        break;
        case IX_CC_IPV4_IPO_TIMESTAMP:
        if (++tsDone > 1)
        {
            IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                   IX_ICMP_PARAM_OPTION ));
            return IX_SUCCESS;
        }
        IX_ERROR_CR(_ix_cc_ipv4_timestamp_option(pOpt, arg_pIpHdr,
                  arg_hBuffer, &forward)); 
        break;
        case IX_CC_IPV4_IPO_LSRR:
        if (++lsrrDone > 1)
	{
            /*  Note that if ICMP returns error, buffer is freed
            in the forwarding code */
            IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                     IX_ICMP_PARAM_OPTION ));
            return IX_SUCCESS;
            
        }
        IX_ERROR_CR(_ix_cc_ipv4_source_route_option(pOpt, arg_pIpHdr,
                   arg_hBuffer, &forward)); 
        break;
        case IX_CC_IPV4_IPO_SSRR:
        if (++ssrrDone > 1)
        {
            
            IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                                IX_ICMP_PARAM_OPTION ));
            return IX_SUCCESS;
        }
        IX_ERROR_CR(_ix_cc_ipv4_source_route_option(pOpt, arg_pIpHdr,
            arg_hBuffer, &forward)); 
        break;
        case IX_CC_IPV4_IPO_NOP:
        /*Parse IPOptions: NOP in ip options */
        forward = 1;
        break;
        default:
        /** should pass through any unrecognised options **/
        forward = 1;
        break;
    }

    /* drop if any one of the options reports to drop the packet */ 
    if (!forward)
      return IX_SUCCESS;

    } /* end of for loop */
    /* everything is fine and the packet is to be forwarded */
    *arg_pDrop = 0;

    return IX_SUCCESS;
}


/**
 * NAME: _ix_cc_ipv4_record_route_option
 *
 * DESCRIPTION: This function processes the record route option
 *              It is called from _ix_cc_ipv4_parse_ip_options. 
 *              This function may modify the IP header. It also
 *              decides if the packet has to be dropped or 
 *              forwarded. The record route option provides
 *              a means to record the route of an IP 
 *		datagram
 * 
 * @Param:  - IN arg_hBuffer - a buffer handle containing the
 *                           packet with IP header 
 *		
 *            IN arg_pOpt - a pointer to the start of relevant
 *                            option in IP header
 *            IN arg_pIphdr - Pointer to IP header
 * @Param:  - INOUT  - None
 * @Param:  - OUT - arg_pForward - value to tell whether the packet 
 *                     should be dropped or forwarded.On return, if 
 *                     set to 1, the packet should be forwarded
 *                     On return,if set to 0, the packet should
 *                     be dropped. 
 *                              
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error token for failure.
 *          arg_pForward should be set accordingly on return
 */

ix_error _ix_cc_ipv4_record_route_option( ix_cc_ipv4_options_hdr *arg_pOpt,
                                         ix_cc_ipv4_header *arg_pIpHdr,
                                         ix_buffer_handle arg_hBuffer,
                                         ix_uint32 *arg_pForward
                                       )
{
    
    ix_uint32 addr;
    ix_uint32 dip;
    ix_uint16 ingressPort;
    ix_uint32 optLen;
    ix_uint32 optPtr;
    ix_uint8 *rrip;
    ix_hw_buffer_meta *pHwMeta;
    ix_uint32 ipAddr;    
    ix_cc_rtmv4_next_hop_info nexthopInfo;
   
   
    *arg_pForward = 0;

    /* check len & ptr */
    /* Extract the option length and option pointer from option header */
    optLen = arg_pOpt->len;
    optPtr = arg_pOpt->ptr;
    

    /** Check that length field in option header is not less than 7 **/
    /** Check that length field in option header is not greater than 40**/
    /** Check that pointer is not less than 4 **/
    /** The smallest possible legal value for pointer is 4 (RFC 791) **/

    if (optLen < 7 || optLen > 40 || optPtr < 4 || (optPtr & 0x3) )
    {
        /*Generate ICMP param problem message */
        IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                                  IX_ICMP_PARAM_OPTION ));
        return IX_SUCCESS;
	
    }

    /* get src & dst */
    /** Since IP header has been filled up in forwarding code,
    Destination IP address is in network byte order. Convert it 
    to host byte order*/
    dip = IX_NTOH32( arg_pIpHdr->destAddress );
    
    /** Get the interface/port from buffer handle **/
    IX_ERROR_CRT(ix_rm_buffer_get_meta(arg_hBuffer, (void **)&pHwMeta),
        IX_CC_ERROR_INTERNAL, 0);
    /* Note m_InputPort is ix_uint16 */
    ingressPort  = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pHwMeta->m_InputPort));
    

    /**If the route data area is already full (the pointer exceeds the
    length) the datagram is forwarded without inserting the address
    into the recorded route.  If there is some room but not enough
    room for a full address to be inserted, the original datagram is
    considered to be in error and is discarded.  In either case an
    ICMP parameter problem message may be sent to the source
    host **/

    /* handle the optPtr > optLen case 
    * RFC 791 [page 21]  */
    /* Case: full. There is no space to record route */
    if (optPtr > optLen - 4)
    {
        *arg_pForward = 1;
        return IX_SUCCESS;
    }

    /* lookup route using dest IP address */
    /** RTM expects dest IP address in host byte order */
    ix_cc_rtmv4_lookup(g_pIpv4Context->hRtm, dip, &nexthopInfo);
   
    if (nexthopInfo.l2Index == IX_CC_RTMV4_L2INDEX_NO_ROUTE)
    {
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log("ix_cc_ipv4_record_route: Unreachable Host."
            "Lookup Failed for ip %x\n", dip);
#endif
        IX_ERROR_CRT(_ix_cc_ipv4_icmp_dest_unreachable( arg_hBuffer,
                                     IX_ICMP_UNREACH_HOST, 0),
                        IX_CC_ERROR_INTERNAL, 0);
        return IX_SUCCESS;
    }

 
    /** Set the value where IP address will be recorded */
    rrip = (ix_uint8 *)arg_pOpt + optPtr - 1;
 
   
    /** RFC1812 MUST feature **/
    /** Routers are called upon to insert their address into Record Route,
    ** Strict Source and Record Route, Loose Source and Record Route,
    ** or Timestamp Options. When a router inserts its address into such 
    ** an option, it MUST use the IP address of the logical interface on 
    ** which the packet is being sent **/ 
   
    /** Check if the packet is meant for local delivery **/
    if (nexthopInfo.flags & IPV4_NH_FLAGS_LOCAL_BIT)
    {

        /* The packet is addressed to "me". */
        /* so record the ip of incoming interface,*/
        /* if there is space. */
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log("ix_cc_ipv4_record_route: "
         "The packet is addressed to me\n");
#endif
        /** Get the IP address of the input interface **/
        /** The following function returns IP address in host order**/
       
        IX_ERROR_CRT(_ix_cc_ipv4_get_port_ip_address(
                 g_Ipv4RegistryData.localBladeId, ingressPort,
		                          &ipAddr),
                          IX_CC_ERROR_INTERNAL, 0); 
    }
    else
    {
         /*record the ip address of the outgoing interface. */
#ifdef DUMP_TO_SCREEN
        ix_ossl_message_log("ix_cc_ipv4_record_route: The packet"
          " is addressed to another machine\n");
#endif
        /**  get IP address of outgoing port **/
        IX_ERROR_CRT(_ix_cc_ipv4_get_port_ip_address(
                               nexthopInfo.bladeID,
			       (ix_uint16)nexthopInfo.portID,
	                              &ipAddr),
                                IX_CC_ERROR_INTERNAL, 0);


    }
    addr = IX_HTON32(ipAddr);
    /* Record the address in packet **/   
    ix_ossl_memcpy(rrip, &addr, sizeof(ix_uint32));
    /* Increment the pointer by 4 bytes as IP address 
    is 4 bytes*/
    optPtr += sizeof(ix_uint32);
        
    arg_pOpt->ptr = (ix_uint8)optPtr;	/* update ptr in the header */
    *arg_pForward = 1;

    return IX_SUCCESS;

}


/**
 * NAME: _ix_cc_ipv4_timestamp_option
 *
 * DESCRIPTION: This function processes the timestamp option
 *              It is called from _ix_cc_ipv4_parse_ip_options.
 *              This function may modify the IP header because
 *              it may record the IP address and timestamp
 *              value.
 *              It also decides if the packet has to
 *              dropped or forwarded.
 * 
 * @Param:  - IN arg_hBuffer - a buffer handle containing the 
 *                            packet with IP header 
 *		
 *            IN arg_pOpt - a pointer to the start of relevant
 *                           option in IP header
 *            IN arg_pIphdr - Pointer to IP header
 * @Param:  - INOUT  - None
 * @Param:  - OUT - arg_pForward - value to tell whether the packet 
 *                       should be dropped or forwarded.
 *                       On return, if set to 1, the packet 
 *                       should be dropped. On return,
 *                       if set to 0, the packet should be forwarded. 
 *                              
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error token for failure.
 *          arg_pForward should be set accordingly on return
 */

ix_error _ix_cc_ipv4_timestamp_option( ix_cc_ipv4_options_hdr *arg_pOpt,
                                         ix_cc_ipv4_header *arg_pIpHdr,
                                         ix_buffer_handle arg_hBuffer,
                                         ix_uint32 *arg_pForward
                                       )
{
    
    ix_uint32 dip;
    ix_uint8 *pTemp;
    ix_cc_ipv4_ip_timestamp *pIpt;
    ix_uint32 optLen;
    ix_uint32 optPtr;
    ix_uint16 overflow;
    ix_uint32 addr;
    ix_uint32 ipAddr;
    ix_uint16 xgressPort;	/* ingress or egress :-)  */
    ix_error err;
    ix_hw_buffer_meta *pHwMeta;
    ix_uint32 timeVal;
    ix_cc_ipv4_time_val timeStruct;
   
    ix_cc_rtmv4_next_hop_info nexthopInfo;
    
   

    *arg_pForward = 0;

    pIpt = (ix_cc_ipv4_ip_timestamp *)arg_pOpt;
    optLen = pIpt->len;
    optPtr = pIpt->ptr;
    overflow = (ix_uint16)pIpt->overflow;
    /* Note that destination IP address is in network 
     byte order */
    dip =  arg_pIpHdr->destAddress;

    /* check len & ptr */
    /** The maximum length of option according to RFC 791 is 40 **/
    /** The smallest legal value for pointer is 4 **/ 
    
    if (optLen < 5 || optLen > 40 || optPtr < 4)
    {
        IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
           IX_ICMP_PARAM_OPTION ));
        return IX_SUCCESS;
		
    }

    /* check if full **/
    /***************************************************
     If the timestamp data area is already full (the pointer exceeds
     the length) the datagram is forwarded without inserting the
     timestamp, but the overflow count is incremented by one.
      
     If the overflow count itself overflows, the
     original datagram is considered to be in error and is discarded.
     In this case an ICMP parameter problem message may be sent to
     the source host 
    ************************/
    if (optPtr > optLen - 4)
    {
        /** Since overflow field is 4 bits, max value that can be 
        ** represented as 2^4=16 */
        /** the overflow count itself overflows, the
        *original datagram is considered to be in error and is discarded.
        *In this case an ICMP parameter problem message may be sent to
        *the source host ***/
        if ((++overflow) > 0x0f)
        {
             IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                   IX_ICMP_PARAM_OPTION ));
             return IX_SUCCESS;
		
        }

        /*no space. nothing to do. Just forward. */
        pIpt->overflow = (ix_uint8)overflow;
        *arg_pForward = 1;
        return IX_SUCCESS;
    }
    
    /**********************************************************
     The Flag (flg) [4 bits] values are

          0 -- time stamps only, stored in consecutive 32-bit words,

          1 -- each timestamp is preceded with internet address of the
               registering entity,

          3 -- the internet address fields are prespecified.  An IP
               module only registers its timestamp if it matches its own
               address with the next specified internet address.
    ********************************************************/
    switch (pIpt->flag)
    {
        case IX_CC_IPV4_IPO_TS_TSONLY:
        break;

        case IX_CC_IPV4_IPO_TS_TSANDADDR:
        {
            if (optPtr + sizeof(ix_uint32) + sizeof(timeStruct) > optLen)
            {
                IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                 IX_ICMP_PARAM_OPTION ));
                return IX_SUCCESS;
			
            }
            /*
            * Record the route. This is similar to what we do in RecordRoute.
            * i.e if the packet is addressed to one of the local interfaces,
            * record the ip of the incoming interface.
            * Otherwise record the ip of the outgoing
            * interface.
            */

            IX_ERROR_CR(ix_cc_rtmv4_lookup(g_pIpv4Context->hRtm,
              IX_NTOH32(dip), &nexthopInfo));
			
    
            /* Route entry invalid */
            if (nexthopInfo.l2Index == IX_CC_RTMV4_L2INDEX_NO_ROUTE)
            {
#ifdef DUMP_TO_SCREEN
                ix_ossl_message_log("IP Options Time Stamp:Unreachable Host"
				"Lookup Failed for ip %x\n", dip);
#endif
                IX_ERROR_CR(_ix_cc_ipv4_icmp_dest_unreachable( arg_hBuffer,
                                     IX_ICMP_UNREACH_HOST, 0));
                return IX_SUCCESS;
				
            }

            if (nexthopInfo.flags & IPV4_NH_FLAGS_LOCAL_BIT)
            {
                /* The packet is addressed to "me". */
                /* so record the ip of incoming interface,*/
                /* if there is space. */
                ix_ossl_message_log("IP Options Time Stamp: "
                "The packet is addressed to me\n");
                /** Get the interface/port from buffer handle **/
                IX_ERROR_CRT(ix_rm_buffer_get_meta( arg_hBuffer,
                             (void**)&pHwMeta),
                              IX_CC_ERROR_INTERNAL, 0);
		
                /* Note m_InputPort is ix_uint16 */
                xgressPort  = IX_NTOH16(IX_RM_MEM_UINT16_READ(
                    &pHwMeta->m_InputPort));
            }
            else
            {
                xgressPort = (ix_uint16)nexthopInfo.portID;

            }
    

            IX_ERROR_CRT(_ix_cc_ipv4_get_port_ip_address(
                              g_Ipv4RegistryData.localBladeId,
		               xgressPort,
		                 &ipAddr), 
                             IX_CC_ERROR_INTERNAL,0);
            addr = IX_HTON32(ipAddr);
            /**
             * Note that the Pointer is the number of octets
             *from the beginning of this option to the end of
             *timestamps plus one (i.e., it points to the
             *octet beginning the space for next timestamp).
            **/
            pTemp = (ix_uint8 *)pIpt + optPtr - 1;

           
            /* Copy the address into option header */
            ix_ossl_memcpy((ix_uint8 *)pTemp, &addr,
                sizeof(ix_uint32));
            optPtr += sizeof(ix_uint32);
        }
        break;

        case IX_CC_IPV4_IPO_TS_PRESPEC:
        {
            
            
            if (optPtr + sizeof(ix_uint32) + sizeof(timeStruct) > optLen)
            {
                IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
				IX_ICMP_PARAM_OPTION ));
                return IX_SUCCESS;
			
            }
   
            /** the internet address fields are prespecified.  An IP
               module only registers its timestamp if it matches its own
               address with the next specified internet address.
            ****/
            pTemp = (ix_uint8 *)pIpt + optPtr - 1;

            ix_ossl_memcpy(&addr, (ix_uint8 *)pTemp, sizeof(ix_uint32));
            

            /*
            * We need to find if the addr is 'local'. 
            */
            /** This API is internal to IPv4 and is more efficient
            to find if addr is local - optionally we can get the same
            information from RTM lookup**/
            err = _ix_cc_ipv4_prop_table_lookup_ip_address(IX_NTOH32(addr));
            if (err)
            {
                *arg_pForward = 1;
                return IX_SUCCESS;

            } 
        

            
            optPtr += sizeof(ix_uint32);
        }
        break;

        default:
            IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                    IX_ICMP_PARAM_OPTION ));
            return IX_SUCCESS;
		
    }     
    IX_ERROR_CRT(ix_ossl_time_of_day_get(&timeVal), 
            IX_CC_ERROR_INTERNAL, 0);
    timeStruct.milliseconds = timeVal;
    pTemp = (ix_uint8 *)pIpt + optPtr - 1;  
    /* Record the timestamp value **/
    ix_ossl_memcpy(pTemp, &timeStruct, sizeof(timeStruct));
    /* Increment the option pointer */
    optPtr += sizeof(timeStruct);
    pIpt->ptr = (ix_uint8)optPtr;/* update the header with latest ptr */

    *arg_pForward = 1;
    return IX_SUCCESS;

}



/**
 * NAME: _ix_cc_ipv4_source_route_option
 *
 * DESCRIPTION: This function processes the Strict/Loose source 
 *              route option. It is called from
 *              _ix_cc_ipv4_parse_ip_options. This function             
 *              may modify the IP header. It also decides if 
 *              the packet has to dropped or forwarded.
 * 
 *              The loose source and record route (LSRR) option
 *              provides a means for the source of an internet 
 *              datagram to supply routing information
 * 		to be used by the gateways in forwarding the 
 *              datagram to the destination, and to record the
 *              route information 
 * 
 * @Param:  - IN arg_hBuffer - a buffer handle containing 
 *            the packet with IP header 
 *		
 *            IN arg_pOpt - a pointer to the start of 
 *                        relevant option in IP header
 *            IN arg_pIphdr - Pointer to IP header
 * @Param:  - INOUT  - None
 * @Param:  - OUT - arg_pForward - value to tell whether the packet 
 *                             should be dropped or forwarded.
 *                             On return, if set to 1, the packet
 *                             should be dropped. On return,
 *                             if set to 0, the packet should be
 *                             forwarded. 
 *                              
 *
 * @Return: IX_SUCCESS if successful or a valid ix_error token
 *          for failure.
 *          arg_pForward should be set accordingly on return
 */
ix_error _ix_cc_ipv4_source_route_option( ix_cc_ipv4_options_hdr *arg_pOpt,
                                         ix_cc_ipv4_header *arg_pIpHdr,
                                         ix_buffer_handle arg_hBuffer,
                                         ix_uint32 *arg_pForward
                                       )
{
    
    ix_uint32 dip;
    
    ix_uint32 optLen;
    ix_uint32 optPtr;
    ix_uint32 optCode;
    ix_uint32 addr, rip;
    ix_uint8 *pTemp;
    ix_uint32 ipAddr;   
    ix_cc_rtmv4_next_hop_info nexthopInfo;
    ix_cc_rtmv4_next_hop_info nexthopInfo1;
    

    *arg_pForward = 0;

    optLen = arg_pOpt->len;
    optPtr = arg_pOpt->ptr;
    optCode = arg_pOpt->code;



    /* The smallest possible legal value for pointer is 4 */
    if (optPtr < 4)
    {
        IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                     IX_ICMP_PARAM_OPTION ));
        return IX_SUCCESS;
		
    }
    /** If the pointer is greater than the length, the source route
    ** is empty (and the recorded route full) and the routing is to
    ** be based on the destination address field **/

    /* source route is empty (i.e recorded route is full).Simply forward */
    if (optPtr > optLen)
    {
        *arg_pForward = 1;
        return IX_SUCCESS;
    }

    /* get dst */
    dip = IX_NTOH32( arg_pIpHdr->destAddress );
    IX_ERROR_CR(ix_cc_rtmv4_lookup(g_pIpv4Context->hRtm,
         dip, &nexthopInfo));

    /** If route entry is not valid, send Destination unreachable msg **/
    if (nexthopInfo.l2Index == IX_CC_RTMV4_L2INDEX_NO_ROUTE)
    {
        IX_ERROR_CR(_ix_cc_ipv4_icmp_dest_unreachable( arg_hBuffer,
                                     IX_ICMP_UNREACH_HOST, 0));
        return IX_SUCCESS;
			
    }
    /* The loose source route option is so called because the gateway
    * or host IP is allowed to use any route of any number of other
    * intermediate gateways to reach the next address in the route */
    /* The strict source route option is so called because the gateway
    *or host IP must send the datagram directly to the next address in
    *the source route through only the directly connected network
    *indicated in the next address to reach the next gateway or host
    *specified in the route */
    
    /*
     * If dst is not 'local', normal forwarding only for LSRR,
     * for SSRR this is error - send icmp error msg.
     */
    if (!(nexthopInfo.flags & IPV4_NH_FLAGS_LOCAL_BIT))
    {
        if (optCode == IX_CC_IPV4_IPO_SSRR)
        {
            IX_ERROR_CR(_ix_cc_ipv4_icmp_param_problem( arg_hBuffer,
                      IX_ICMP_PARAM_OPTION));
            return IX_SUCCESS;
        }
        /* Loose Routing. So simply forward. */
        *arg_pForward = 1;
        return IX_SUCCESS;
    }	
    
    /* Check for more source routes */
  
    if (optPtr + sizeof(ix_uint32) - 1 > optLen)
    {
        /* 
        * dst is local and there are no more source routing entries.
        * This means the packet has arrived at its destination.
        * send it to the stack
        */
        /*outside of this function,
        the packet will be delivered to the stack */
        *arg_pForward = 1;
        return IX_SUCCESS;
    }
    /* get next addr from ip list as the new dst address */
    /** What we are trying to do here is to take the next IP address 
    ** from the option header and do a lookup.
    ** We are checking what is the next hop for this IP address. 
    ** If the nexthop for the route entry is not the same as the IP 
    ** listed in IP options header, then for strict source and record
    ** route option, it is an error
    **/    
    pTemp = (ix_uint8 *)arg_pOpt + optPtr - 1;  

    
    ix_ossl_memcpy(&addr, pTemp, sizeof(ix_uint32));
    

    /* look up dst in route table, to determine egress port. */ 
    /* get ip of egress port & record it. */
    
    IX_ERROR_CR(ix_cc_rtmv4_lookup(g_pIpv4Context->hRtm,
      IX_NTOH32(addr), &nexthopInfo1));
	
    
    if ( IX_HTON32(nexthopInfo1.l2Index) == IX_CC_RTMV4_L2INDEX_NO_ROUTE)
    {
        IX_ERROR_CR(_ix_cc_ipv4_icmp_dest_unreachable( arg_hBuffer,
           IX_CC_IPV4_ICMP_SOURCE_ROUTE_FAILED, 0));
        return IX_SUCCESS;
           
    }

    /* For SSRR: The next system (addr) should be directly connected.
    * Otherwise it's an error. */
    
    if (optCode == IX_CC_IPV4_IPO_SSRR)
    {
        /* If addr is not the same as nexthop IP address
         then generate ICMP error message */
#if (defined IX_PLATFORM_2401) || (defined IX_PLATFORM_2801)

        /* The ICMP replay from DUT is not seen on expected port when fargmentation is needed.
        * if the nexthop is stack driver */
        if (!(nexthopInfo1.flags & IPV4_NH_FLAGS_LOCAL_BIT) && 
			(addr != IX_HTON32(nexthopInfo1.ipAddr)))
#else /* IX_PLATFORM_2401 || IX_PLATFORM_2801 */
        if (addr != IX_HTON32(nexthopInfo1.ipAddr))
#endif /* IX_PLATFORM_2401 || IX_PLATFORM_2801 */
        {
            IX_ERROR_CR(_ix_cc_ipv4_icmp_dest_unreachable( arg_hBuffer,
              IX_CC_IPV4_ICMP_SOURCE_ROUTE_FAILED, 0));
            return IX_SUCCESS;
             
        }
    }

    /* find the ip of outgoing interface */
    /* Finally make the neccessary changes to the ip header */
    
#if (defined IX_PLATFORM_2401) || (defined IX_PLATFORM_2801)

	/* Defect #IGK0100022735:
	   The ICMP replay from DUT is not seen on expected port when fargmentation is needed. */
	/* if the nexthop is stack driver */
	if (nexthopInfo1.flags & IPV4_NH_FLAGS_LOCAL_BIT)
	{
		ipAddr = addr;
	}
	else
	{
#endif /* IX_PLATFORM_2401 || IX_PLATFORM_2801 */
		IX_ERROR_CRT(_ix_cc_ipv4_get_port_ip_address(nexthopInfo1.bladeID,
			                      (ix_uint16)nexthopInfo1.portID,
	 		                       &ipAddr), 
    	                                  IX_CC_ERROR_INTERNAL, 0);
#if (defined IX_PLATFORM_2401) || (defined IX_PLATFORM_2801)

	}
#endif /* IX_PLATFORM_2401 || IX_PLATFORM_2801 */
    rip = IX_HTON32(ipAddr);
    
    ix_ossl_memcpy(pTemp, &rip, sizeof(ix_uint32));
    /* Increment the pointer by size of IP address */
    arg_pOpt->ptr += sizeof(ix_uint32);
    arg_pIpHdr->destAddress = addr;/*addr is already in network byte order*/

    *arg_pForward = 1;
    return IX_SUCCESS;
}
