networkingtestandutils/ipv6to4tunnel/src/6to4.cpp
author hgs
Thu, 08 Jul 2010 15:21:49 +0530
changeset 39 45fd446095e6
parent 0 af10295192d8
permissions -rw-r--r--
201027_01

// 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 "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:
// Name        : 6to4.cpp
// Part of     : 6to4 plugin / 6to4.prt
// Implements 6to4 automatic and configured tunnels, see
// RFC 3056 & RFC 2893
// Version     : 0.2
//




// INCLUDE FILES
#include <inet6log.h>
#include <f32file.h>
#include <icmp6_hdr.h>
#include <ext_hdr.h>
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <es_prot_internal.h>
#endif

#include "6to4.h"
#include "6to4_flow.h"
#include "6to4_listener.h"
#include "6to4_tunnel.h"

// EXTERNAL DATA STRUCTURES
// EXTERNAL FUNCTION PROTOTYPES  
// CONSTANTS
// MACROS
// LOCAL CONSTANTS AND MACROS


// This is a route that is needed for system to pass automatic tunneled 
// packets (2002::/16) to the 6to4 module.
const TIp6Addr K6to4Prefix =
	{ {{0x20, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} };

static const union {TUint8 a[4]; TUint32 b;} v4Prefix = { {0, 0, 0xff, 0xff} };


// MODULE DATA STRUCTURES
// LOCAL FUNCTION PROTOTYPES
// FORWARD DECLARATIONS

// ============================= LOCAL FUNCTIONS ==============================

// ============================ MEMBER FUNCTIONS ==============================

// ----------------------------------------------------------------------------
// CProtocol6to4::CProtocol6to4
// C++ default constructor can NOT contain any code, that
// might leave.
// ----------------------------------------------------------------------------
//
CProtocol6to4::CProtocol6to4 ()
	{
	}

// Destructor
CProtocol6to4::~CProtocol6to4 ()
	{
	delete iConfig;
	delete iEventListener; 
	}

// ----------------------------------------------------------------------------
// CProtocol6to4::InitL
// Initializes the 6to4.
// ----------------------------------------------------------------------------
//
void CProtocol6to4::InitL (TDesC & aTag)
	{
	CProtocolBase::InitL (aTag);

	// There are two configuration files. The first one, 6to4.ini
	// just defines the file name where the tunnels are located.
	// Because the tunnel file is of variable length, it can't
	// be parsed in ordinary Symbian way, but it needs it's own parser.
	LoadConfigurationFile ();
	}

// ----------------------------------------------------------------------------
// CProtocol6to4::LoadConfigurationFile
// Reads first file K6to4IniData, e.g., 6to4.ini, and finds out the file
// where the configured tunnels are defined. Then parses and creates the 
// configured tunnels.
// ----------------------------------------------------------------------------
//
TBool CProtocol6to4::LoadConfigurationFile ()
	{
	if (iConfig)
		return EFalse;            // Already loaded!

	// if iConfigErr != 0 (KErrNone), then an attempt for
	// loading configuration has been made and failed,
	// assume it never will succeed and avoid further
	// attemps on each FindVar...
	if (iConfigErr)
		return EFalse;
	
	LOG (Log::Printf (_L ("CProtocol6to4::LoadConfigurationFile(): %S\r\n"), &K6To4IniData));
	TRAP(iConfigErr, iConfig = CESockIniData::NewL (K6To4IniData));

	if (iConfigErr)
		return EFalse;
		
	if (iConfig)
		{
		TPtrC tunnelIniFile;
		// Read initialization file name, e.g., 6to4_tunnels.ini.
		if (iConfig->FindVar (K6To4IniSectionTunnels, K6To4IniTunnelFile, tunnelIniFile))
			{
			// Parse the 6to4_tunnels.ini (or what was defined in 6to4.ini).
			// NOTE: iConfigErr use must be removed when other definitions
			// than configuded tunnels are possible in K6To4IniData file -tom
			TRAP(iConfigErr, LoadFileL (tunnelIniFile));
			if (iConfigErr)
				return EFalse;
			}
		// Missing K6To4IniSectionTunnels is fine
		}
		return ETrue;
	}

// ----------------------------------------------------------------------------
// CProtocol6to4::NetworkAttachedL
// Binds the hooks (inbound & outbound) to the IP6. Creates virtual interfaces
// for tunneled packets. The automatically tunneled packets are handled by
// virtual interface 6to4, and the configured tunneled packets by virtual
// interface named according to the tunnel name. The compatibility to the 
// TCP/IP stack version is also checked (if not compatible, a log message is 
// printed). Registers a listener for interface IP addresses. That is used for
// filtering purposes. 
// ----------------------------------------------------------------------------
//
void CProtocol6to4::NetworkAttachedL ()
	{
	// Outbound hook
	NetworkService()->BindL (this, BindFlowHook ());

	// Inbound hook, 41 == IPv6 inner
	NetworkService()->BindL (this, BindHookFor (KProtocolInet6Ipip));
	// Inbound hook, 4, == IPv4 inner
	NetworkService()->BindL(this, BindHookFor (KProtocolInetIpip));

	MInterfaceManager *const ifmgr = NetworkService ()->Interfacer();

	ifmgr->AddRouteL (K6to4Prefix, 16, K6to4);
	// Create a new virtual interface "6to4" for the 6to4. This is used for 
	// automatic tunneling. The interface index of this virtual interface  
	// is saved to this object. 
	const MInterface *const m = ifmgr->Interface (K6to4);
	iInterfaceIndex = m->Index ();

	// For each configured tunnel, the same thing is needed. The route is
	// established using configured ip address and prefix information. The
	// virtual interface gets the name according to the configured tunnel name.
	// The virtual interface index is stored to the CTunnel object. 
	TTunnelQueueIter iter (iTunnelParser.TunnelQueue());
	CTunnel *tunnel = NULL;
	while ((tunnel = iter++) != NULL)
		{
		ifmgr-> AddRouteL (tunnel->RouteAddr(),	tunnel->RoutePrefixLength(), tunnel->Name());
		ifmgr-> AddRouteL (tunnel->VirtualIfAddr(), 128, tunnel->Name(), KRouteAdd_MYPREFIX);
		const MInterface *const vif = ifmgr->Interface (tunnel->Name());
		tunnel->SetInterfaceIndex(vif->Index ());
		}

	// The event service was not available, so to make sure it's there, this
	// Check is made. Prints a log message if any problems. 
	CheckCompatibility ();

	if (iEventService)
		{
		// A listener is needed for getting information on ip addresses
		// that are added/deleted to/from certain interfaces. This
		// information is used for filtering incoming packets from
		// malicious senders.
		delete iEventListener;
		iEventListener = NULL;
		iEventListener = new (ELeave) C6to4Listener (NetworkService(), *iEventService);
		}
	}

// ----------------------------------------------------------------------------
// CProtocol6to4::NetworkDetached
// Unbind the hooks.
// ----------------------------------------------------------------------------
//
void CProtocol6to4::NetworkDetached ()
	{
	delete iEventListener;
	iEventListener = NULL;
	}

// ----------------------------------------------------------------------------
// CProtocol6to4::Identify
// Provide identification information to the caller.
// ----------------------------------------------------------------------------
//
void CProtocol6to4::Identify (TServerProtocolDesc & aEntry)
	{
	aEntry.iName = K6to4;
	aEntry.iAddrFamily = KAfInet6;
	aEntry.iSockType = KSockDatagram;
	aEntry.iProtocol = KProtocolInet6Ipip;
	aEntry.iVersion = TVersion (1, 0, 0);
	aEntry.iByteOrder = EBigEndian;
	// aEntry.iServiceInfo=KSIDatagram | KSIConnectionLess;
	aEntry.iServiceInfo =
	  KSIConnectionLess | KSIMessageBased | KSIBroadcast | KSIPeekData |
	  KSIGracefulClose;
	aEntry.iNamingServices = 0;
	aEntry.iSecurity = KSocketNoSecurity;
	aEntry.iMessageSize = 0xffff;
	aEntry.iServiceTypeInfo = EPreferMBufChains | ENeedMBufs;
	aEntry.iNumSockets = KUnlimitedSockets;
	}

void CProtocol6to4::Identify (TServerProtocolDesc * aDesc) const
	{
	Identify (*aDesc);
	}

// ----------------------------------------------------------------------------
// CProtocol6to4::ApplyL
// This is the handler for incoming packets. Detunnel legal packets.
// ----------------------------------------------------------------------------
//
TInt CProtocol6to4::ApplyL (RMBufHookPacket & aPacket, RMBufRecvInfo & aInfo)
	{
	if (aInfo.iIcmp)
		{
		// This is a returned packet inside an ICMP. Could check whether the
		// IP header is the tunneling generated by this packet and do something
		// about it, but for now, just ignore and let someone else deal with it.
		return KIp6Hook_PASS;
		}
	TInet6Packet < TIpHeader > ip (aPacket, aInfo.iOffset);
	if (ip.iHdr == NULL)
		return KIp6Hook_PASS;
	
	// Get inner destination address in "normalized" IPv6 format
	TIp6Addr inner_dst;
	if (aInfo.iProtocol == (TInt)KProtocolInet6Ipip)
		{
		// Inner header IPv6

		// Need to check the mapped length explicitly, because
		// IPv4 min size is less than IPv6 header...
		if (ip.iLength < (TInt)sizeof(TInet6HeaderIP))
			return KIp6Hook_PASS;
		inner_dst = ip.iHdr->ip6.DstAddr();
		}
	else if (aInfo.iProtocol == (TInt)KProtocolInetIpip)
		{
		// Inner header IPv4 (construct IPv4 mapped address)
		inner_dst.u.iAddr32[0] = 0;
		inner_dst.u.iAddr32[1] = 0;
		inner_dst.u.iAddr32[2] = v4Prefix.b;
		inner_dst.u.iAddr32[3] = ip.iHdr->ip4.DstAddrRef();
		}
	else
		return KIp6Hook_PASS;	// Ignore all others.

	// Let those packets through that belong to any configured tunnel 
	TTunnelQueueIter iter (iTunnelParser.TunnelQueue());
	for (;;)
		{
		CTunnel *const tunnel = iter++;
		if (tunnel == NULL)
			{
			// In case not handled by any of the configured tunnels, check if 
			// the address makes it possible to tunnel it automatically.
			if (inner_dst.u.iAddr16[0] == K6to4Prefix.u.iAddr16[0])
				{
				// Set interface index of the packet
				// Should check that inner src is one of my own 6to4 addresses?
				aInfo.iInterfaceIndex = iInterfaceIndex;
				}
			break;
			}
		if (inner_dst.IsEqual (tunnel->VirtualIfAddr()) &&
			TInetAddr::Cast(aInfo.iSrcAddr).Ip6Address().IsEqual(tunnel->EndpointAddr()))
			{
			// A tunnel willing to accept this incoming packet was found.
			// Set also virtual interface index for the packet
			aInfo.iInterfaceIndex = tunnel->InterfaceIndex();
			break;
			}
		}

	// Let the IP layer do the actual detunneling process
	// (peeling off one IP header layer). [This way tunneled
	// fragments get correctly processed, even if inner IP was
	// IPv4 -- not currently the case here, inner is always IPv6.
	// IPv6 fragments would work even if the outer IP layer
	// was peeled off here].
	return KIp6Hook_PASS;
	};

// ----------------------------------------------------------------------------
// CProtocol6to4::CheckCompatibility
// Checks if the MEventService API is implemented. If not, 6to4 is not 
// compatible with the TCP/IP stack version and log message is printed (and
// 6to4 shouldn't be used).
// ----------------------------------------------------------------------------
//
void CProtocol6to4::CheckCompatibility ()
	{
	MInterfaceManager *ifacer = NetworkService ()->Interfacer ();
	TInt err;

	TRAP (err, iEventService = IMPORT_API_L (ifacer, MEventService));
#ifdef _LOG
	if (err != KErrNone)
		LOG (Log::Printf (_L
			 ("MEventService not available. Some features may be missing\n")));
#endif
	}

// ----------------------------------------------------------------------------
// CProtocol6to4::LoadFileL
// Loads and parses a tunnel configuration file.
// ----------------------------------------------------------------------------
//
TInt CProtocol6to4::LoadFileL (const TDesC & aFile)
	{
	// Connect to the file server and open the tunnel configuration
	// file (e.g., 6to4_tunnels.ini or what configured in 6to4.ini).
	TAutoClose < RFs > fs;
	User::LeaveIfError (fs.iObj.Connect ());
	fs.PushL ();

	TAutoClose < RFile > file;
	User::LeaveIfError (file.iObj.Open (fs.iObj, aFile, EFileRead));
	file.PushL ();

	// Read file to a buffer
	TInt size;
	User::LeaveIfError (file.iObj.Size (size));
	HBufC8 *tmp_buf = HBufC8::NewL (size);

	CleanupStack::PushL (tmp_buf);

	TPtr8 tmp_ptr (tmp_buf->Des ());
	User::LeaveIfError (file.iObj.Read (tmp_ptr));
	HBufC16 *file_data = HBufC16::NewL (size);
	file_data->Des ().Copy (tmp_buf->Des ());
	TPtr16 ptr (file_data->Des ());

	CleanupStack::PushL (file_data);

	// Parse tunnels from the buffer.
	TInt ret = iTunnelParser.ParseL (ptr);

	if (ret != KErrNone)
		{
		LOG (Log::
			 Printf (_L
					 ("CProtocol6to4::LoadFileL [%S]: Error [%d] in tunnel conf file\r\n"),
					 &aFile, ret));
		}

	CleanupStack::Pop ();
	CleanupStack::Pop ();
	file.Pop ();
	fs.Pop ();

	delete file_data;
	delete tmp_buf;

	return KErrNone;
	}

// ----------------------------------------------------------------------------
// CProtocol6to4:OpenL
// Outbound Open handler for the protocol. Checks first if the packet belongs 
// to one of the tunnels. If so, creates and configures a 6to4 local flow 
// object. Sets the
// destination IP address according to the IPv4 address of the outer (IPv4)
// header. Clears the source IP address of the packet and sets the IP version
// to 4. Marks the next header to be IPv6. Returns the newly created flow.
// ----------------------------------------------------------------------------
//
MFlowHook *CProtocol6to4::OpenL (TPacketHead & aHead, CFlowContext * aFlow)
	{
	TIp6Addr & dst = aHead.ip6.DstAddr ();
	TIp6Addr & src = aHead.ip6.SrcAddr ();
	TIpAddress outerDst;

	// Check first if the flow belongs to one of the configured tunnels
	TTunnelQueueIter iter (iTunnelParser.TunnelQueue());
	for (;;)
		{
		CTunnel *const tunnel = iter++;
		if (tunnel == NULL)
			{
			// Packet didn't belong to configured tunnels, try automatic
			// tunneling
			if (dst.u.iAddr16[0] != K6to4Prefix.u.iAddr16[0])
				return NULL;    // No configured tunnel or a 6to4 address

			// Apply the 6to4 conversion for the address 
			// IPv4 format

			outerDst.u.iAddr32[0] = 0;
			outerDst.u.iAddr32[1] = 0;
			outerDst.u.iAddr32[2] = v4Prefix.b;
			outerDst.u.iAddr8[12] = dst.u.iAddr8[2];
			outerDst.u.iAddr8[13] = dst.u.iAddr8[3];
			outerDst.u.iAddr8[14] = dst.u.iAddr8[4];
			outerDst.u.iAddr8[15] = dst.u.iAddr8[5];

			outerDst.iScope = 0;
			break;
			}
		if (aHead.iInterfaceIndex == tunnel->InterfaceIndex())
			{
			// Found a configured tunnel
			// Use configured endpoint address as the outer destination ipv4 address
			outerDst = tunnel->EndpointAddr();
			break;
			}
		}
	// In current system, the (inner) source is always defined at this point
	// (cancel flow if it is not!)
	if (!aHead.iSourceSet)
		User::Leave(KErrNotReady);
	
	// Must generate a value for inner hoplimit. For this, use the socket
	// option to retrieve the currently defined value. The default is
	// different for multicast and unicast, so need to test the inner address
	// [Note: Really, the stack should be doing this for me, but ... -- msa]
	const TBool is_multicast =
		dst.IsMulticast() ||
		 (dst.u.iAddr32[0] == 0 &&
		  dst.u.iAddr32[1] == 0 &&
		  dst.u.iAddr32[2] == v4Prefix.b &&
		  (dst.u.iAddr8[12] & 0xF0) == 0xE0);
	TPckgBuf<TInt> opt;
	(void)aFlow->GetOption(KSolInetIp, is_multicast ? KSoIp6MulticastHops : KSoIp6UnicastHops, opt);
	aHead.ip6.SetHopLimit(opt());

	// We are interested in this flow, let's create a new local flow 
	// instance
	C6to4FlowInfo *info = new (ELeave) C6to4FlowInfo(aHead);


	// Next header is going to be 4 or 41
	aHead.iProtocol = (TUint8)(dst.IsV4Mapped() ? KProtocolInetIpip : KProtocolInet6Ipip);
	aHead.ip6.SetNextHeader (aHead.iProtocol);
	aHead.ip6.SetHopLimit(0);

	dst = outerDst.Address();
	aHead.iDstId = outerDst.iScope;

	// Clean the source IP address from the packet. Flow sets it 
	// automatically before the ReadyL() is called.
	aHead.iSourceSet = 0;
	src = KInet6AddrNone;
	aHead.iSrcId = 0;

	if (dst.IsV4Mapped())
		{
		aHead.ip6.SetVersion (4);
		// Update header size overhead
		aFlow->iHdrSize += 20;
		}
	else
		{
		aHead.ip6.SetVersion (6);
		// Update header size overhead
		aFlow->iHdrSize += sizeof(TInet6HeaderIP);
		}
	aHead.iIcmpType = 0;
	aHead.iIcmpCode = 0;
	aHead.iSrcPort = 0;
	aHead.iDstPort = 0;

#if TPACKETHEAD_FRAGMENT
	// Tunneled packets should be fragmented before tunneling
	aHead.iFragment = 1;
#endif
	return info;
	}

// ========================== OTHER EXPORTED FUNCTIONS ========================

//  End of File