linklayerprotocols/pppnif/SVJCOMP/VJCOMP.CPP
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linklayerprotocols/pppnif/SVJCOMP/VJCOMP.CPP	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,922 @@
+// 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 "VJ.H"
+#include "VJLOG.H"
+#include <networking/in_std.h>
+#include <in_sock.h>
+
+CVJCompressor::CVJCompressor()
+	{
+	}
+
+void CVJCompressor::ConstructL(CVJCompFactory* aFactory, TUint aMaxSlot, TBool aCompressConnId)
+/**
+Construct the CVJCompressor object.
+
+@param aFactory Factory that created this object
+@param aMaxSlot Value of the highest VJ connection number to be sent
+@param aCompressConnId Whether to compress the connection number 
+*/
+	{
+	__ASSERT_DEBUG(aMaxSlot >= KMinVjSlot && aMaxSlot <= KMaxVjSlot, User::Panic(_L("VJ Panic"), 0));
+	iMaxVJSlots = aMaxSlot+1;
+	iLastTxConn = aMaxSlot+1; // Initialize with an invalid slot number
+
+   	iTxStates = new (ELeave) TVJCompHdr[iMaxVJSlots];
+
+	iTxStates[0].SetNextPtr(&iTxStates[aMaxSlot]);
+	iTxStates[0].SetConnectionNumber(0);
+
+	iLastTxHdr = &iTxStates[0];
+
+	TUint i;
+	for (i=aMaxSlot; i>0; i--)
+		{
+		iTxStates[i].SetNextPtr(&iTxStates[i-1]);
+		iTxStates[i].SetConnectionNumber(i);
+		}
+
+	iCompressConnId = aCompressConnId;
+	iFactory = aFactory;
+	iFactory->Open();
+	}
+
+CVJCompressor::~CVJCompressor()
+	{
+	delete [] iTxStates;
+
+	if (iFactory)
+	    {
+		iFactory->Close();
+		}
+	}
+
+ThdrIP* CVJCompressor::GetIPHeader(RMBufChain &aChain)
+/**
+Get the IP Header 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 IP header
+*/
+	{
+	RMBuf* Temp = aChain.Remove();
+	TUint n = aChain.Align(KInetMaxHeaderSize);
+	ThdrIP* IPHeader = (n<KIPMinHeaderSize) ? NULL : (ThdrIP*)aChain.First()->Ptr();
+	aChain.Prepend(Temp);
+	return IPHeader;
+	}
+
+void CVJCompressor::EncodeDelta(TUint8** aVJCompHeader, TInt16 aValue)
+/**
+Encodes a delta value in VJ compressed format.
+
+@param aVJCompHeader Pointer to pointer into VJ header buffer to store encoded value;
+  returns with pointer incremented one past end of value
+@param aValue Value to store
+*/
+	{
+	if ((TUint16)aValue >= 256 || aValue == 0)
+		{
+		*(*aVJCompHeader) = 0;
+		(*aVJCompHeader)++;
+
+		BigEndian::Put16(*aVJCompHeader, aValue);
+		(*aVJCompHeader) += 2;
+		}
+	else
+		{
+		*(*aVJCompHeader) = (TUint8)aValue;
+		(*aVJCompHeader)++;
+		}
+	}
+
+TBool CVJCompressor::SendAsRawIP(	ThdrIP* aIPHeader, 
+								    ThdrTCP* aTCPHeader )
+/**
+Determines if the packet must bypass VJ compression altogether.
+This happens with fragmented IP packets and certain TCP flags.
+
+@param aIPHeader First IP packet header
+@param aTCPHeader TCP header
+
+@return Whether the packet must be sent raw
+*/
+	{
+	TBool RetCode = ETrue;
+
+	if (!aIPHeader->NetGetFragment())
+		{
+		//
+		//	Frame isn't fragmented, so make sure that no flags are set
+		//	which would prevent us from compressing the packet.
+		//
+		TUint Flags = aTCPHeader->VJGetFlags();
+		RetCode = (Flags & (KTcpFIN | KTcpSYN | KTcpRST | KTcpACK)) != KTcpACK;
+		}
+	
+	return RetCode;
+	}
+
+
+TBool CVJCompressor::SuitableForVJCompression(ThdrIP* aIPHeader, 
+										  ThdrTCP* aTCPHeader, 
+										  TUint* aConnection,
+										  ThdrIP* aRetrievedIPHdr,
+										  ThdrTCP* aRetrievedTCPHdr)
+/**
+Sees if a packet header is stored in the VJ connection cache.
+
+@param aIPHeader IP packet header
+@param aTCPHeader TCP header
+@param aConnection Returns the matching VJ connection number
+@param aRetrievedIPHdr Returns the cached IP packet header
+@param aRetrievedTCPHdr Returns the cached TCP packet header
+
+@return Whether the packet matches one in the cache
+*/
+	{
+	TBool RetCode = EFalse;
+	if (GetStoredTxHeader(	aConnection, 
+							aIPHeader, 
+							aTCPHeader, 
+							aRetrievedIPHdr, 
+							aRetrievedTCPHdr))
+		{
+		//
+		//	OK we found a stored header with the right 
+		//	addresses; is the header compressible?
+		//
+		RetCode = IsIPCompressible(aIPHeader, aRetrievedIPHdr) &&
+		          IsTCPCompressible(aTCPHeader, aRetrievedTCPHdr);
+		}
+
+	return RetCode;
+	}
+
+TBool CVJCompressor::IsIPCompressible(ThdrIP* aIPHeader, ThdrIP* aRetrievedHdr)
+/**
+Compares two IP headers to see if the seldom-changing fields match.
+
+@param aIPHeader First IP packet header
+@param aRetrievedHdr Second IP packet header
+
+@return Whether the headers match
+*/
+	{
+	TBool	RetCode = EFalse;
+
+	//
+	//	Ensure that the following match
+	//			Word 0
+	//					Protocol Version,
+	//					Header Length,
+	//					Type Of Service,
+	//			Word 3	
+	//					Fragment stuff
+	//			Word 4
+	//					Time to live,
+	//					Protocol,
+	//			If appropriate 
+	//					IP Options 
+	//
+	if ((aIPHeader->Word0() == aRetrievedHdr->Word0()) &&
+		(aIPHeader->Word3() == aRetrievedHdr->Word3()) &&
+		(aIPHeader->Word4() == aRetrievedHdr->Word4()))
+		{
+		//
+		//	Well those are fine now check the IP Options if there are any
+		//
+		const TUint Length = aIPHeader->NetGetHdrLen() - KIPMinHeaderSize;
+		TUint8*	Options = aIPHeader->GetOptions();
+
+		const TUint StoredLength = aRetrievedHdr->NetGetHdrLen() - KIPMinHeaderSize;
+		TUint8*	StoredOptions = aRetrievedHdr->GetOptions();
+		
+		if (Length == StoredLength)
+			{
+			if (Length == 0 || !Mem::Compare(Options, Length, StoredOptions, StoredLength))
+				{
+				RetCode = ETrue;
+				}
+			}
+		}
+		return RetCode;
+	}
+
+TBool CVJCompressor::IsTCPCompressible(ThdrTCP* aHeader, ThdrTCP* aRetrievedHdr)
+/**
+Compares two TCP headers to see if the options, ECN & reserved bits match.
+Everything else in the TCP header is handled specially by the
+VJ compression algorithm.
+
+@param aHeader First TCP header
+@param aRetrievedHdr Second TCP header
+
+@return Whether the headers match
+*/
+	{
+	TBool   RetCode = EFalse;
+
+    //
+    //  Ensure that the ECN and TCP reserved bits match
+    //
+    if ((aHeader->VJGetReserved() == aRetrievedHdr->VJGetReserved()) &&
+       ((aHeader->VJGetFlags() & KTcpECN) == (aRetrievedHdr->VJGetFlags() & KTcpECN)) &&
+   	//
+	//	Ensure that the header lengths match, if they do then 
+	//	check that the options match
+	//
+	   (aHeader->NetGetHdrLen() == aRetrievedHdr->NetGetHdrLen()))
+		{
+		//
+		//	Now compare the TCP Options 
+		//
+		const TUint Length = aHeader->NetGetHdrLen() - KTCPHeaderSize;
+		TUint8* Options = GetTCPOpts(aHeader);
+
+		const TUint StoredLength = aRetrievedHdr->NetGetHdrLen() - KTCPHeaderSize;
+		TUint8* StoredOptions = GetTCPOpts(aRetrievedHdr);
+
+		if (Length == StoredLength)
+			{
+			// This comparison will inevitably fail if a RFC 1323
+			// TCP timestamp option is found, causing VJ compression to be
+			// effectively disabled.
+			if (StoredLength == 0 || !Mem::Compare(Options, Length, StoredOptions, StoredLength))
+				{
+				RetCode = ETrue;
+				}
+			}
+		}
+	return RetCode;
+	}
+
+TBool CVJCompressor::CompressUrgentPtr(TUint8** aVJPtr, TUint8* aChanges, ThdrTCP* aTCPHeader, ThdrTCP* aRetrievedTCPHdr)
+/**
+Compresses the TCP Urgent pointer field.
+
+@param aVJPtr Pointer to the pointer within the VJ header to be modified;
+returns pointing one past the last header location used
+@param aChanges The VJ change mask to be updated
+@param aTCPHeader TCP header
+@param aRetrievedTCPHdr Cached TCP header from which to make a delta for aTCPHeader
+
+@return ETrue if the field was compressed
+*/
+	{
+	TBool   RetCode = ETrue;
+	TUint   Flags = aTCPHeader->VJGetFlags();
+	if (Flags & KTcpURG)
+		{
+		(*aChanges) |= KVjCompMaskUrgent;
+		TUint16 UrgPtr = aTCPHeader->NetGetUrgPtr();
+		EncodeDelta(aVJPtr, UrgPtr);
+		}
+	else if (aTCPHeader->NetGetUrgPtr() != aRetrievedTCPHdr->NetGetUrgPtr())
+		{
+		//
+		//	They can change the Urgent Ptr without setting the flag but 
+		//	it's not recommended, just send uncompressed frame
+		//
+		RetCode = EFalse;
+		}
+	return RetCode;
+	}
+
+TBool CVJCompressor::CompressWindow(TUint8** aVJPtr, TUint8* aChanges, ThdrTCP* aTCPHeader, ThdrTCP* aRetrievedTCPHdr)
+/**
+Compresses the TCP Window field.
+
+@param aVJPtr Pointer to the pointer within the VJ header to be modified;
+returns pointing one past the last header location used
+@param aChanges The VJ change mask to be updated
+@param aTCPHeader TCP header
+@param aRetrievedTCPHdr Cached TCP header from which to make a delta for aTCPHeader
+
+@return ETrue if the field was compressed
+*/
+	{
+	TBool	RetCode = ETrue;
+
+	TInt16 DeltaWindow = (TUint16)(aTCPHeader->NetGetWindow() - aRetrievedTCPHdr->NetGetWindow());
+	if (DeltaWindow)
+		{
+		LOG(_LIT(string1,"\nWindow Delta");)
+		LOG(Log::Write(string1);)
+		LOG(_LIT(string2,"%d\n");)
+		LOG(Log::Printf(string2,DeltaWindow);)
+		*aChanges |= KVjCompMaskWindow;
+		EncodeDelta(aVJPtr, DeltaWindow);
+		}
+
+	return RetCode;
+	}
+
+TBool CVJCompressor::CompressAck(TUint8** aVJPtr, TUint8* aChanges, ThdrTCP* aTCPHeader, ThdrTCP* aRetrievedTCPHdr)
+/**
+Compresses the TCP Acknowledgement Number field.
+
+@param aVJPtr Pointer to the pointer within the VJ header to be modified;
+returns pointing one past the last header location used
+@param aChanges The VJ change mask to be updated
+@param aTCPHeader TCP header
+@param aRetrievedTCPHdr Cached TCP header from which to make a delta for aTCPHeader
+
+@return ETrue if the field was compressed
+*/
+	{
+	TBool	RetCode = ETrue;
+
+	TInt32 DeltaAck = aTCPHeader->NetGetAckNum() - aRetrievedTCPHdr->NetGetAckNum();
+	if (DeltaAck)
+		{
+		if (IsDeltaCompressible(DeltaAck))
+			{
+			EncodeDelta(aVJPtr, (TInt16)DeltaAck);
+			*aChanges |= KVjCompMaskAck;
+			}
+		else
+			{
+			// Can't compress this large a difference
+			RetCode = EFalse;
+			}
+		}
+		return RetCode;
+	}
+
+
+TBool CVJCompressor::CompressSeq(TUint8** aVJPtr, TUint8* aChanges, ThdrTCP* aTCPHeader, ThdrTCP* aRetrievedTCPHdr)
+/**
+Compresses the TCP Sequence Number field.
+
+@param aVJPtr Pointer to the pointer within the VJ header to be modified;
+returns pointing one past the last header location used
+@param aChanges The VJ change mask to be updated
+@param aTCPHeader TCP header
+@param aRetrievedTCPHdr Cached TCP header from which to make a delta for aTCPHeader
+
+@return ETrue if the field was compressed
+*/
+	{
+	TBool	RetCode = ETrue;
+
+	TInt32 DeltaSeq = aTCPHeader->NetGetSeqNum() - aRetrievedTCPHdr->NetGetSeqNum();
+	if (DeltaSeq)
+		{
+		if (IsDeltaCompressible(DeltaSeq))
+			{
+			EncodeDelta(aVJPtr, (TInt16)(DeltaSeq));
+			*aChanges |= KVjCompMaskSeq;
+			}
+		else
+			{
+			// Can't compress this large a difference
+			RetCode = EFalse;
+			}
+		}
+	return RetCode;
+	}
+
+TBool CVJCompressor::CompressSpecialCases(TUint8** aVJPtr, 
+						   TUint8* const aVJInitialDeltaPtr, 
+						   TUint8* aChanges, 
+						   ThdrTCP* aTCPHeader, 
+						   ThdrTCP* aRetrievedTCPHdr, 
+						   ThdrIP*	aIPHeader,
+						   ThdrIP* aRetrievedIPHdr)
+/**
+Compresses special case TCP/IP packets.
+See RFC 1144 sec. 3.2.3
+
+@pre aChanges must reflect the compressed header except for the
+KVjCompMaskPush and KVjCompMaskIp bits which must be clear.
+
+@param aVJPtr Pointer to the pointer within the VJ header to be modified;
+returns pointing one past the last header location used
+@param aVJInitialDeltaPtr Beginning of the compressed VJ header
+@param aChanges The VJ change mask to be updated
+@param aTCPHeader TCP header
+@param aRetrievedTCPHdr Cached TCP header from which to make a delta for aTCPHeader
+@param aIPHeader IP header
+@param aRetrievedIPHdr Cached IP header from which to make a delta for aIPHeader
+
+@return ETrue if the packet can be compressed
+*/
+	{
+	
+	__ASSERT_DEBUG(!(*aChanges & (KVjCompMaskIp | KVjCompMaskPush)), User::Panic(_L("VJ Panic"), 0));
+	TBool RetCode = ETrue;
+
+	TUint DeltaSeq = aTCPHeader->NetGetSeqNum() - aRetrievedTCPHdr->NetGetSeqNum();
+	TUint DeltaAck = aTCPHeader->NetGetAckNum() - aRetrievedTCPHdr->NetGetAckNum();
+
+	switch (*aChanges)
+		{
+		case 0:
+		    // No change from the last packet; this frame may be a retransmission.
+		    // See RFC 1144 §4.2
+			if (((aIPHeader->NetGetLength() - aIPHeader->NetGetHdrLen()) == aTCPHeader->NetGetHdrLen()) ||
+			    ((aRetrievedIPHdr->NetGetLength() - aRetrievedIPHdr->NetGetHdrLen()) != aRetrievedTCPHdr->NetGetHdrLen()))
+				{
+				//
+				//	The frame has been sent compressed and was probably missed at the other
+				//	end, so send it uncompressed.
+				//
+				LOG(_LIT(string1,"Missed frame");)
+				LOG(Log::Write(string1);)
+				RetCode = EFalse;				
+				}
+			break;
+
+		case KVjCompMaskSpecialD:
+		case KVjCompMaskSpecialI:
+			// Can't send SWU and SWAU, so send Uncompressed
+			RetCode = EFalse;
+			break;
+
+		case KVjCompMaskSeq | KVjCompMaskAck:
+			if (DeltaAck == DeltaSeq)
+				{
+				if (DeltaSeq == (aRetrievedIPHdr->NetGetLength() - aRetrievedIPHdr->NetGetHdrLen() - aRetrievedTCPHdr->NetGetHdrLen()))
+					{
+					// Terminal traffic
+					*aChanges = KVjCompMaskSpecialI;
+					*aVJPtr = aVJInitialDeltaPtr; // Reset back to just after CSum
+					}
+				}
+			break;
+
+		case KVjCompMaskSeq:
+			if (DeltaSeq == (aRetrievedIPHdr->NetGetLength() - aRetrievedIPHdr->NetGetHdrLen() - aRetrievedTCPHdr->NetGetHdrLen()))
+				{
+				// Data Xfer e.g. FTP
+				*aChanges = KVjCompMaskSpecialD;
+				*aVJPtr = aVJInitialDeltaPtr; // Reset back to just after CSum
+				}
+			break;
+
+	    default:
+	        // Not a special case
+	        break;
+		}
+
+	return RetCode;
+	}
+
+TBool CVJCompressor::CompressIPId(TUint8** aVJPtr, TUint8* aChanges, ThdrIP* aIPHeader, ThdrIP* aRetrievedIPHdr)
+/**
+Compresses the IP Packet ID field.
+
+@param aVJPtr Pointer to the pointer within the VJ header to be modified;
+returns pointing one past the last header location used
+@param aChanges The VJ change mask to be updated
+@param aIPHeader IP header
+@param aRetrievedIPHdr Cached IP header from which to make a delta for aIPHeader
+
+@return ETrue
+*/
+	{
+	TInt16 DeltaIPId = (TInt16) ((TInt)aIPHeader->NetGetId() - (TInt)aRetrievedIPHdr->NetGetId());
+	if (DeltaIPId != 1)
+		{
+		EncodeDelta(aVJPtr, DeltaIPId);
+		*aChanges |= KVjCompMaskIp;
+		}
+
+	return ETrue;
+	}
+
+TBool CVJCompressor::CompressPush(TUint8* aChanges,  ThdrTCP* aTCPHeader)
+/**
+Compresses the TCP Push flag.
+
+@param aChanges The VJ change mask to be updated
+@param aTCPHeader TCP header
+
+@return ETrue
+*/
+	{
+	TUint Flags = aTCPHeader->VJGetFlags();
+	if (Flags & KTcpPSH )
+		{
+		*aChanges |= KVjCompMaskPush;
+		}
+
+	return ETrue;
+	}
+
+TBool CVJCompressor::IsSameConnAsLast(TUint* aCompressedHdrLen, TUint aConnection)
+/**
+Determines whether the given VJ connection number is the same as that of the
+last compressed packet sent and returns the size of the compressed header.
+
+@param aCompressedHdrLen Returns the length of the compressed header
+@param aConnection VJ connection number
+
+@return Whether this is the same connection number as the last one
+*/
+	{
+	TBool RetCode;
+
+	if ((aConnection != iLastTxConn) || (iCompressConnId == EFalse))
+		{
+		RetCode = EFalse;
+		*aCompressedHdrLen = 4; // Changes, Connection, CSum
+		}
+	else
+		{
+		RetCode = ETrue;
+		*aCompressedHdrLen = 3; // Changes, CSum
+		}
+	return RetCode;
+	}
+
+
+void CVJCompressor::SetFirstFewBytes(TUint8* aChanges, TBool aNewConnection, TUint8** aVJHeader, ThdrTCP* aTCPHeader, TUint aConnection)
+/**
+Sets the change mask, connection number (if necessary) and TCP checksum bytes
+in the VJ compressed header.
+
+@param aChanges The VJ change mask to be copied and potentially modified
+@param aNewConnection Whether this is a new connection, requiring the number in the header
+@param aVJHeader Pointer to the pointer to the VJ header to be modified
+@param aTCPHeader TCP header
+@param aConnection VJ connection number
+*/
+	{
+	TUint8* ChecksumPtr;
+
+	//
+	// Be warned this value is NOT stored in native byte order;
+	// the sixteen bit value is just loaded from the frame -- it's NOT
+	// byte swapped 
+	//
+	TUint16 Checksum = (TUint16)aTCPHeader->NetGetChecksum();
+	LOG(_LIT(logString1,"Checksum is %x");)
+	LOG(Log::Printf(logString1,Checksum);)
+
+	if (aNewConnection)
+		{
+		(*aVJHeader)[1] = (TUint8)aConnection;
+		ChecksumPtr = (*aVJHeader)+2;
+		LittleEndian::Put16(ChecksumPtr,Checksum);
+		*aChanges |= KVjCompMaskConn;
+		}
+	else
+		{
+		ChecksumPtr = (*aVJHeader)+1;
+		LittleEndian::Put16(ChecksumPtr,Checksum);
+		}
+
+	*(*aVJHeader) = *aChanges;
+	}
+
+void CVJCompressor::CopyInNewTxHeader(RMBufChain& aPacket, TUint8* aCompressedHdr, TUint aOldHeaderLength, TUint aNewHeaderLength )
+/**
+Copies the compressed header into the packet.
+
+@param aPacket MBuf chain containing packet
+@param aCompressedHdr VJ compressed header
+@param aOldHeaderLength Length of the current TCP/IP header in aPacket
+@param aNewHeaderLength Length of VJ compressed header
+*/
+	{
+	//
+	// Remove the first info buffer thing
+	//
+	RMBuf* Temp = aPacket.Remove();
+
+	//
+	//	Trim the Packet length to take into account the new header
+	//
+	aPacket.TrimStart(aOldHeaderLength-aNewHeaderLength);
+	TPtrC8 TempDesc(aCompressedHdr, aNewHeaderLength);
+	aPacket.CopyIn(TempDesc, 0);
+	
+	aPacket.Prepend(Temp);
+	}
+
+void CVJCompressor::DecrementPacketLen( RMBufChain& aPacket, TUint SizeDecrease )
+/**
+There is a header prepended to the frame, one element is the length 
+which has now changed, so change it
+
+@param aPacket MBuf chain containing packet
+@param SizeDecrease Amount by which to reduce the length metadata
+*/
+	{
+	RMBufPktInfo* info = RMBufPacketBase::PeekInfoInChain(aPacket);
+	info->iLength -= SizeDecrease;
+	}
+
+TBool CVJCompressor::CompressFrame(RMBufChain& aPacket, 
+							   ThdrIP* aIPHeader, 
+							   ThdrTCP* aTCPHeader, 
+							   TUint aConnection,
+							   ThdrIP* aRetrievedIPHdr,
+							   ThdrTCP* aRetrievedTCPHdr)
+/**
+Compresses a TCP/IP header if possible.
+
+@param aPacket MBuf chain containing packet
+@param aIPHeader IP header
+@param aTCPHeader TCP header
+@param aConnection VJ connection number
+@param aRetrievedIPHdr Cached IP packet header
+@param aRetrievedTCPHdr Cached TCP packet header
+
+@return Whether the header was compressed
+*/							   
+	{
+	TBool	RetCode=EFalse;
+	TUint8	Changes=0;
+	TUint8	VJHeader[16];	// The VJ Header without changes, checksum and connection Number
+	TUint CompressedHdrLen;
+	
+	//
+	//	Is Connection ID Compression turned on?
+	//
+	TBool NewConnection = !IsSameConnAsLast(&CompressedHdrLen, aConnection);
+
+	TUint8*	VJPtr = VJHeader + CompressedHdrLen;	
+	TUint8*	StartVJDeltaPtr = VJPtr;
+
+	if (!CompressUrgentPtr(&VJPtr, &Changes, aTCPHeader, aRetrievedTCPHdr))
+	{
+		return RetCode;
+    }
+    
+	if (!CompressWindow(&VJPtr, &Changes, aTCPHeader, aRetrievedTCPHdr))
+	{
+		return RetCode;
+    }
+
+	if (!CompressAck(&VJPtr, &Changes, aTCPHeader, aRetrievedTCPHdr))
+	{
+		return RetCode;
+    }
+
+	if (!CompressSeq(&VJPtr, &Changes, aTCPHeader, aRetrievedTCPHdr))
+	{
+		return RetCode;
+    }
+
+	if (!CompressSpecialCases(&VJPtr, StartVJDeltaPtr, &Changes, aTCPHeader, aRetrievedTCPHdr, aIPHeader, aRetrievedIPHdr))
+	{
+		return RetCode;
+    }
+
+	if (!CompressIPId(&VJPtr, &Changes, aIPHeader, aRetrievedIPHdr))
+	{
+		return RetCode;
+    }
+
+	if (!CompressPush(&Changes, aTCPHeader))
+	{
+		return RetCode;
+    }
+
+	RetCode = ETrue;
+	CompressedHdrLen += (VJPtr - StartVJDeltaPtr);
+	VJPtr = VJHeader;
+	SetFirstFewBytes(&Changes, NewConnection, &VJPtr, aTCPHeader, aConnection);
+	
+	//
+	//	We now have a compressed header,
+	//	Save the uncompressed header and 
+	//	replace the IP header on the outgoing packet
+	//
+
+	iLastTxConn = aConnection;
+
+	CopyTxHeader(aIPHeader, aConnection);
+
+	TUint OriginalHdrLen = aIPHeader->NetGetHdrLen() + aTCPHeader->NetGetHdrLen();
+
+	CopyInNewTxHeader(aPacket, VJHeader, OriginalHdrLen, CompressedHdrLen);
+
+	DecrementPacketLen( aPacket, (OriginalHdrLen - CompressedHdrLen));
+
+	return RetCode;
+	}
+
+void CVJCompressor::ConvertFrameToUncompVJ(ThdrIP* aIPHeader, TUint aConnection)
+/**
+The frame is to be sent as an Uncompressed VJ frame, so set the 
+connection number in place of the protocol type, and save the header.
+
+@param aIPHeader TCP/IP header
+@param aConnection VJ connection number
+*/
+	{
+		CopyTxHeader(aIPHeader, aConnection);
+		aIPHeader->NetSetProtocol(aConnection);
+		iLastTxConn = aConnection;
+	}
+
+TInt CVJCompressor::VJCompressFrame(RMBufChain& aPacket)
+/**
+Takes a packet and transforms it for sending.
+It may end up VJ compressed, VJ uncompressed or untouched.
+
+@param aPacket MBuf chain containing packet
+
+@return PPP protocol type for the converted packet
+*/
+	{
+	ThdrIP*		IPHeader;
+	ThdrTCP*	TCPHeader;
+	ThdrIP		StoredIPHdr;
+	ThdrTCP		StoredTCPHdr;
+	TUint		ConnectionNumber;
+
+	IPHeader = GetIPHeader(aPacket);
+	if (IPHeader->NetGetProtocol() == KProtocolInetTcp)
+		{
+		TCPHeader = ThdrTCP::NetPtr(IPHeader);
+		if (!SendAsRawIP(IPHeader, TCPHeader))
+			{
+			if (SuitableForVJCompression( IPHeader, TCPHeader, &ConnectionNumber, &StoredIPHdr, &StoredTCPHdr))
+				{
+				//
+				//	Better Compress it then
+				//
+				if (CompressFrame(aPacket, IPHeader, TCPHeader, ConnectionNumber, &StoredIPHdr, &StoredTCPHdr))
+					{
+					return KPppIdVjCompTcp;
+					}
+				}
+			//
+			//	Change the frame to VJ Uncompressed type
+			//
+			ConvertFrameToUncompVJ(IPHeader, ConnectionNumber);
+			return KPppIdVjUncompTcp;
+			}
+		}
+
+	//
+	//	Send normal IP frame
+	//
+	//
+	return KPppIdIp;
+	}
+
+
+void CVJCompressor::CopyTxHeader( ThdrIP* aIPHeader, TUint aConnection)
+/**
+Copies the given TCP/IP header into the storage slot for the given
+connection number.
+
+@param aIPHeader TCP/IP header
+@param aConnection VJ connection number
+*/
+	{
+	__ASSERT_DEBUG(aConnection < iMaxVJSlots, User::Panic(_L("VJ Panic"), 0));
+	iTxStates[aConnection].StoreTCPIPHeader(aIPHeader);
+	}
+
+
+TBool CVJCompressor::IPAddressesMatch(ThdrIP* aIPHeader, ThdrIP* aNotherIPHdr)
+/**
+Compares the addresses in two IP headers.
+
+@param aIPHeader First IP header
+@param aNotherIPHdr Second IP header
+
+@return Whether source and dest addresses in both headers are the same
+*/
+	{
+	return (aNotherIPHdr->GetSrcAddr() == aIPHeader->GetSrcAddr()) &&
+		   (aNotherIPHdr->GetDstAddr() == aIPHeader->GetDstAddr());
+	}
+
+TBool CVJCompressor::TCPPortsMatch(ThdrTCP* aHeader, ThdrTCP* aNotherHdr)
+/**
+Compares the TCP port numbers TCP headers.
+
+@param aHeader First TCP header
+@param aNotherHdr Second TCP header
+
+@return Whether both source and dest ports in both headers are the same
+*/
+	{
+	return (aNotherHdr->NetGetSrcPort() == aHeader->NetGetSrcPort()) &&
+		   (aNotherHdr->NetGetDstPort() == aHeader->NetGetDstPort());
+	}
+
+
+TBool CVJCompressor::GetStoredTxHeader(TUint* aConnection, ThdrIP* aIPHeader, ThdrTCP* aTCPHeader, ThdrIP* aRetrievedIPHdr, ThdrTCP* aRetrievedTCPHdr)
+/**
+Retrieves a packet header from the VJ connection cache.
+The packet must match on the IP source and destination address and on
+the TCP source and destination port numbers.
+
+@param aConnection Returns the matching VJ connection number
+@param aIPHeader IP packet header
+@param aTCPHeader TCP header
+@param aRetrievedIPHdr Returns the cached IP packet header
+@param aRetrievedTCPHdr Returns the cached TCP packet header
+
+@return Whether a matching packet was found in the cache
+*/
+	{
+	TBool RetCode = EFalse;
+
+	//
+	//	The transmit buffer is maintained
+	//	in a least recently used circular linked list.
+	//  iLastTxHdr always points to the least recently used entry and
+    //  iLastTxHdr->NextPtr() points to the most recently used.
+    //
+    
+    //
+    //  Make a special case of the common occurrence that the 
+    //  most recently used header is re-used (for speed).
+	//
+
+	TVJCompHdr* LastPtr = iLastTxHdr;  // Pointer to the last element in the circular list
+	TVJCompHdr* Ptr = LastPtr->NextPtr();
+	Ptr->RetrieveTCPIPHeader(aRetrievedIPHdr, aRetrievedTCPHdr);
+
+    if ( Ptr->IsValid() &&
+    	 IPAddressesMatch(aIPHeader, aRetrievedIPHdr) &&
+         TCPPortsMatch(aTCPHeader, aRetrievedTCPHdr))
+      {
+	  //
+      // We found a match without having to search the list
+	  //
+	  RetCode = ETrue;
+      }
+    else
+      {
+      //
+      // Oh well, we have to play with linked lists
+      //
+      TVJCompHdr* PrevPtr;
+      do 
+         {
+         PrevPtr = Ptr;
+         Ptr = Ptr->NextPtr();
+
+		 //
+     	 // Don't bother comparing addresses and ports if the connection has
+     	 // never been used. It's tempting to break out of the search loop
+     	 // in that case (the remainder of the list is guaranteed to be empty
+     	 // because it's stored in MRU order), but ghdn PrevPtr wouldn't
+     	 // be correct.
+     	 //
+         if (Ptr->IsValid())
+         	{
+		    Ptr->RetrieveTCPIPHeader(aRetrievedIPHdr, aRetrievedTCPHdr);
+			if (   IPAddressesMatch(aIPHeader, aRetrievedIPHdr) &&
+	               TCPPortsMatch(aTCPHeader, aRetrievedTCPHdr))
+	         	{
+	         	//
+	         	// We found it!! 
+	         	//
+	         	RetCode = ETrue;
+	         	break;
+	         	}
+	         }
+         }
+      while(Ptr != LastPtr);
+
+      if (RetCode && LastPtr != Ptr)
+         {
+         //
+         // Right, take the buffer we just found and move it to
+         // the start, with all the fiddling with pointers that 
+         // implies.
+         //
+         PrevPtr->SetNextPtr(Ptr->NextPtr());
+         Ptr->SetNextPtr(LastPtr->NextPtr());
+         LastPtr->SetNextPtr(Ptr);
+         }
+      else
+         {
+         //
+         // We didn't find it so use the current last frame.
+         //
+         iLastTxHdr = PrevPtr;
+         }
+      }
+  
+	*aConnection = Ptr->ConnectionNumber();
+	return RetCode;
+	}