--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/iphook/inhook6/src/in_pkt.cpp Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,558 @@
+// Copyright (c) 2006-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:
+// in_pkt.cpp - packet handling routines
+// Mainly the RMBufPacketPeek implementation
+// + extension header locations
+// + method for adding a destination option
+//
+
+#include "in_pkt.h"
+#include "ext_hdr.h"
+
+#define OPTIMIZE_PADDING
+
+
+// RMBufPacketPeek
+// ***************
+
+EXPORT_C TUint8 *TInet6PacketBase::Access(RMBufChain &aPacket, TInt aOffset, TInt aSize, TInt aMin)
+ /**
+ * Accesses a contiguous block of memory within RMBufChain starting
+ * at specified offset.
+ *
+ * Attempts to map aSize amount of contiguous RMBuf space at
+ * the specified offset, by rearranging the RMBufs,
+ * if necessary.
+ *
+ * If aSize amount is not available, the returned pointer maps
+ * the maximum available space (< aSize).
+ *
+ * @param aSize
+ * The size of the requested contiguous mapping (in octets).
+ * The value cannot be larger than KMBufSmallSize. If aSize <= 1,
+ * the method will return availabe contiguous mapping at the
+ * indicated offset without rearranging the RMBuf chain. Values
+ * larger than 1 may require rearrangement to get the requested
+ * area into single RMBuf.
+ * @param aOffset
+ * The offset to the beginning of the area. The value MUST
+ * be in range: 0 <= aOffset <= Length().
+ * @param aMin
+ * The minimum accepted iLength. If iLength is less that aMin,
+ * then returns NULL.
+ * @return
+ * A pointer to the data starting at indicated
+ * offset. The iLength is set to the
+ * maximum available contiguous data starting from the
+ * requested offset. iLength is always <= KMBufSmallSize.
+ * @note
+ * The length can be either less or
+ * larger than aSize. The caller must verify the returned
+ * length to detect whether Access succeeded.
+ */
+ {
+
+ //
+ // Can only map at most KMBufSmallSize bytes
+ // (truncate aSize to avoid Panics from the Align)
+ //
+ if (aSize > KMBufSmallSize)
+ aSize = KMBufSmallSize;
+
+ for (int already_tried = 0; ;already_tried = 1)
+ {
+ RMBuf *p;
+ TInt offset, len;
+ iLength = 0;
+
+ if (aPacket.IsEmpty() || !aPacket.Goto(aOffset, p, offset, len))
+ return NULL;
+ else if ((aSize <= len && (offset & iAlign) == 0) || already_tried)
+ {
+ if (len < aMin)
+ return NULL;
+ iLength = len;
+ return p->Buffer() + offset;
+ }
+ // The requested alignment value is not
+ // satisfied!
+ //
+ // Brute force buffer mangling here: just
+ // Split the chain at requested point, apply
+ // align to the tail part, and then join the
+ // pieces together (assuming this join operation
+ // does not undo the align...)
+ // (If this is going to occur frequently
+ // some optimizations is in order... -- msa)
+ //
+ //
+ // *NOTE*
+ // If aOffset == 0, SplitL works weird (IMHO): it does
+ // nothing (when it logically should move all to the
+ // tail part!). Thus, zero offset needs to be handled
+ // specially (sort of optimizing, so it's not too bad)
+ // -- msa
+ //
+ if (aOffset > 0)
+ {
+ RMBufChain tail;
+ TInt err = aPacket.Split(aOffset, tail);
+ if (err == KErrNone && !tail.IsEmpty())
+ {
+ tail.Align(aSize);
+ aPacket.Append(tail);
+ }
+ }
+ else
+ aPacket.Align(aSize);
+ // And now just retry the Goto and use that
+ // result as is (either we succeeded or not)
+ }
+ }
+
+EXPORT_C TPtr8 RMBufPacketPeek::Access(TInt aSize, TUint aOffset)
+ /**
+ * Accesses a contiguous block of memory within RMBufChain starting
+ * at specified offset.
+ *
+ * Attempts to map aSize amount of contiguous RMBuf space at
+ * the specified offset into a TPtr8, by rearranging the RMBufs,
+ * if necessary.
+ *
+ * If aSize amount is not available, the returned pointer maps
+ * the maximum available space (< aSize).
+ *
+ * @param aSize
+ * The size of the requested contiguous mapping (in octets).
+ * The value cannot be larger than KMBufSmallSize. If aSize <= 1,
+ * the method will return availabe contiguous mapping at the
+ * indicated offset without rearranging the RMBuf chain. Values
+ * larger than 1 may require rearrangement to get the requested
+ * area into single RMBuf.
+ * @param aOffset
+ * The offset to the beginning of the area. The value MUST
+ * be in range: 0 <= aOffset <= Length().
+ * @return
+ * A pointer descriptor for the data starting at indicated
+ * offset. The length of the descriptor is set to the
+ * maximum available contiguous data starting from the
+ * requested offset. Length is always <= KMBufSmallSize.
+ * @note
+ * The length can be either less or
+ * larger than aSize. The caller must verify the returned
+ * length to detect whether Access succeeded.
+ */
+ {
+ TInet6PacketBase map(TInet6PacketBase::EAlign1);
+ TUint8 *const ptr = map.Access(*this, aOffset, aSize, 1);
+ return TPtr8(ptr, map.iLength, map.iLength);
+ }
+
+
+EXPORT_C TIpHeader *RMBufPacketPeek::GetIpHeader()
+ /**
+ * Access IP header (v4 or v6) from the packet at offset 0.
+ *
+ * @return IP header (or NULL).
+ * A non-NULL return implies that full header, either IPv4 or
+ * IPv6 is accessible through the pointer. This method is
+ * intended to save code space, and not necessarily for
+ * speed...
+ */
+ {
+ TInet6Packet<TIpHeader> ip(*this);
+ if (ip.iHdr)
+ {
+ if (ip.iHdr->ip4.Version() == 4)
+ {
+ // Need to do sanity check on header length (because it can be garbage)
+ const TInt hlen = ip.iHdr->ip4.HeaderLength();
+ if (hlen >= TInet6HeaderIP4::MinHeaderLength() && ip.iLength >= hlen)
+ return (TIpHeader *)ip.iHdr;
+ }
+ else if (ip.iHdr->ip4.Version() == 6)
+ {
+ if (ip.iLength >= TInet6HeaderIP::MinHeaderLength())
+ return (TIpHeader *)ip.iHdr;
+ }
+ }
+ return NULL;
+ }
+
+// TPacketPocker
+// *************
+
+EXPORT_C TPacketPoker::TPacketPoker(RMBufChain &aChain) : iCurrent(aChain.First()), iOffset(0), iTail(0)
+ /**
+ * Constructor.
+ *
+ * @param aChain The RMBuf chain to be poked.
+ */
+ {
+ // Find initial value for iTail.
+ while (iCurrent)
+ {
+ iTail = iCurrent->Length();
+ if (iTail > 0)
+ break;
+ // Ugh. There have been RMBuf's with ZERO length!
+ iCurrent = iCurrent->Next();
+ }
+ }
+
+EXPORT_C void TPacketPoker::OverL(TInt aSize)
+ /**
+ * Skip over bytes.
+ *
+ * @param aSize The number of bytes to skip
+ * @leave KErrEof, if skipped past end of chain.
+ */
+ {
+ for (;;)
+ {
+ if (aSize < iTail)
+ {
+ iTail -= aSize; // Results always: iTail > 0!
+ iOffset += aSize;
+ return;
+ }
+ aSize -= iTail;
+ if ((iCurrent = iCurrent->Next()) == NULL)
+ {
+ iTail = 0; // All scanned, at end of chain
+ if (aSize > 0) // --> Error, if request was more
+ User::Leave(KErrEof); // KErrEof used (for lack of any better)
+ return;
+ }
+ iOffset = 0;
+ iTail = iCurrent->Length();
+ }
+ }
+
+EXPORT_C TUint8 *TPacketPoker::AdjustL(TInt aSize)
+ /**
+ * Arrange contiguous run of bytes.
+ *
+ * Arragen content of RMBuf chain so that starting from
+ * the current offset, a number of bytes is accessible
+ * in contiguous memory.
+ *
+ * @param aSize The requested length,
+ * @return The pointer to be beginning of area.
+ * @leave KErrEof, if request cannot be satisfied.
+ */
+ {
+ RMBufChain chain(iCurrent);
+ if (iOffset > 0)
+ {
+ RMBufChain tail;
+ chain.SplitL(iOffset, tail);
+ tail.Align(aSize);
+ chain.Append(tail);
+ }
+ else
+ chain.Align(aSize);
+ ASSERT(iCurrent == chain.First());
+ iTail = iCurrent->Length() - iOffset;
+ if (iTail < aSize) // Align failed?
+ User::Leave(KErrEof); // KErrEof used (for lack of any better)
+ return Ptr();
+ }
+
+//
+
+EXPORT_C TBool TPacketPoker::IsExtensionHeader(TInt aProtocolId)
+ /**
+ * Tests whether a protocol is a known IPv6 extension header using the standard format.
+ *
+ * @param aProtocolId Protocol ID to test.
+ * @return ETrue,
+ * if the protocol header follows the generic IPv6 extension
+ * header format (TInet6HeaderExtension).
+ */
+ {
+ // If more of these are known, could perhaps implement a
+ // static boolean table indexed by protocol -- msa
+ return
+ (
+ aProtocolId == STATIC_CAST(TInt, KProtocolInet6HopOptions) ||
+ aProtocolId == STATIC_CAST(TInt, KProtocolInet6DestinationOptions) ||
+ aProtocolId == STATIC_CAST(TInt, KProtocolInet6RoutingHeader)
+ );
+ }
+
+// TPacketHead
+// ***********
+
+EXPORT_C TBool TPacketHead::ExtHdrGet(TInt aType, TInt& aOfs, TInt& aLen)
+ /**
+ * Gets the offset and length of an extension header.
+ *
+ * @param aType Extension header type
+ * @retval aOfs On return, the header offset
+ * @retval aLen On return, the header length
+ * @return ETrue if the header was found, otherwise EFalse
+ */
+ {
+ if (iPacket.IsEmpty())
+ return EFalse;
+
+ TInt type = ip6.NextHeader();
+ TInt ofs = 0;
+
+ while (TPacketPoker::IsExtensionHeader(type) && ofs<iOffset)
+ {
+ //TInet6Options is about as generic Extension Header class as possible.
+ //Thus no need to create another one.
+ TInet6Packet<TInet6Options> hdr(iPacket, ofs);
+ if (hdr.iHdr == NULL) return EFalse;
+
+ if (type == aType)
+ {
+ aOfs = ofs;
+ aLen = hdr.iHdr->HeaderLength();
+ return ETrue;
+ }
+ type = hdr.iHdr->NextHeader();
+ ofs += hdr.iHdr->HeaderLength();
+ }
+ return EFalse;
+ }
+
+EXPORT_C TBool TPacketHead::ExtHdrGetOrPrependL(TInt aType, TInt& aOfs, TInt& aLen)
+ /**
+ * Gets the offset and length of an extension header, or if it doesn't already
+ * exist, creates the header before all other extension headers.
+ *
+ * @param aType
+ * Extension header type
+ * @retval aOfs
+ * If the header is created, this specifies the header offset. If
+ * the header already exists, on return: the actual header offset.
+ * @retval aLen
+ * If the header is created, this specifies the header length. If
+ * the header already exists, on return: the actual header length.
+ * @return
+ * ETrue if the header was found, EFalse if the header was created.
+ */
+ {
+ if (ExtHdrGet(aType, aOfs, aLen))
+ return ETrue;
+ ASSERT(aLen && ((aLen & 7) == 0));
+
+ if (iPacket.IsEmpty())
+ {
+ iPacket.AllocL(aLen);
+ }
+ else
+ {
+ iPacket.PrependL(aLen);
+ }
+
+ //Zero out the header
+ iPacket.FillZ(aLen);
+ TInet6Packet<TInet6Options> hdr(iPacket, 0);
+ if (hdr.iHdr == NULL)
+ {
+ iPacket.TrimStart(aLen);
+ User::Leave(KErrGeneral);
+ }
+
+ hdr.iHdr->SetNextHeader(ip6.NextHeader());
+ ip6.SetNextHeader(aType);
+ hdr.iHdr->SetHdrExtLen((aLen >> 3) - 1);
+
+ aOfs = 0;
+ iOffset += aLen;
+ return EFalse;
+ }
+
+EXPORT_C TBool TPacketHead::ExtHdrGetOrAppendL(TInt aType, TInt& aOfs, TInt& aLen)
+ /**
+ * Gets the offset and length of an extension header, or if it doesn't already
+ * exist, creates the header after all other extension headers.
+ *
+ * @param aType Extension header type
+ * @retval aOfs
+ * If the header is created, this specifies the header offset. If
+ * the header already exists, on return, the actual header offset.
+ * @retval aLen
+ * If the header is created, this specifies the header length. If
+ * the header already exists, on return, the actual header length.
+ * @return ETrue if the header was found, EFalse if the header was created
+ */
+ {
+ if (ExtHdrGet(aType, aOfs, aLen)) return ETrue;
+ if (iPacket.IsEmpty()) return ExtHdrGetOrPrependL(aType, aOfs, aLen);
+ ASSERT(aLen && ((aLen & 7) == 0));
+
+ TInt nxt = ip6.NextHeader();
+ TInt ofs = 0;
+ FOREVER
+ {
+ TInet6Packet<TInet6Options> hdr(iPacket, ofs);
+ if (hdr.iHdr == NULL)
+ User::Leave(KErrGeneral);
+
+ nxt = hdr.iHdr->NextHeader();
+ ofs += hdr.iHdr->HeaderLength();
+ if (ofs >= iOffset)
+ {
+ hdr.iHdr->SetNextHeader(aType);
+ break;
+ }
+ }
+
+ RMBufChain tail;
+ tail.AllocL(aLen);
+ tail.FillZ();
+ iPacket.Append(tail);
+
+ TInet6Packet<TInet6Options> hdr(iPacket, iOffset);
+ if (hdr.iHdr == NULL)
+ {
+ iPacket.TrimEnd(iOffset);
+ User::Leave(KErrGeneral);
+ }
+ hdr.iHdr->SetNextHeader(nxt);
+ hdr.iHdr->SetHdrExtLen((aLen >> 3) - 1);
+
+ aOfs = iOffset;
+ iOffset += aLen;
+ return EFalse;
+ }
+
+//
+// TODO: Possibility to replace existing padding with an option which
+// would preserve the alignment. (eg. Home Address is kind of Pad2)
+//
+EXPORT_C void TPacketHead::AddDestinationOptionL(const TUint8* aOption, TUint8 aLen, TUint8 aAlign/*=0*/, TUint8 aModulo/*=4*/)
+ {
+ /**
+ * Adds a Destination Option extension header option.
+ *
+ * If no Destination Option extension header already exists, one is created
+ * as the first extension header. The option is aligned according to the
+ * specified aAlign and aModulo parameters.
+ *
+ * @param aOption
+ * Option Data
+ * @param aLen
+ * Option Data length
+ * @param aAlign
+ * Option alignment requirement. The default is no alignment.
+ * @param aModulo
+ * Option must start at an octect position which is a multiple
+ * of the specified value
+ */
+ TPtrC8 ptr(aOption, aLen);
+ AddDestinationOptionL(ptr, aAlign, aModulo);
+ }
+
+EXPORT_C void TPacketHead::AddDestinationOptionL(const TPtrC8& aOption, TUint8 aAlign/*=0*/, TUint8 aModulo/*=4*/)
+ /**
+ * Adds a Destination Option extension header option.
+ *
+ * If no Destination Option extension header already exists, one is
+ * created as the first extension header. The option is aligned
+ * according to the specified aAlign and aModulo parameters.
+ *
+ * @param aOption
+ * Pointer to the start of the Option Data
+ * @param aAlign
+ * Option alignment requirement. The default is no alignment.
+ * @param aModulo
+ * Option must start at an octect position which is a multiple
+ * of the specified value
+ */
+ {
+ TUint padlen = aAlign?(TUint(aAlign - 2) % aModulo):0; //Leading pad
+ TInt len = 2 + padlen + aOption.Length();
+ TInt trailpad = (8 - (len & 7)) & 7;
+ len += trailpad;
+
+ TInt ofs;
+ if (! ExtHdrGetOrAppendL(KProtocolInet6DestinationOptions, ofs, len))
+ {
+ //Simple case, new header was allocated and initialized with required length.
+ //Just put the option in.
+ ofs += 2;
+#ifdef OPTIMIZE_PADDING
+ if (padlen > 1)
+ {
+ TInet6Packet<TInet6OptionBase> pad(iPacket, ofs);
+ if (pad.iHdr == NULL)
+ User::Leave(KErrGeneral);
+ pad.iHdr->SetType(KDstOptionPadN);
+ pad.iHdr->SetDataLen(padlen - 2);
+ }
+#endif
+ ofs += padlen;
+ iPacket.CopyIn(aOption, ofs);
+#ifdef OPTIMIZE_PADDING
+ ofs += aOption.Length();
+ if (trailpad > 1)
+ {
+ TInet6Packet<TInet6OptionBase> pad(iPacket, ofs);
+ if (pad.iHdr == NULL)
+ User::Leave(KErrGeneral);
+ pad.iHdr->SetType(KDstOptionPadN);
+ pad.iHdr->SetDataLen(trailpad - 2);
+ }
+#endif
+ return;
+ }
+
+ //Now we have the position and size of the header
+ TInet6Packet<TInet6Options> opt(iPacket, ofs);
+ if (opt.iHdr == NULL)
+ User::Leave(KErrGeneral);
+
+ //padlen = leading pad
+ ofs += len;
+ padlen = aAlign?(TUint(aAlign - ofs) % aModulo):0;
+ TInt size = padlen + aOption.Length();
+ trailpad = (8 - (size & 7)) & 7;
+ size += trailpad;
+
+ RMBufChain pkt;
+ pkt.AllocL(size);
+ pkt.FillZ();
+#ifdef OPTIMIZE_PADDING
+ //Optimize leading pad
+ if (padlen > 1)
+ {
+ TInet6Packet<TInet6OptionBase> pad(pkt, 0);
+ if (pad.iHdr == NULL)
+ User::Leave(KErrGeneral);
+ pad.iHdr->SetType(KDstOptionPadN);
+ pad.iHdr->SetDataLen(padlen - 2);
+ }
+ //Optimize trailing pad
+ if (trailpad > 1)
+ {
+ TInet6Packet<TInet6OptionBase> pad(pkt, padlen+aOption.Length());
+ if (pad.iHdr == NULL)
+ User::Leave(KErrGeneral);
+ pad.iHdr->SetType(KDstOptionPadN);
+ pad.iHdr->SetDataLen(trailpad - 2);
+ }
+#endif
+ pkt.CopyIn(aOption, padlen);
+
+ iPacket.Append(pkt);
+
+ iOffset += size;
+ opt.iHdr->SetHdrExtLen(opt.iHdr->HdrExtLen() + (size >> 3));
+ }