// 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_chk.h - IPv6/IPv4 checksum module
// Raw Internet Checksum computation from RMBufChain.
//



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

#ifndef __IN_CHK_H__
#define __IN_CHK_H__

#include <nifmbuf.h>
#include "in_pkt.h"

/** Checksum calculation.

@publishedAll
@released
*/
class TChecksum {
public:
        // TChecksum can be initialized with an old inverted checksum
        TChecksum::TChecksum(TUint16 aSum = ~0) : iSum((TUint16)~aSum) {}
	inline void Init(TUint16 aSum = ~0);

	inline TUint32 Sum();
        inline TUint32 Sum32();
        inline void Fold();
        inline void Reverse();

        // Complex Add methods
        IMPORT_C void Add(RMBufChain &aPacket, TInt aOffset);
        IMPORT_C void Add(RMBufChain &aPacket, const RMBufPktInfo *aInfo, TInt aOffset);

        // Inline Add methods
        inline void Add(const TUint16 *aPtr, TInt aLength);
        inline void AddHi(TUint8 aByte);
        inline void AddLo(TUint8 aByte);
        inline void Add(TUint16 aHalfWord);
        inline void Add(TUint32 aWord);
        inline void AddH(TUint16 aHalfWord);
        inline void AddH(TUint32 aWord);

        // Static methods
        static inline TUint32 Fold(TUint32 aSum);
        static inline TUint16 ComplementedFold(TUint32 aSum);
        IMPORT_C static TUint32 Calculate(const TUint16 *aPtr, TInt aLength);

private:
	TUint32	iSum;   // 32-bit sum in network byte order
};


/**
// TInet6Checksum Template.
//
//		This template class provides utilitlies to compute and check
//		IPv6 Upper Layer Checksums
//		These are not merged with the TInet6Packet class, because that
//		class is intended (and is used) for all headers, not just upper
//		layers.
//
//	The template parameter (Header class) must
//		- have Checksum() method
//		- have SetChecksum() method
//		- the checkum in header must be aligned to 16 bit word
//
// @publishedAll
// @released
*/
template <class T>
class TInet6Checksum : public TInet6Packet<T>
	{
public:
	TInet6Checksum() {}
	TInet6Checksum(RMBufChain &aPacket) : TInet6Packet<T>(aPacket) {}
	TInet6Checksum(RMBufChain &aPacket, TInt aOffset) : TInet6Packet<T>(aPacket, aOffset) {}
	void ComputeChecksum(RMBufChain &aPacket, const RMBufPktInfo *aInfo, TInt aOffset = 0);
	TBool VerifyChecksum(RMBufChain &aPacket, const RMBufPktInfo *aInfo, TInt aOffset = 0);
	void ComputeChecksum();
	TBool VerifyChecksum();
	};

//
// Inline methods for TChecksum
//

inline void TChecksum::Init(TUint16 aSum)
{
        iSum = ~aSum & 0xffff;
}

inline void TChecksum::Add(const TUint16 *aPtr, TInt aLength)
{
	iSum += Calculate(aPtr, aLength);
}

inline void TChecksum::AddHi(TUint8 aByte)
{
        iSum += (aByte << 8);
}

inline void TChecksum::AddLo(TUint8 aByte)
{
        iSum += aByte;
}

// Add halfword in network byte order
inline void TChecksum::Add(TUint16 aHalfWord)
{
        iSum += aHalfWord;
}

// Add word in network byte order
inline void TChecksum::Add(TUint32 aWord)
{
	iSum += aWord >> 16;
        iSum += aWord & 0xffff;
}

// Add halfword in host byte order
inline void TChecksum::AddH(TUint16 aHalfWord)
{
		__DEBUG_ONLY (const TInt one = 1;)
		ASSERT(*(TUint8*)&one); //check that we are little-endian

		iSum += ((aHalfWord << 8) | (aHalfWord >> 8)) & 0xffff;
}

// Add word in host byte order
inline void TChecksum::AddH(TUint32 aWord)
{
		__DEBUG_ONLY (const TInt one = 1;)
		ASSERT(*(TUint8*)&one); //check that we are little-endian

        iSum += (aWord >> 24);          // add octet  3
        iSum += (aWord >> 8) & 0xffff,  // add octets 2-1
        iSum += (aWord << 8) & 0xff00;  // add octet  0
}

// Fold 32-bit sum into 16 bits
inline void TChecksum::Fold()
{
        iSum = Fold(iSum);
}

// Return 16-bit inverted checksum
inline TUint32 TChecksum::Sum()
{
        return Fold(iSum) ^ 0xffff;
}

// Return 32-bit intermediate sum
inline TUint32 TChecksum::Sum32()
{
        return iSum;
}

// Reverse checksum direction. Can be used to back out partial sums.
inline void TChecksum::Reverse()
{
        iSum = Sum();
}

inline TUint32 TChecksum::Fold(TUint32 aSum)
{
	aSum = (aSum >> 16) + (aSum & 0xffff);  // Max: 0x0001fffe
	aSum += (aSum >> 16);                   // Max: 0x0001ffff
        return aSum & 0xffff;                   // Truncate to 16 bits
}

inline TUint16 TChecksum::ComplementedFold(TUint32 aSum)
{
        return (TUint16)~Fold(aSum);
}

template <class T>
void TInet6Checksum<T>::ComputeChecksum(RMBufChain &aPacket, const RMBufPktInfo *aInfo, TInt aOffset)
	{
        TChecksum sum;
	this->iHdr->SetChecksum(0);
        sum.Add(aPacket, aInfo, aOffset);
	this->iHdr->SetChecksum(sum.Sum());
	}

template <class T>
TBool TInet6Checksum<T>::VerifyChecksum(RMBufChain &aPacket, const RMBufPktInfo *aInfo, TInt aOffset)
	{
        TChecksum sum;
        sum.Add(aPacket, aInfo, aOffset);
        return !sum.Sum();
	}
//
// The following are mainly for the IPv4 Header checksum
//
template <class T>
void TInet6Checksum<T>::ComputeChecksum()
	{
        TChecksum sum;
	this->iHdr->SetChecksum(0);
        sum.Add((TUint16 *)(this->iHdr), this->iHdr->HeaderLength());
	this->iHdr->SetChecksum(sum.Sum());
	}

template <class T>
TBool TInet6Checksum<T>::VerifyChecksum()
	{
        TChecksum sum;
        sum.Add((TUint16*)(this->iHdr), this->iHdr->HeaderLength());
        return !sum.Sum();
	}

#endif
