networkingtestandutils/ipv6to4tunnel/src/6to4.cpp
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkingtestandutils/ipv6to4tunnel/src/6to4.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,504 @@
+// 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  
+
+