// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Symbian Foundation License v1.0" to Symbian Foundation members and "Symbian Foundation End User License Agreement v1.0" to non-members
// which accompanies this distribution, and is available
// at the URL "http://www.symbianfoundation.org/legal/licencesv10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// in_pkt.h - packet handling routines
// Generic packet handling utility for mapping packet handling to the RMBufChain.
//



/**
 @file in_pkt.h
 @publishedAll
 @released
*/

#ifndef __IN_PKT_H__
#define __IN_PKT_H__

#include <nifmbuf.h>
#include "ip6_hdr.h"	// ..should eventually be <inet/ip6_hdr.h>? -- msa
#include "ip4_hdr.h"

#define TPACKETHEAD_FRAGMENT	1	///< Enable iFragment in TPacketHead

/**
 TScopeType is only provided so that "magic" constants can be
 avoided in the source code. However, the max value cannot be changed
 to anything from 0xF. The scope type is assumed to be 4 bits long
 in many occasions.

 The value of the scope type is directly bound the the IPv6 Scope
 level - 1. This can be done, as IPv6 Scope level 0 is not legal
 (or usable) in any context within the stack.
 This allows our non-standard network scope (= 0x10) to
 be coded internally in 4 bits (as 0xF).

 @publishedAll
 @released
 @since v7.0s
*/
enum TScopeType
	{
	EScopeType_IF	= 0x0,	///< (= #KIp6AddrScopeNodeLocal - 1), id is interface index
	EScopeType_IAP	= 0x1,	///< (= #KIp6AddrScopeLinkLocal - 1). id is IAP number
	EScopeType_GLOBAL = 0xD,///< (= #KIp6AddrScopeGlobal - 1). id is global scope id
	//
	// no symbols defined for types 2..14 (they are also valid)
	//
	EScopeType_NET	= 0xF	///< (= #KIp6AddrScopeNetwork - 1), id is network number (must be the last entry)
	};

//
//	TIpHeader
//	*********
class TIpHeader
	/**
	A simple help class that uses a union to merge handling of either an IPv4 or 
	an IPv6 header. 
	@since v7.0
	@publishedAll
	@released
	*/
	{
public:
	/**
	Gets the minimum header length.

	IPv6 header is longer than minimum IPv4 header, thus
	returned value is for IPv4. This function only defined
	because it is required when this class is used as template
	parameter in TInet6Packet.
	
	@return Minimum IPv4 header length
	*/
	inline static TInt MinHeaderLength() {return TInet6HeaderIP4::MinHeaderLength(); }
	/**
	Gets the maximum header length.

	IPv6 header always shorter than maximum IPv4 header, thus
	returned value is for IPv4. This function is only defined
	because "header mapping" classes are expected to have it.
	
	@return Maximum IPv4 header length
	*/
	inline static TInt MaxHeaderLength() {return TInet6HeaderIP4::MaxHeaderLength(); }

	union
		{
		TInet6HeaderIP4 ip4;
		TInet6HeaderIP ip6;
		};
	};


//	RMBufPacketPeek
//	***************
class RMBufPacketPeek : public RMBufChain
	/**
	Extends RMBufChain to add functions to read packet data as a descriptor 
	and as an IP header.

	The RMBufChain is assumed to contain the raw packet, without
	the info block prepended (e.g. if this class is used for RMBufPacketBase
	derived handle, it must be in "unpacked" state).
	
	@since v7.0
	@publishedAll
	@released
	*/
	{
public:
	IMPORT_C TPtr8 Access(TInt aSize, TUint aOffset = 0);
	IMPORT_C TIpHeader *GetIpHeader();
	};

//	TPacketHead
//	***********
class TPacketHead
	/**
	Storage for some precomputed information for an outbound packet flow.

	The outbound TPacketHead is part of the flow context (CFlowContext).

	The CFlowContext::Connect initializes the content from the parameters
	of the flow (TFlowInfo) and runs the connection process.. The connection
	process (MIp6Hook::OpenL and MFlowHook::ReadyL phases) completes the
	information. After this, as long as the flow is connected, the content
	is mostly frozen and <b>must not be modified by anyone</b>.
 
    When there is a need to change any flow information, the changes must
	be done to the flow parameters (and not to TPacketHead). The change of
	flow parameters also sets the CFlowContext::iChanged flag, and this
	eventually causes a new CFlowContext::Connect, which re-initializes
	the TPacketHead with the new information.

	For each field in the TPacketHead, the hook writer must follow the
	basic rule (only for fields that it intends to change):

	- if some field is changed in MIp6Hook::OpenL, then the previous
	value should be restored in the MFlowHook::ReadyL.
	- an exeception: the hook must omit the restore, if the
	previous value was unspecified value (for example, the source
	address).
	- the content of #iPacket (and #iOffset) are special: they cannot
	be modified in the MIp6Hook::OpenL phase. A hook can
	modify them only in the MFlowHook::ReadyL phase. And, if the hook
	is adding an IP header for tunneling, it must save the current content
	of these fields in the ReadyL function, and then clear out the fields
	(it must make the iPacket empty and zero iOffset). The hook must add
	the saved iPacket content below the added tunnel header in
	MFlowHook::ApplyL .

	@since v7.0
	@publishedAll
	@released
	*/
	{
public:
	IMPORT_C TBool ExtHdrGet(TInt aType, TInt& aOfs, TInt& aLen);
	IMPORT_C TBool ExtHdrGetOrPrependL(TInt aType, TInt& aOfs, TInt& aLen);
	IMPORT_C TBool ExtHdrGetOrAppendL(TInt aType, TInt& aOfs, TInt& aLen);
	IMPORT_C void AddDestinationOptionL(const TPtrC8& aOption, TUint8 aAlign=0, TUint8 aModulo=4);
	IMPORT_C void AddDestinationOptionL(const TUint8* aOption, TUint8 aLen, TUint8 aAlign=0, TUint8 aModulo=4);

public:
	/**
	"Virtual" IP header. The IPv6 header stucture is used, but the same
	format is <b>also</b> used for the IPv4 destinations (Version() == 4,
	even though the header format is still IPv6!)
	
	This header is initialized in the beginning of the OpenL phase
	as follows:
	@li	Version = 0
	@li	Traffic Class, copied from the flow iOptions.iTrafficClass
	@li	Flow Label = 0
	@li	Payload Length = 0 (dummy field, not used)
	@li	Next Header, copied from the flow iProtocol
	@li	Hop Limit, copied from the flow iOptions.iHopLimit
	@li	Src Address, copied from the flow Local Address (usually unspecified)
	@li	Dst Address, copied from the flow Remote Address
	
	At beginning of the ReadyL phase (= at end of OpenL), the destination
	address (and iDstId) are used to find a route on the interface. Depending
	on whether this address is IPv4 (mapped) or IPv6, the Version field is set
	accordingly to either 4 or 6.

	After succesfull completion of the ReadyL, this used for *each* packet
	which needs an IP header to be generated on send. The Version() determines
	whether IPv4 or IPv6 frame is to be generated (this is the initial
	header in the packet, *before* running outbound ApplyL hooks):
	
	@verbatim
	                   IPv6            IPv4
	   Version         == 6            ==4
	   Traffic Class   used as is      used as TOS
	   Flow Label      used as is      ignored
	   Payload Length  ignored         ignored
	   Next Header     used as is      used as Protocol
	   Hop Limit       used as is      used as TTL
	   Src Address     used as is      used as IPv4 mapped
	   Dst Address     used as is      used as IPv4 mapped
	@endverbatim
	*/
	TInet6HeaderIP ip6;
	/**
	Contains the scope id associated with the destination address
	which is stored in #ip6 Dst Address. This id and address must
	always be considered as a unit. Logically, any change changes
	both values.

	iDstId is initialized from the flow context TFlowInfo::iRemote.Scope() at
	beginning of the flow connect phase. If application does not define
	this scope id, then the system will attempt to choose a default value
	at beginning of the connect phase. If the default cannot be determined,
	the flow is put into pending state (and no connect happens).

	@par MIp6Hook::OpenL
	On entry to the OpenL, the iDstId is always non-zero and destination
	address is specified. If a hook changes the destination address in
	OpenL method, it must provide the correct id value
	which goes with the new destination. If it cannot do this, it
	must either abort the connect by leaving with an error state, or it
	can leave with PENDING (> 0) status to signal there is no route
	for the new destination.
	If the stack cannot find suitable interface for the destination, then
	it aborts the connect phase, and the flow is placed into holding state.

	@note
		Only a tunneling hook can safely change the destination
		address (a use of routing header can also be a kind of
		tunneling).
	
	@par MFlowHook::ReadyL
	If the hook changed the destination address (or id) in the OpenL,
	the ReadyL must restore the original values back.

	*/
	TUint32 iDstId;
	/**
	Contains the scope id associated with the source address
	which is stored in #ip6 Src address. This is defined when the source
	address is defined, and otherwise undefined.

	iSrcId is initialized from TFlowInfo::iLocal.Scope() at beginning of the
	flow connect phase. If application defines the source address,
	but does not specify this scope id, then the system chooses
	the id based on the interface defined by the source address.
	If scope and address are both specified, they must match the
	selected interface.

	@par MIp6Hook::OpenL
	On entry to the OpenL, the iSrcId (and source address) may be
	undefined (#iSourceSet = 0). If defined (iSourceSet = 1), then
	both address and iSrcId are defined (iSrcId != 0). A hook may
	force a reselection of the source just by zeroing the
	iSourceSet.

	@par MFlowHook::ReadyL
	If the hook changed the source address (or id) in the OpenL,
	the ReadyL must restore the original values back, but only
	if the original value was defined (#iSourceSet = 1 in OpenL).
	*/
	TUint32 iSrcId;
	/**
	The source address has been set.

	This bit indicates whether the value stored in #ip6 src field
	and #iSrcId is to be used as a source address as is.

	Initialized from TFlowInfo::iLocalSet, which tells whether user
	specified tbe source address or not (e.g used RSocket Bind method).
	The stack checks the value after each MIp6Hook::OpenL call, and
	if the flag is set, the source in ip6 is used as is. If the flag
	is zero, then the stack performs the normal source address selection
	based on the current destination address (#iSrcId and destination
	address).

	@par MIp6Hook::OpenL
	On entry, this flag is always set and source address is defined.
	A hook may clear this flag, if it wants the
	stack choose the source address based on current destination.
	The clearing operation is normally needed only by a tunneling
	hook.

	@note
		If the hook specifies the source address, it must be either
		a valid source address for the interface or unspecified
		address.

	@par MFlowHook::ReadyL
	Upon entry to the ReadyL, the source address is always fully
	known (the hook can assume that #iSrcId and the #ip6 source
	addresses are valid).
	If the source address was set before the OpenL, then this
	must restore the original value (along with the #iSrcId
	and source address).
	*/
	TUint iSourceSet:1;
#ifdef TPACKETHEAD_FRAGMENT
	/**
	The fragment processing alredy done.
	
	This bit is meaningful only in OpenL phase. If already set,
	then some ealier hook has requested that the packet must
	be fragmented to fit the mtu.
	
	A tunneling hook can set this bit in OpenL, if it needs
	the fragmenting to happen before the ApplyL is called (e.g.
	the fragments are tunneled instead of fragmenting the
	tunneling).
	
	This bit can only be set or left as is. It cannot be cleared
	once set.
	*/
	TUint iFragment:1;
#endif
	/**
	Selector info, the upper layer protocol.

	iProtocol has the same value as ip6.NextHeader() when iPacket is empty,
	and otherwise it is the same as NextHeader() of the last extension
	header in the iPacket.

	The values of the other selector fields: #iIcmpType, #iIcmpCode
	#iSrcPort and #iDstPort depend on iProtocol. Whenever iProtocol
	is changed, the other fields must be updated accordingly.

	@par MIp6Hook::OpenL
	Because iPacket cannot be modified during the OpenL phase, the
	content of this field and the Next Header (protocol) field in
	the #ip6 pseudoheader must always be the same. This field should
	be considered as <b>read-only</b>, unless the hook intends to
	apply IP-in-IP tunneling, in which case the hook <b>must</b>
	change the value to the appropriate tunneling protocol
	(#KProtocolInet6Ipip or #KProtocolInetIpip).

    @par MFlowHook::ReadyL
	Only a tunneling hook needs to restore the value here to match
	the original upper layer protocol. See #iPacket for
	more detailed information.
	*/
	TUint8 iProtocol;
	/**
	Selector field whose value depends on #iProtocol. 
 
	If this field does not have meaning with the protocol,
	the field content should be set to ZERO.
	*/
	TUint8 iIcmpType;
	/**
	Selector field whose value depends on #iProtocol. 
 
	If this field does not have meaning with the protocol,
	the field content should be set to ZERO.
	*/
	TUint8 iIcmpCode;
	/**
	Selector field whose value depends on #iProtocol. 
 
	If this field does not have meaning with the protocol,
	the field content should be set to ZERO.
	*/
	TUint16 iSrcPort;
	/**
	Selector field whose value depends on #iProtocol. 
 
	If this field does not have meaning with the protocol,
	the field content should be set to ZERO.
	*/
	TUint16 iDstPort;
	/**
	The amount of pre-computed IPv6 extension headers in iPacket which
	are copied to the beginning of each outgoing packet

	If iOffset > 0, then #iPacket includes that much of extension
	headers that are copied in front of each packet.
	*/
	TInt iOffset;
	/**
	Pre-computed extension headers for all packets in this flow.
	
	These can only be added in the ReadyL phase. If any of the
	ReadyL's adds extension headers into this, it must take care
	of maintaining the correct Next Header in the virtual IP header
	(and the original upper layer protocol must be placed in the
	next header of the last extension header added.
	
	Stack copies the content of this to each outgoing packet, just below
	the IP header, before running the ApplyL functions of the outbound
	flow hooks.

	@par MIp6Hook::OpenL
	The iPacket <b>must not</b> be modified during the OpenL phase.

	@par MFlowHook::ReadyL
	A non-tunneling hook may add extension headers into the current
	iPacket. A tunneling hook has more complex requirements:
	it must save the current iPacket and #iOffset and initialize
	iOffset = 0, and iPacket as empty.

    @par MFlowHook::ApplyL
	When a tunneling hook adds the tunneling IP header, it
	must also copy the saved iPacket below the added IP header.
	*/
	RMBufPacketPeek iPacket;
	/**
	The received packet which caused an ICMP error reply to be sent.

	This is only used for ICMP error repply flows, and should be
	ignored by others -- mainly for IPSEC hook. The packet, if
	present, is in unpacked state.
	*/
	RMBufPacketBase iIcmp;
	/**
	The current destination interface.

 	This is ONLY used during connect/OpenL phase.

	The value is maintained by the stack, and is intended as
	read-only information for the hooks that have a use for
	it (for example, IPSEC implementing VPN specific policies).

	A hook must not modify this value (the stack will recompute
	the value after each OpenL, based on the possibly changed
	address parameters in the TPacketHead)

	@par MIp6Hook::OpenL
	<b>read-only</b>
	@par MFlowHook::ReadyL
	<b>read-only</b>
	*/
 	TUint32 iInterfaceIndex;
	};

class TInet6PacketBase
	/**
	* Thin base class for the TInet6Packet.
	*/
	{
public:
	enum TAlign
		{
		EAlign1 = 0,	///< Align to byte (no align requirement)
		EAlign2 = 1,	///< Align to 2 byte unit (even address)
		EAlign4 = 3,	///< Align to 4 byte unit
		EAlign8 = 7		///< Align to 8 byte unit
		};

	/**
	Constructor.

	@param aAlign	The align requirement.
	*/
	TInet6PacketBase(TAlign aAlign) : iLength(0), iAlign(aAlign) {}

	/**
	Length of the mapped region.

	The real mapped length as computed by the Access function.
	If access returned non-NULL, the following is always TRUE:

	@li	aMin <= iLength
	*/
	TInt iLength;

	IMPORT_C TUint8 *Access(RMBufChain &aPacket, TInt aOffset, TInt aSize, TInt aMin);

	inline void SetAlign(TAlign aAlign)
		/**
		* Changes the align requirement.
		*
		* @param aAlign The new align requirement.
		*/
		{
		iAlign = aAlign;
		}
protected:
	/**
	The align requirement.
	*/
	TAlign iAlign;
	};

// TInet6Packet template
// *********************
template <class T>
class TInet6Packet : public TInet6PacketBase
	/**
	Encapsulates an IPv6 packet header as a section of an RMBufChain.

	The T template parameter should represent a packet header type. It should 
	support static functions MaxHeaderLength() and MinHeaderLength() that return 
	TInt values for maximum and minimum header lengths respectively.

	@publishedAll
	@released
	@since v7.0
	*/
	{
public:
	TInet6Packet(TAlign aAlign = EAlign4) : TInet6PacketBase(aAlign), iHdr(NULL)
		/**
		Default constructor.

		Construct an empty mapping. To be usable, the Set() function
		must be used.
		*/
		{}
	TInet6Packet(RMBufChain &aPacket) : TInet6PacketBase(EAlign4)
		/**
		Constructor specifying a RMBufChain object.

		Verify and arrange it so that a class T can be mapped
		to a contiguous octets from the beginning of the RMBufChain
		content, and set iHdr to point this area.

		If this is not possible, iHdr is initialized to NULL.

		@param	aPacket
			Packet containing the header T at offset = 0
		*/
		{
		iHdr = (T *)Access(aPacket, 0, T::MaxHeaderLength(), T::MinHeaderLength());
		}

	TInet6Packet(RMBufChain &aPacket, TInt aOffset, TAlign aAlign = EAlign4) : TInet6PacketBase(aAlign)
		/**
		Constructor specifying a RMBufChain object and an offset.

		Verify and arrange it so that a class T can be mapped
		to a contiguous octets starting at specified offset of
		the RMBufChain content, and set iHdr to point this area.

		If this is not possible, iHdr is initialized to NULL.

		@param aPacket
			Packet containing the header T at aOffset
		@param aOffset
			Offset of the header to be mapped.
		@param aAlign
			The alignement requirement.
		*/
		{
		iHdr = (T *)Access(aPacket, aOffset, T::MaxHeaderLength(), T::MinHeaderLength());
		}

	void Set(RMBufChain &aPacket, TInt aOffset, TInt aSize)
		/**
		Sets the packet header from a specified RMBufChain object.

		Verify and arrange it so that a aSize octets can be mapped
		to a contiguous octets starting at specified offset of
		the RMBufChain content, and set iHdr to point this area.

		If this is not possible, iHdr is initialized to NULL.

		Note that this differs from the contructors: the required
		size is a parameter, and not determined by the T::MinHeaderLength().
		However, the "T* iHdr" is set to point the start of the requested
		area. It's a responsibility of the user of this method to know
		whether using this pointer is safe with the specified size parameter.

		@param	aPacket
			Packet containing the header T at aOffset
		@param	aOffset
			Offset (position) of the header to be mapped
		@param	aSize
			Length of required contiguous memory
		*/
		{
		iHdr = (T *)Access(aPacket, aOffset, aSize, aSize);
		}

	inline T& operator()()
		{
		return *iHdr;
		}
	/**
	The pointer to the mapped region (if non-NULL). If NULL,
	then there is no mapping, and iLength == 0.
	*/
	T *iHdr;
	};


//	TPacketPoker
//	************
class TPacketPoker
	/**
	Provides a utility for linear scanning of a chain of RMBuf objects (an RMBufChain).

	An object of this type maintains a current point in the RMBufChain. This point 
	can only move forward, and a leave occurs if the point advances beyond the 
	end of the chain.

	Any pointers and aligns arranged before the current point, remain valid: for 
	example, you can save a reference and advance the pointer, and the reference 
	remains usable.
 
	If instead you need to go to a single specified offset, then use
	RMBufChain::Goto() or RMBufPacketPeek::Access().

	@post
	A Generic implementation assert: 
	after construct, iTail == 0 iff iCurrent == 0 (all scanned), or
	in other words: as long as there are bytes after current point,
	iTail will be non-zero (and More() returns ETrue).
	All methods maintain this invariant or leave, if impossible.

	Some other utility methods, not directly related to scanning, are also included. 
	@since v7.0
	@publishedAll
	@released
	*/
	{
public:
	IMPORT_C TPacketPoker(RMBufChain &aChain);

	inline void SkipL(TInt aSize)
		/**
		Moves the current point forward a specified number of bytes.

		@param aSize Number of bytes to move forward
		@leave KErrEof
			if the request cannot be satisfied.
		*/
		{ if (aSize < iTail) { iTail -= aSize; iOffset += aSize; } else OverL(aSize); }

	inline TUint8 *Ptr() const
		/**
		Raw pointer to the current point (can be invalid, if iTail = 0).
	
		@note Internal "unsafe" method
		*/
		{return iCurrent->Ptr() + iOffset; }

	inline TUint8 *ReferenceL(TInt aSize = 1)
		/**
		Gets a pointer to the current point, such that
		at least the specified minimum number of bytes can be read.

		@param aSize
			Specified minimum number of bytes to be read through
			the returned pointer.
		@return Raw data pointer
		@leave KErrEof
			if the request cannot be satisfied.
		*/
		{ if (iTail >= aSize) return Ptr(); else return AdjustL(aSize); }

	inline TUint8 *ReferenceAndSkipL(TInt aSize)
		/**
		Gets a pointer to the current point, such that at least the
		specified minimum number of bytes can be read,
		and moves the point the specified number of bytes forward.

		@param aSize
			Specified minimum number of bytes to be read through the returned 
			pointer, and the number of bytes to move forward
		@return
			Raw data pointer
		@leave KErrEof
			if the request cannot be satisfied.
		*/
		{ TUint8 *x = ReferenceL(aSize); SkipL(aSize); return x; }

	inline TInt Remainder() const
		/**
		Gets the length of the contiguous space after the current point.	

		@return Length after the current point
		*/
		{ return iTail; }

	inline TBool AtBegin() const
		/**
		Tests whether the current point is at the beginning of an RMBuf.

		@return ETrue if current point is at the beginning
		*/
		{ return iOffset == 0; }

	inline TBool More() const
		/**
		Tests whether there is more data to scan.

		@return ETrue if there is more data to scan
		*/
		{ return iTail > 0; }

	IMPORT_C static TBool IsExtensionHeader(TInt aProtocolId);
private:
	IMPORT_C void OverL(TInt aSize);
	IMPORT_C TUint8 *AdjustL(TInt aSize);
	/** The RMBuf of the current point. */
	RMBuf *iCurrent;
	/** The offset of the current point in the RMBuf. */
	TInt iOffset;
	/** Remaining bytes starting from the current point in the RMBuf. */
	TInt iTail;
	};

#endif
