linklayerprotocols/pppnif/SVJCOMP/VJDECOMP.CPP
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linklayerprotocols/pppnif/SVJCOMP/VJDECOMP.CPP	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,708 @@
+// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include <networking/in_std.h>
+#include <in_sock.h>
+#include "VJLOG.H"
+#include "VJ.H"
+
+CVJDeCompressor::CVJDeCompressor()
+	{
+	}
+
+void CVJDeCompressor::ConstructL( CVJCompFactory* aFactory, TUint aMaxSlot)
+/**
+Construct the CVJDeCompressor object.
+
+@param aFactory Factory that created this object
+@param aMaxSlot Value of the highest VJ connection number to be received
+*/
+	{
+	//
+	// Note that aMaxSlot is the index of the maximum slot that will be accessed.
+	//
+	__ASSERT_DEBUG(aMaxSlot <= KMaxVjSlot, User::Panic(_L("VJ Panic"), 0));
+	iNumVJSlots = aMaxSlot;
+	iRxStates = new (ELeave) TVJCompHdr[aMaxSlot+1];
+	iFactory = aFactory;
+	iFactory->Open();
+	}
+
+CVJDeCompressor::~CVJDeCompressor()
+	{
+	delete []iRxStates;
+	if (iFactory)
+	    {
+		iFactory->Close();
+		}
+	}
+
+void CVJDeCompressor::CRCError()
+/**
+Sets the discard flag after receiving a bad frame so that future
+decompressed packets don't get out of sync.
+*/
+	{
+	LOG(_LIT(string1,"CRC Error");)
+	LOG(Log::Write(string1);)
+	SetFlag(KVJDiscard);
+	}
+
+void CVJDeCompressor::CopyRecvHeader(const TUint aConnection, ThdrIP* aHeader)
+/**
+Caches a TCP/IP Header to be used as a reference to
+reconstruct future compressed headers.
+
+@param aConnection Valid VJ connection number
+@param aHeader TCP/IP header
+*/
+	{
+	__ASSERT_DEBUG(aConnection <= iNumVJSlots, User::Panic(_L("VJ Panic"), 0));
+	iRxStates[aConnection].StoreTCPIPHeader(aHeader);
+	}
+
+void CVJDeCompressor::GetStoredRxHeader(const TUint aConnection, ThdrIP* aIPHeader, ThdrTCP* aTCPHeader)
+/**
+Retrieves a packet header from the VJ connection cache.
+
+@param aConnection Valid VJ connection number to retrieve
+@param aIPHeader IP packet header
+@param aTCPHeader TCP header
+*/
+	{
+	__ASSERT_DEBUG(aConnection <= iNumVJSlots, User::Panic(_L("VJ Panic"), 0));
+	iRxStates[aConnection].RetrieveTCPIPHeader(aIPHeader, aTCPHeader);
+	}
+
+
+TBool CVJDeCompressor::CheckStoredRxHeader(const TUint aConnection) const
+/**
+Checks if the given VJ connection number has previously been filled in.
+
+@param aConnection Valid VJ connection number to check
+
+@return ETrue if CopyRecvHeader() has previously been called
+        on this connection number
+*/
+	{
+	__ASSERT_DEBUG(aConnection <= iNumVJSlots, User::Panic(_L("VJ Panic"), 0));
+	return iRxStates[aConnection].IsValid();
+	}
+
+
+TBool CVJDeCompressor::DecompVJComp(RMBufChain& aPacket)
+/**
+Decompresses a VJ compressed packet in place.
+
+@param aPacket MBuf chain containing packet
+
+@return ETrue if the frame was successfully decompressed
+
+@see DecompVJUncomp
+*/
+	{
+	TBool		RetCode = EFalse;
+	TUint8		Connection;
+	TUint16		CurrentFrameLength;
+
+	TUint8* const InitialHeaderPtr = GetVJPtr(aPacket, &CurrentFrameLength);
+	TUint8 Changes	= *InitialHeaderPtr;
+	TUint Offset = 1;
+
+	if (Changes & KVjCompMaskConn)
+		{
+		Connection = *(InitialHeaderPtr+Offset);
+		Offset++;
+		if (Connection <= iNumVJSlots && CheckStoredRxHeader(Connection))
+			{
+			LOG(_LIT(string1,"Clear error comp");)
+			LOG(Log::Write(string1);)
+			ClearFlag(KVJDiscard);
+			iLastRxConn = Connection;
+			}
+		else
+			{
+			//
+			//  The received connection number was invalid.
+			//	Ignore all further Compressed frames until an Uncompressed frame
+			//	or a frame containing an explicit connection number is received
+			//
+			LOG(_LIT(string1,"Invalid connection");)
+			LOG(Log::Write(string1);)
+			SetFlag(KVJDiscard);
+			return RetCode;
+			}
+		}
+	else
+		{
+		if(TestFlag(KVJDiscard))
+			{
+			return RetCode;
+			}
+		Connection = (TUint8)iLastRxConn;
+		}
+
+	TRAPD(ret, DecompressFrameL(aPacket, Connection, Changes, InitialHeaderPtr, Offset, CurrentFrameLength));
+	if (ret == KErrNone)
+		{
+		//
+		//	Under all other circumstances the frame is thrown away
+		//
+		RetCode = ETrue;
+		}
+		
+	return RetCode;
+	}
+
+TBool CVJDeCompressor::DecompVJUncomp(RMBufChain& aPacket)
+/**
+Handles a VJ uncompressed packet.
+Stores the packet header in the appropriate VJ connection slot.
+
+@param aPacket MBuf chain containing packet
+
+@return ETrue if the frame was successfully processed
+
+@see DecompVJComp
+*/
+	{
+	TBool	RetCode = EFalse;
+
+	//
+	//	When we get here, the first packet is some information thing,
+	//	we want the IPHeader this was the easiest way I could see to do it
+	//	
+	//
+	ThdrIP*	IPHeader = GetIPHeader(aPacket);
+	
+	if (IPHeader == NULL)
+	    {
+		//
+		//  The packet was too short.
+		//	Ignore all further Compressed frames until an Uncompressed frame
+		//	or a frame containing an explicit connection number is received
+		//
+		LOG(_LIT(string1,"Short VJ frame received");)
+		LOG(Log::Write(string1);)
+		SetFlag(KVJDiscard);
+		
+		RetCode = EFalse;
+		}
+    else
+        {
+    	TUint ConnectionNumber = IPHeader->NetGetProtocol();
+
+    	if (ConnectionNumber <= iNumVJSlots)
+    		{
+    		//
+    		//	Set the real protocol header to TCP
+    		//
+    		IPHeader->NetSetProtocol(KProtocolInetTcp);
+    		
+    		iLastRxConn = ConnectionNumber;
+    		LOG(_LIT(string1,"Clear error uncomp");)
+    		LOG(Log::Write(string1);)
+    		ClearFlag(KVJDiscard);
+
+    		//
+    		//	Copy the header into one of our connection things
+    		//	and zero the checksum
+    		//
+    		CopyRecvHeader(ConnectionNumber, IPHeader);
+
+    		//
+    		//	Receive this frame properly
+    		//
+    		RetCode = ETrue;
+    		}
+    	else
+    		{
+    		//
+			//  The received connection number was invalid.
+			//	Ignore all further Compressed frames until an Uncompressed frame
+			//	or a frame containing an explicit connection number is received
+    		//
+    		LOG(_LIT(string1,"Invalid VJ connection received");)
+    		LOG(Log::Write(string1);)
+    		SetFlag(KVJDiscard);
+    		
+    		RetCode = EFalse;
+    		}
+		}
+
+		
+	return RetCode;
+
+	}
+
+void CVJDeCompressor::DecompressFrameL(RMBufChain& aPacket, 
+								TUint8 aConnection, 
+								TUint8 aChanges, 
+								TUint8* const aInitialHeaderPtr,
+								TUint	aOffset,
+								TUint16 aCurrentFrameLength)
+/**
+Uncompresses a VJ compressed TCP/IP header.
+
+@param aPacket MBuf chain containing packet to be updated
+@param aConnection VJ connection number
+@param aChanges The VJ change mask
+@param aInitialHeaderPtr Beginning of the compressed VJ header
+@param aOffset Offset from start of packet to TCP checksum field
+@param aCurrentFrameLength Length of the compressed packet
+
+@leave KErrCorrupt if aPacket is corrupt
+*/							   
+{
+	TUint8*	VJCompHeader = aInitialHeaderPtr + aOffset;
+
+	TUint16 Checksum = BigEndian::Get16(VJCompHeader);
+	VJCompHeader+=2;
+
+	//
+	//	Use the aConnection number to retrieve the TCP Header and IP Header
+	//
+	ThdrIP		IPHeader;
+	ThdrTCP		TCPHeader;
+	GetStoredRxHeader(aConnection, &IPHeader, &TCPHeader);
+
+	TUint16 IPHeaderLength	= (TUint16)IPHeader.NetGetHdrLen();
+	TUint16 TCPHeaderLength = (TUint16)TCPHeader.NetGetHdrLen();
+	TUint16 PreviousDataLength = (TUint16)(IPHeader.NetGetLength() - IPHeaderLength - TCPHeaderLength);
+	
+	//
+	//	Set the actual checksum
+	//
+	TCPHeader.VJSetChecksum(Checksum);
+
+	//
+	//	Set Push if appropriate, otherwise clear it.
+	//
+	DecompPushFlag(aChanges, &TCPHeader);
+
+	//
+	//	Handle the Sequence, Window, Ack, Urgent compressed elements
+	//
+	DecompSWAU(aChanges, &VJCompHeader, &TCPHeader, PreviousDataLength);
+
+	//
+	//	Handle the compressed IP Identification field
+	//
+	DecompIPId(aChanges, &VJCompHeader, &IPHeader);
+
+	TUint16 OriginalHeaderLength = (TUint16)(VJCompHeader - aInitialHeaderPtr);
+
+	//
+	// Check if the received frame is shorter than the VJ header, and discard it if
+	// so.  This condition is detected by VJCompHeader being incremented beyond the end of
+	// the packet during processing.
+	//
+	if (OriginalHeaderLength > aCurrentFrameLength)
+		{
+		LOG(_LIT(string1,"Short frame discarded");)
+		LOG(Log::Write(string1);)
+		User::Leave(KErrCorrupt);
+		}
+
+	TUint16 CurrentDataLength = (TUint16)(aCurrentFrameLength - OriginalHeaderLength);
+
+	IPHeaderLength	= (TUint16)IPHeader.NetGetHdrLen();
+
+	IPHeader.NetSetLength((TUint16)(CurrentDataLength + IPHeaderLength + TCPHeaderLength));
+
+	//
+	//	Generate the IP Header Checksum.
+	//
+	DoIPChecksum(&IPHeader, IPHeaderLength);
+
+	//
+	//	Now the hard bit; I have reconstructed the packet, now need 
+	//	to get it into the right format and receive it.
+	//
+	CopyInNewHeaderL(&aPacket, &IPHeader, &TCPHeader, (VJCompHeader - aInitialHeaderPtr), IPHeaderLength, TCPHeaderLength);
+
+	//TCPHeader.Printf();
+	//
+	//	Store the rebuilt TCP/IP header for this aConnection
+	//
+	//  GetIPHeader will never return NULL here because we check the
+	//  packet size above.
+	//
+	CopyRecvHeader(aConnection, GetIPHeader(aPacket));	  
+	
+	//
+	//	Increment the length in the Info Header
+	//
+	RMBufPktInfo* info = RMBufPacket::PeekInfoInChain(aPacket);
+	info->iLength += IPHeaderLength + TCPHeaderLength - OriginalHeaderLength;
+}
+
+// TODO: This is a funny place for this declaration
+void ThdrTCP::Printf()
+	{	
+	TInt i;
+	LOG(_LIT(string1,"TCP Header.");)
+	LOG(Log::Write(string1);)
+	LOG(_LIT(string2,"%02x ");)
+	for (i=0;i<20;i++)
+		{
+		LOG(Log::Printf(string2,u.iData8[i]);)
+		}
+	}
+
+ThdrIP* CVJDeCompressor::GetIPHeader(RMBufChain &aChain)
+/**
+Extracts the IP Header from the packet even though there is a buffer
+of Info on the front.
+This is used in VJ, to avoid the awful hack in the main receive path.
+PRR 20-11-97
+
+@param aChain MBuf chain containing packet
+
+@return Pointer to packet data in chain, or NULL if the packet is too short
+*/
+	{
+	RMBuf* Temp = aChain.Remove();
+	TUint n = aChain.Align(KInetMaxHeaderSize);
+	ThdrIP* IPHeader = (n<KIPMinHeaderSize) ? NULL : (ThdrIP*)aChain.First()->Ptr();
+	aChain.Prepend(Temp);
+	return IPHeader;
+	}
+
+void CVJDeCompressor::DoIPChecksum(ThdrIP* aIPHeader, TUint16 aIPHeaderLength)
+/**
+Calculate the IP checksum and store it in the given IP packet header.
+See RFC 1071.
+
+@param aIPHeader IP packet header
+@param aIPHeaderLength Length of IP header in bytes (must be even)
+*/
+	{
+	__ASSERT_DEBUG(!(aIPHeaderLength % 2), User::Panic(_L("VJ Panic"), 0));	// length must be even
+
+	aIPHeader->VJSetChecksum(0);	// Clear the header checksum before calculating
+
+	TUint16*	Ptr = (TUint16*)aIPHeader;
+	TUint32		Checksum=0;
+	TInt		i;
+	for (i=aIPHeaderLength; i>0; i-=2)
+		{
+		Checksum += *Ptr++;
+		}
+
+	//
+	//	Take care of the wrapping over 16 bits
+	//  Doing it twice is sufficient for IP headers.
+	//
+	Checksum = (Checksum & 0xFFFF) + (Checksum >> 16);
+	Checksum = (Checksum & 0xFFFF) + (Checksum >> 16);
+	Checksum = ~Checksum;
+
+	//
+	// Do the endian conversion once, after the checksum is calculated
+	//
+	Checksum = BigEndian::Get16((TUint8*)&Checksum);
+
+	aIPHeader->VJSetChecksum((TUint16)Checksum);
+	}
+
+TUint8* CVJDeCompressor::GetVJPtr(RMBufChain &aChain, TUint16* aCurrentFrameLength)
+/**
+Gets a pointer to the VJ Header even though there is a pseudo (Adam) buffer of Info on the front.
+
+@param aChain MBuf chain containing packet
+@param aCurrentFrameLength Returns the length of the packet
+
+@return Pointer to beginning of packet data
+*/
+	{
+	RMBuf* Temp = aChain.Remove();
+	aChain.Align(KInetMaxHeaderSize);
+	TUint8* IPPtr = aChain.First()->Ptr();
+
+	*aCurrentFrameLength = (TUint16)aChain.Length();
+
+	aChain.Prepend(Temp);
+	return IPPtr;
+	}
+
+void CVJDeCompressor::CopyInNewHeaderL(RMBufChain* aPacket, ThdrIP* aIPHeader, ThdrTCP* aTCPHeader,
+    TUint aCompressedHeaderLength, TUint16 aIPHeaderLength, TUint16 aTCPHeaderLength)
+/**
+Copy the uncompressed header into the proper location in the packet.
+
+@param aPacket MBuf chain containing packet to be updated
+@param aIPHeader IP packet header
+@param aTCPHeader TCP header
+@param aCompressedHeaderLength Length of VJ compression header
+@param aIPHeaderLength Length of IP header
+@param aTCPHeaderLength Length of TCP header
+*/
+	{
+	TUint16 NewHeaderLength = (TUint16)(aIPHeaderLength + aTCPHeaderLength);
+	//
+	//  Take the Info thing off 
+	//
+	RMBuf* info = aPacket->Remove();
+
+	//
+	//	Now it is just possible that the frame contains no data
+	//
+	if ( aPacket->Length() == (TInt)aCompressedHeaderLength )
+		{
+		//
+		//	Have finished with the compressed header
+		//
+		aPacket->TrimStart(aCompressedHeaderLength);
+
+		aPacket->AllocL(NewHeaderLength);
+
+		}
+	else
+		{
+		//
+		//	Have finished with the compressed header
+		//
+		aPacket->TrimStart(aCompressedHeaderLength);
+
+		//
+		//	Make way for the uncompressed header
+		//
+		aPacket->PrependL(NewHeaderLength);
+		}
+
+	TPtrC8 TempDescIP((TUint8*)aIPHeader, aIPHeaderLength);
+	aPacket->CopyIn(TempDescIP, 0);
+
+	TPtrC8 TempDescTCP((TUint8*)aTCPHeader, aTCPHeaderLength);
+	aPacket->CopyIn(TempDescTCP, aIPHeaderLength);
+
+	//
+	//	Put the Info header back unchanged; it must be updated by the caller.
+	//
+	aPacket->Prepend(info);
+	}
+
+void CVJDeCompressor::DecompSWAU(const TUint aChanges, TUint8** aVJCompHeader, ThdrTCP* aTCPHeader, TUint16 aPreviousFrameLength)
+/**
+Decompresses the special case SWAU type packet.
+
+@param aChanges The VJ change mask
+@param aVJCompHeader Pointer to the pointer to the VJ compressed header
+@param aTCPHeader TCP header to be updated
+@param aPreviousFrameLength Length of cached packet for this VJ connection
+*/
+	{
+	TUint32	SequenceNumber;
+	TUint32	AckNumber;
+
+	switch ( aChanges & KVjCompMaskSpecials )
+		{
+		case KVjCompMaskSpecialI: // Echoed data e.g. Telnet
+			//
+			//	The sequence and acknowledge numbers increment by the data 
+			//	portion of the frame
+			//
+
+			SequenceNumber = aTCPHeader->NetGetSeqNum();
+			aTCPHeader->NetSetSeqNum(SequenceNumber + aPreviousFrameLength);
+			AckNumber = aTCPHeader->NetGetAckNum();
+			aTCPHeader->NetSetAckNum(AckNumber + aPreviousFrameLength);
+			break;
+
+		case KVjCompMaskSpecialD: //Unidirectional Data e.g. ftp
+			//
+			//	The sequence numbers increment by the data portion of the frame.	
+			//
+			SequenceNumber = aTCPHeader->NetGetSeqNum();
+			aTCPHeader->NetSetSeqNum(SequenceNumber + aPreviousFrameLength);
+			break;
+
+		default:
+			//
+			//	One (or more)of the SWAU bits are set and it's not a special case.
+			//
+			DecompUrgent(aVJCompHeader, aTCPHeader, aChanges);
+
+			if( aChanges & KVjCompMaskWindow )
+				{
+				DecompWindow(aVJCompHeader, aTCPHeader);
+				}
+
+			if( aChanges & KVjCompMaskAck )
+				{
+				DecompAck(aVJCompHeader, aTCPHeader);
+				}
+
+			if( aChanges & KVjCompMaskSeq )
+				{
+				DecompSeq(aVJCompHeader, aTCPHeader);
+				}
+
+			break;
+		}
+	}
+
+void CVJDeCompressor::DecompUrgent(TUint8** aVJCompHeader, ThdrTCP*aTCPHeader, TUint aChanges)
+/**
+Decompresses the Urgent field.
+
+@param aVJCompHeader Pointer to the pointer to the VJ compressed header
+@param aTCPHeader TCP header to be updated
+@param aChanges The VJ change mask
+*/
+	{
+	TUint Flags = aTCPHeader->VJGetFlags();
+
+	if ( aChanges & KVjCompMaskUrgent )
+		{
+		Flags |= KTcpURG;
+		TUint16 Delta = DecodeDelta(aVJCompHeader);
+		aTCPHeader->NetSetUrgPtr(Delta);
+		}
+	else
+		{
+		Flags &= ~KTcpURG;
+		}
+
+	aTCPHeader->VJSetFlags(Flags);
+	}
+
+void CVJDeCompressor::DecompIPId(const TUint aChanges, TUint8** aVJCompHeader, ThdrIP* aIPHeader)
+/**
+Decompresses the IP Identification field.
+
+@param aChanges The VJ change mask
+@param aVJCompHeader Pointer to the pointer to the VJ compressed header
+@param aIPHeader IP packet header to be updated
+*/
+	{
+	TUint16 ID;
+	if ( aChanges & KVjCompMaskIp )
+		{
+		ID = (TUint16)aIPHeader->NetGetId();
+		ID = (TUint16)(ID + DecodeDelta(aVJCompHeader));
+		aIPHeader->NetSetId(ID);
+		}
+	else
+		{
+		ID = (TUint16)aIPHeader->NetGetId();
+		ID++;
+		aIPHeader->NetSetId(ID);
+		}
+	}
+
+void CVJDeCompressor::DecompSeq(TUint8** aVJCompHeader, ThdrTCP* aTCPHeader)
+/**
+Decompresses the Sequence Number field.
+
+@param aVJCompHeader Pointer to the pointer to the VJ compressed header
+@param aTCPHeader TCP header to be updated
+*/
+	{
+	TUint16 Delta = DecodeDelta(aVJCompHeader);
+	TUint32 SequenceNumber = aTCPHeader->NetGetSeqNum();
+
+	SequenceNumber += Delta;
+
+	aTCPHeader->NetSetSeqNum(SequenceNumber);
+    }
+
+void CVJDeCompressor::DecompAck(TUint8** aVJCompHeader, ThdrTCP* aTCPHeader)
+/**
+Decompresses the Acknowledgement Number field.
+
+@param aVJCompHeader Pointer to the pointer to the VJ compressed header
+@param aTCPHeader TCP header to be updated
+*/
+	{
+	TUint16 Delta = DecodeDelta(aVJCompHeader);
+	TUint32 AckNumber = aTCPHeader->NetGetAckNum();
+
+	AckNumber += Delta;
+
+	aTCPHeader->NetSetAckNum(AckNumber);
+	}
+
+
+void CVJDeCompressor::DecompWindow(TUint8** aVJCompHeader, ThdrTCP* aTCPHeader)
+/**
+Decompresses the Window field.
+
+@param aVJCompHeader Pointer to the pointer to the VJ compressed header
+@param aTCPHeader TCP header to be updated
+*/
+	{
+	TInt16 Delta = DecodeDelta(aVJCompHeader);
+	TUint16 Window = aTCPHeader->NetGetWindow();
+
+	LOG(_LIT(string1,"Window %d");)
+	LOG(Log::Printf(string1, Window);)
+	LOG(_LIT(string2,"Window change %d");)
+	LOG(Log::Printf(string2, Delta);)
+
+	Window = (TUint16)(Delta + Window);
+	LOG(_LIT(string3,"New Window %d");)
+	LOG(Log::Printf(string3, Window);)
+
+	aTCPHeader->NetSetWindow(Window);
+	}
+
+void CVJDeCompressor::DecompPushFlag(const TUint aChanges, ThdrTCP* aTCPHeader)
+/**
+Decompresses the Push flag.
+
+@param aChanges The VJ change mask
+@param aTCPHeader TCP header to be updated
+*/
+	{
+	TUint Flags = aTCPHeader->VJGetFlags();
+
+	if ( aChanges & KVjCompMaskPush )
+		{
+		Flags |= KTcpPSH;
+		}
+	else
+		{
+		Flags &= ~KTcpPSH;
+		}
+
+	aTCPHeader->VJSetFlags(Flags);
+    }
+
+TUint16	CVJDeCompressor::DecodeDelta( TUint8** aVJCompHeader )
+/**
+Decodes a compressed delta value.
+
+@param aVJCompHeader Pointer to pointer into VJ header buffer holding encoded value;
+returns with pointer incremented one past end of value
+
+@return Decoded value
+*/
+	{
+	TUint16 Value = (TUint16) **aVJCompHeader;
+	(*aVJCompHeader)++;
+	
+	if (Value == 0)
+		{
+		//
+		//	Zero is an extension; the next two bytes give the 16 bit value
+		//
+		Value = BigEndian::Get16(*aVJCompHeader);
+		*aVJCompHeader += 2;
+		}
+	return Value;
+	}
+