/*******************************************************************************
 *                            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_fwder_util.c
 *
 * Abstract:
 *      The file contains utility functions to perform the following.
 *		- header validation checks on the packet
 *		- performs a longest prefix match on destination IP address 
 *		- process nexthop information and update meta data
 *		- update the header with new TTL and checksum
 *		- debug utility function
 *
 * Contents:
 *		Definitions of the following local functions :=
 *    	Ipv4Fwder()	:	performs the IPV4 forwarding    
 *
 *****************************************************************************/

#ifndef	__IPV4_FWDER_UTIL_C__
#define	__IPV4_FWDER_UTIL_C__


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

/*****************************************************************************
 * Abstract:
 *    Utility function to log a value in SRAM. This function doesn't swap
 *	  out the calling threead.
 *
 * Parameters :
 *    inVal		-	IN the value to be logged
 * 	  where		-	IN the address in SRAM where the value is logged.
 *
 * Returns:
 *    Nothing.
 *
 *****************************************************************************/
INLINE	void	
	Ipv4FwderDbgLogWait(
	uint32_t		inVal, 
	uint32_t		where)
{
	// this function writes to scratch[where]
	__declspec(sram_write_reg) unsigned int output;
	SIGNAL	sig;

	output = inVal;
	
	sram_write(&output,(void __declspec(sram) *)(where),1,sig_done,&sig);

	while (signal_test(&sig) == 0);

	return;

} /* Ipv4FwderDbgLogWait */

/*****************************************************************************
 * Abstract:
 *    Atomically increment counter in SRAM
 *
 * Parameters :
 *    pInBase			-	IN base address of the statistics structure
 *	  inOffset			-	IN longword offset of specific counter
 *
 * Returns:
 *    Nothing.
 *
 *****************************************************************************/
INLINE void 
	Ipv4FwderIncrCounter(
	uint32_t	*pInBase,
	uint32_t	inOffset)
{

#ifdef	IPV4_MIN_INTRINSIC

	pInBase[inOffset]++;

#else

#ifdef IPV4_COUNTERS_SCRATCH
	scratch_incr((volatile void __declspec(scratch) *)(pInBase + inOffset));
#else
	sram_incr((volatile void __declspec(sram) *)(pInBase + inOffset));
#endif

#endif

} /* Ipv4FwderIncrCounter */

/*****************************************************************************
 * Abstract:
 *    Perform Longest Prefix Match (LPM) for the destination IPV4 address.
 *
 * Parameters :
 *    da	-	IN the IPV4 destination address
 *
 * Returns:
 *    Index of the nexthop information.
 *
 *****************************************************************************/
INLINE	int 					/* returns an integer */
	Ipv4FwderLookup(
	uint32_t	da)
{
	int index;
	index = ixp_ipv4_route_lookup(da,(volatile __declspec(sram) void *)TRIE_TABLE_SRAM_BASE,TRIE5);
	return index;
}	/* Ipv4FwderLookup */

/*****************************************************************************
 * Abstract:
 *    Validates IP header according to RFC1812 and RFC2644.
 *
 * Parameters :
 *    pInIp		-	IN pointer to buffer containing IP header
 *	  inOffset	-	IN byte offset of IP header relative to start of the buffer
 *	  inputMemType - IN enum specifying what type of memory is input buffer
 *
 * Returns:
 *    IXP_IPV4_SUCCESS	- If the packet passes all the checks.
 *	  IXP_IPV4_FAILURE	- If the packet fails any of the checks
 *	  an exception code	- If the packet has to be sent to core
 *
 *****************************************************************************/

INLINE	int						/* returns an integer */
	Ipv4FwderValidateHeader(
	void 		*pInIp,
	uint32_t 	inOffset,
	mem_t		inputMemType)
{
	UINT	srcAddr;
	UINT	verHdr; 

	// verify if the packet received is atleast 20 bytes
	if (DL_META_GET_PACKET_SIZE() < 20)
	{
		/* update stats for toosmall and drop */
		IPV4_INCR_TOOSMALL(pIpv4FwderCounterBase);
		return IPV4_FAILURE;
	}

	verHdr = _buf_byte_extract((UINT*)pInIp,inOffset+IPV4_VERSION_LENGTH_START_BYTE,
			IPV4_VERSION_LENGTH_LEN,inputMemType);

	// check if the version is 0x4 and header length is 0x5.
	if (verHdr == 0x45)
	{
		int result;

#ifdef	RFC1812_SHOULD
		UINT	totalLen;
		
		totalLen = _buf_byte_extract((UINT*)pInIp,inOffset+IPV4_TOTAL_LENGTH_START_BYTE,
			IPV4_TOTAL_LENGTH_LEN,inputMemType);

		if (totalLen > DL_META_GET_PACKET_SIZE())
		{
			/* packets with bad length */
			IPV4_INCR_BADLEN(pIpv4FwderCounterBase);
			return IPV4_EXCP_LENGTH_MISMATCH;
		}

#endif

		// now verify the IP header for RFC1812 compliance.
		result = ixp_ipv4_verify((void *)pInIp,inOffset,inputMemType);

#ifdef	RFC2644_CHECKS

		if (result == IPV4_SUCCESS)
		{


			UINT srcAddr;

			srcAddr = _buf_byte_extract((UINT*)pInIp,inOffset+IPV4_SOURCE_ADDRESS_START_BYTE,
				IPV4_SOURCE_ADDRESS_LEN,inputMemType);

			result = ixp_ipv4_is_dbcast(srcAddr,(volatile __declspec(sram) void *)DBCAST_TABLE_SRAM_BASE,
				DBCAST_TABLE_BLOCK_SIZE);

		}
#endif
		return result;

	}
	else 
	{
		if (((verHdr>>4) != 4)  || ((verHdr & 0xf) < 0x5))
		{
			/* the header length < 5, which is invalid. So drop */
			IPV4_INCR_BADHDR(pIpv4FwderCounterBase);
			return IPV4_FAILURE;
		}
		else
			/* options */
			return IPV4_EXCP_OPTIONS;
	}
			
}	/* Ipv4FwderValidateHeader */

/*****************************************************************************
 * Abstract:
 *    Process nexthop information and update the meda data.
 *
 * Parameters :
 *    inPort		-	IN the port from which the packet is received in this
 *						format	  
 *							  +-----------------+-------------------+ 
 *							  | blade ID(8bits) | port (24bits)     |
 *							  +-----------------+-------------------+
 *	  inIpTotalLen	-	IN byte offset of IP header relative to start of the buffer
 *	  inNextHopIndex	-	IN index of the nexthop information obtained by 
 *								route lookup.
 *
 * Returns:
 *    IPV4_SUCCESS		- If the packet can be forwarded.
 *	  IPV4_FAILURE		- If the packet needs to be dropped
 *	  an exception code	- If the packet has to be sent to core
 *
 *****************************************************************************/
INLINE 	int	
	Ipv4FwderProcNexthopInfo(
	uint32_t	 inPort,
	uint32_t	 inIpTotalLen,
	int32_t		 inNextHopIndex)
{

#ifdef	PROCESS_CONTROL_BLOCK
	__declspec(sram_read_reg) unsigned int portStatus;
	SIGNAL portStatusSig;
	ipv4_fwder_control_block_t	*pCtrlBlk = 
			(ipv4_fwder_control_block_t	*)CONTROL_BLOCK_SRAM_BASE;
#endif

#ifdef	IPV4_MIN_INTRINSIC
	nexthop_info_t	nhInfo;		/* our cache to hold nexthop info */
#else
#ifdef	NEXTHOP_INFO_SRAM
	__declspec(sram_read_reg) nexthop_info_t	nhInfo;
	SIGNAL	sram_sig;
#else
	__declspec(dram_read_reg) nexthop_info_t	nhInfo;
	SIGNAL_PAIR	dram_sig;
#endif
#endif

#ifdef	NEXTHOP_INFO_SRAM
	/* the nexthop info is in SRAM */

	nexthop_info_t	*pNhInfo;		/* ptr to NH info in SRAM */

	pNhInfo = (nexthop_info_t *)(NEXTHOP_TABLE_SRAM_BASE +
					(inNextHopIndex << NEXTHOP_INFO_SHF_VAL));

#else
	/* otherwise we have the nexthop info in DRAM. */

	__declspec(dram) nexthop_info_t	*pNhInfo;		/* ptr to NH info in DRAM */

	pNhInfo = (__declspec(dram) nexthop_info_t	*)(NEXTHOP_TABLE_SDRAM_BASE	+ 
					(inNextHopIndex << NEXTHOP_INFO_SHF_VAL));
	
#endif

#ifdef	PROCESS_CONTROL_BLOCK
	sram_read(&portStatus, (volatile void __declspec(sram)*)&pCtrlBlk->portStatus, 1, sig_done, &portStatusSig);
#endif


#ifdef	IPV4_MIN_INTRINSIC
	nhInfo = *pNhInfo;
#else

#ifdef	NEXTHOP_INFO_SRAM

#ifdef	PROCESS_CONTROL_BLOCK
	sram_read((__declspec(sram_read_reg) void *)&nhInfo,
			(volatile __declspec(sram) void *)pNhInfo,4,sig_done,&sram_sig);
	wait_for_all(&sram_sig, &portStatusSig);
#else
	sram_read((__declspec(sram_read_reg) void *)&nhInfo,
			(volatile __declspec(sram) void *)pNhInfo,4,ctx_swap,&sram_sig);
#endif
#else

	dram_read((__declspec(dram_read_reg) void *)&nhInfo,
		(volatile __declspec(dram) void *)pNhInfo,2,sig_done,&dram_sig);
#ifdef	PROCESS_CONTROL_BLOCK
	wait_for_all(&dram_sig, &portStatusSig);
#else
	wait_for_all(&dram_sig);
#endif
#endif

#endif

	/* update the meta data */
	 
	DL_META_SET_OUTPUT_PORT(GET_NH_PORT(nhInfo));
	DL_META_SET_FABRIC_PORT(GET_NH_BLADE_ID(nhInfo));
	DL_META_SET_NEXTHOP_ID(GET_NH_ID(nhInfo));
	DL_META_SET_NEXTHOP_ID_TYPE(GET_NH_ID_TYPE(nhInfo));
	
	/* if NH ID type != 0, this is not IPV4 nexthop info. So return */
	if (DL_META_GET_NEXTHOP_ID_TYPE() != 0)
		return IPV4_SUCCESS;

	//LOG(dlMeta.nextHopId,0x0);
	/*
	the 'inNextHopIndex' passed could be zero. This entry may have
	a default route or may indicate that no route exists. If no route
	exists, the first longword (nexthop ID) of this entry has a value of '-1'.
	 */
	if (GET_NH_ID(nhInfo) == 0xffff)
	{
		/* no route */
		IPV4_INCR_NOROUTE(pIpv4FwderCounterBase);
		return IPV4_EXCP_NO_ROUTE;
	}

	/*
	 Now, we start processing the flags. These flags normally result in
	 exception packets. In general, these flags are cleared. If any of
	 the flags is set, we will process flags in non-critical code path.
	 The flags are in bits 16 thru 31.
	 */
	if (GET_NH_FLAGS(nhInfo) == 0)
	{

#ifdef	PROCESS_CONTROL_BLOCK
		/* check if fwding is enabled on the inport */
		if (!(portStatus & (1 << (inPort & 0x1f))))
			return IPV4_FAILURE;
#endif

		// check whether we can transmit the packet without fragmenting it.
		if (inIpTotalLen <= GET_NH_MTU(nhInfo))
		{
			// check if the packet has to be sent out from the same port from
			// which the packet has been received.
			UINT tmpPort;

			tmpPort = (GET_NH_BLADE_ID(nhInfo) << 24) | GET_NH_PORT(nhInfo);
			if (inPort != tmpPort)
				return IPV4_SUCCESS;
			else
				return IPV4_EXCP_REDIRECT;
		}
		else
			return IPV4_EXCP_FRAG_REQUIRED;
	}
	else
	{
		// processing flags. Non critical path.
		if (GET_NH_FLAGS(nhInfo) & IPV4_NH_FLAGS_LOCAL_BIT)
			return IPV4_EXCP_LOCAL_DELIVERY;
		else if (GET_NH_FLAGS(nhInfo) & IPV4_NH_FLAGS_DOWN_BIT)
				return IPV4_EXCP_DOWN;
		else if (GET_NH_FLAGS(nhInfo) & IPV4_NH_FLAGS_BROADCAST_BIT)
				return IPV4_EXCP_BROADCAST;
		else if (GET_NH_FLAGS(nhInfo) & IPV4_NH_FLAGS_MULTICAST_BIT)
				return IPV4_EXCP_MULTICAST;
			else
				return IPV4_FAILURE;
	}


} /* Ipv4FwderProcNexthopInfo */

/*****************************************************************************
 * Abstract:
 *    Decrement TTL, update checksum and place the packet in destination buffer.
 *
 * Parameters :
 *    pOutIp			-	OUT pointer to buffer containing IP header
 *    pInIp			-	IN pointer to buffer containing IP header
 *	  inOffset			-	IN byte offset of IP header relative to start
 *								of the buffer
 *	  inOutOffset		-	IN byte offset of IP header relative to start of the
 *								destination buffer.
 *	  inputMemType	- 	IN enum specifying the type of memory for input buffer
 *	  outputMemType -	IN enum specifying the type of memory for output buffer
 *
 * Returns:
 *    IPV4_SUCCESS		- If the packet can be sent out.
 *	  IPV4_FAILURE		- If the packet needs to be dropped
 *	  an exception code	- If the packet has to be sent to core
 *
 *****************************************************************************/
INLINE 	int					/* returns an integer */
	Ipv4FwderUpdateHeader(
	void 		*pOutIp,
	void 		*pInIp,
	uint32_t	inOffset,
	uint32_t	inOutOffset,
	mem_t		inputMemType,
	mem_t		outputMemType)
{

	if (DL_META_GET_NEXTHOP_ID_TYPE() != 0)
	{
		/* so this is not IPV4 nexthop. Simply copy from header from src 
		   to dst. SCR #4398 */

		return IPV4_SUCCESS;
	}

	/* Otherwise, perform IPV4 TTL check */

	// check if the TTL expired?
	if (ixp_ipv4_ttl_verify(pInIp,inOffset,inputMemType) != IPV4_SUCCESS)
	{
		/* bad ttl */
		IPV4_INCR_BADTTL(pIpv4FwderCounterBase);
		return IPV4_EXCP_BAD_TTL;
	}

	ixp_ipv4_modify(pOutIp,inOutOffset,outputMemType,0x0,pInIp,inOffset,inputMemType);

	return IPV4_SUCCESS;

}	/* Ipv4FwderUpdateHeader */



#endif	//__IPV4_FWDER_UTIL_C__

