/*******************************************************************************
 *                            Intel Proprietary
 *
 * Copyright (c) 1998-2002 By Intel Corporation.  All rights reserved.  
 * No part of this program or publication may be reproduced, transmitted,
 * transcribed, stored in a retrieval system, or translated into any language
 * or computer language in any form or by any means, electronic, mechanical,
 * magnetic, optical, chemical, manual, or otherwise, without the prior
 * written permission of:
 *                        Intel Corporation
 *                        2200 Mission College Blvd.
 *                        Santa Clara, CA  95052-8119
 *******************************************************************************/

/*****************************************************************************
 *
 * File:  ipv4_fwdr.c
 *
 * Abstract:
 *      IPV4 forwarder takes performs header validation checks on the packet
 *		performs a longest prefix match on destination IP address and updates 
 *		the header with new TTL and checksum, updates the meda data with the
 *		result of the lookup.
 *
 * Contents:
 *		Definitions of the following exported functions :=
 *    	Ipv4Fwder()	:	performs the IPV4 forwarding    
 *
 *****************************************************************************/


#ifndef	__IPV4_FWDER_C__
#define	__IPV4_FWDER_C__

/*
 * include files
 */
#include	"ipv4_fwder_microc.h"
#include	"ipv4_fwder_util.c"

/**
 ****************************************************************************
 * @ingroup Ipv4Fwder
 * 
 * @description
 *		This function implements the IPV4 Forwarder microblock. It verifies the
 *		IP header according to RFC1812, RFC2644. If the packet is good, then a 
 *		Longest Prefix Match (LPM) is peformed on the destination address.
 *		The function then updates the packet header, and meta data before returning.
 *    
 * @param	pInIp 		- IN pointer to buffer containing IP Header
 * @param	inOffset 	- IN byte offset of IP header relative to start
 *							 of the buffer 
 * @param	pOutIp 		- IN pointer to the buffer to write the updated
 *							 IP header
 * @param	outOffset	- IN byte offset of IP header relative to start of
 *							 the output buffer
 * @param	inputMemType - IN enum specifying the type of memory containing
 *							 input buffer
 * @param	outputMemType - IN enum specifying the type of memory containing
 *							 output buffer
 *
 * @return 	Nothing                                                    
 **************************************************************************/
IPV4_INLINE	void	/* returns nothing */
	Ipv4Fwder(
	void 		*pInIp,
	uint32_t 	inOffset, 
	void 		*pOutIp, 
	uint32_t	outOffset,
	mem_t		inputMemType,
	mem_t		outputMemType)
{

	int 	result;
	UINT 	ipTotalLen;		// total length read from IP header 
	UINT 	inPort; 		// port from which this packet is rxed
	int		nextHopIndex;	// index of next hop information.
	UINT	destAddr;

	/* first check if this packet is for us? */
	if (dlNextBlock != BID_IPV4)
	{
		/* no by-pass path for this microblock */
		return;
	}

	/* point to stats */
#ifdef IPV4_COUNTERS_SCRATCH
	pIpv4FwderCounterBase = (ipv4_fwder_me_stat_t *)( 
								IPV4_STATS_TABLE_BASE | 
								(DL_META_GET_INPUT_PORT() << 
								STATS_PER_PORT_SHF_VAL));
#else
	pIpv4FwderCounterBase = (ipv4_fwder_me_stat_t *)( 
								STATS_TABLE_SRAM_BASE | 
								(DL_META_GET_INPUT_PORT() << 
								STATS_PER_PORT_SHF_VAL));
#endif

	// combine the inport with blade Id by loading bladeId
	// in most significant byte.
	inPort = DL_META_GET_INPUT_PORT() | (THIS_BLADE_ID <<24);

	/* update pkts received */
	IPV4_INCR_RX(pIpv4FwderCounterBase);


	// Now perform header validation according to RFC1812. The result 
	// is IXP_IPV4_SUCCESS for a good packet.
	result = Ipv4FwderValidateHeader(pInIp,inOffset,inputMemType);
	if (result != IPV4_SUCCESS)
		goto ipv4_check_result;

	/* update pkts attempted to fwd */
	IPV4_INCR_FWD(pIpv4FwderCounterBase);

	/* check the nexthop id */
	if (DL_META_GET_NEXTHOP_ID() == 0xffff)
	{
		destAddr = _buf_byte_extract((UINT*)pInIp,
						inOffset+IPV4_DESTINATION_ADDRESS_START_BYTE,
						IPV4_DESTINATION_ADDRESS_LEN,inputMemType);

		// Perform the lookup on the destination address in our forwarding table.
		nextHopIndex = Ipv4FwderLookup(destAddr);

		ipTotalLen = _buf_byte_extract((UINT*)pInIp,
						inOffset+IPV4_TOTAL_LENGTH_START_BYTE,
						IPV4_TOTAL_LENGTH_LEN,inputMemType);
		/*
		 Using the nexthop index, read and process next hop info
		 from SDRAM. Outcome of this macro will be IPV4_SUCCESS (then the
		 packet will be forwarded) or IPV4_FAILURE (packet is dropped) or
		 an exception code (packet is sent to X-Scale).
		*/
		result = Ipv4FwderProcNexthopInfo(inPort,ipTotalLen,nextHopIndex);
		if (result != IPV4_SUCCESS)
			goto ipv4_check_result;
	}

	/*
	 If we execute this, the packet is good to forward. Now we check TTL,
	 decrement TTL and update checksum. Outcome of this macro will be 
	 IPV4_SUCCESS (then the packet will be forwarded) or IPV4_FAILURE 
	 (packet is dropped) or an exception code (packet is sent to X-Scale).
	*/
	result = Ipv4FwderUpdateHeader(pOutIp,pInIp,inOffset,outOffset,
				inputMemType,outputMemType);
	if (result != IPV4_SUCCESS)
		goto ipv4_check_result;

	// send it to next block.
	dlNextBlock = IPV4_NEXT1;
	return;

ipv4_check_result:
	if (result != IPV4_FAILURE)
		goto ipv4_exception;

ipv4_failure:
	/* update drop pkts */
	IPV4_INCR_DROP(pIpv4FwderCounterBase);
	dlNextBlock = IX_DROP;
	return;

ipv4_exception:
	/* update exception pkts */
	IPV4_INCR_EXCP(pIpv4FwderCounterBase);
	dl_set_exception(BID_IPV4,result);
	dlNextBlock = IX_EXCEPTION;
	return;

}


#endif	//__IPV4_FWDER_C__