diff -r 000000000000 -r af10295192d8 linklayerprotocols/pppnif/SVJCOMP/VJDECOMP.CPP --- /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 +#include +#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 = (nPtr(); + 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; + } +