networksecurity/ipsec/ipsec6/src/natt_eng6.cpp
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networksecurity/ipsec/ipsec6/src/natt_eng6.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,324 @@
+// 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:
+// natt_eng6.cpp - Nokia VPN specific UDP encapsulating
+// natt_eng.cpp - Nokia VPN specific and IETF specified UDP encapsulating of ESP packet
+// This class takes care of Nokia VPN specific and IETF specified UDP encapsulating
+// ESP UDP capsulating means the following tunnel mode ESP packet encapsulation. 
+// | IPV4 HDR  | UDP |  ESP    |  IP HDR  !    DATA    !  EPS   !  ESP  !
+// |   New     | Hdr |  Header | Original !            ! Trailer!  AUTH !
+// |<---------- encrypted --------->| 
+// |<------------- authenticated ------------>|
+// where UDP port number is configurable
+// For example:
+// -- IETF specified UDP encapsulation: Port 4500
+// -- The "native" Nokia specified UDP encapsulation: Port 9872
+// -- Policy specified "raw" UDP encapsulation: Port xxxx
+// This kind of encapsulation is required for handling IPSEC traffic
+// in networks with NAT devices.
+// OBS: UDP header does not effect to the byte counts in SA object !
+// Nokia VPN specific UDP encapsulating of ESP packet
+// class
+//
+
+#include "ip6_hdr.h"
+#include "ext_hdr.h"
+#include "in_chk.h"
+#include "udp_hdr.h"
+#include "sadb.h"
+#include <networking/ipsecerr.h>
+#include "natt_eng.h"
+
+//
+//  GetNextheaderL
+//  **********
+//  Check that current packet is IPV4 packet and return IP header length
+//  and pointer to next_header field
+//
+//  An outgoing packet MUTS be in the following format when it arrives to
+//  NATT_ENG
+//    +-----------+---------+----------+------------+--------+-------+
+//    | IPV4 HDR  |  ESP    |  IP HDR  !    DATA   
+//    |   Outer   |  Header | Original !            ! Trailer!  AUTH !
+//    +-----------+---------+----------+------------+--------+-------+
+//                          |<---------- encrypted --------->| 
+//                |<------------- authenticated ------------>|
+//
+//
+//  The packet is not modified by this
+//
+static TUint8 *GetNextheaderL(RMBufPacketBase &aPacket, TInt &aLength)
+    {
+    TPacketPoker pkt(aPacket);
+    TInet6HeaderIP4 *ip;
+    TUint8 *next_header = NULL;
+
+    //
+    // There must be at least the first RMBuf, containing the IPv6 or IPv4
+    // header in full! "Autodetect" between IPv4 and IPv6
+    //
+    ip = (TInet6HeaderIP4 *)pkt.ReferenceL(TInet6HeaderIP4::MinHeaderLength());
+    if (ip->Version() == 4) {
+       next_header = (TUint8 *)ip + TInet6HeaderIP4::O_Protocol;
+       aLength     = ip->HeaderLength();
+    }
+
+    return next_header;
+    }
+
+//
+//  TIpsecNATT::Overhead
+//  *******************
+//  Return maximum possible overhead caused by this ESP SA
+//
+TInt TIpsecNATT::Overhead() const
+    {
+    
+    return sizeof(TInet6HeaderUDP); 
+    }
+
+//
+//  TIpsecNATT::ApplyL
+//  *****************
+//  NAT traversal processing of the outbound packet (UDP encapsuation)
+//
+//  This is part of IPv6 outgoing packet hook processing.
+//  -   aHead parameter is present in case processing needs
+//      some additional information from it (not all hooks
+//      have need for it)
+//  -   aPacket is the outgoing packet. It is *FULL* packet
+//      and already includes the outgoing IPv6 header. If
+//      the hook needs to do any modifications to the outgoing
+//      packet, it must make them into aPacket.
+//  -   aPacket has an info block associated. The info->iLength
+//      *MUST* be maintained, if any change affects it.
+//
+//  Returns (as specified for the hook mechanism)
+//  -   Any Leave causes the packet to be dropped (hook must
+//      take care not cause a leave, if it frees that packet
+//      for < 0 return).
+//  -   return = 0, pass on to the next output hook
+//  -   return < 0, the hook took "ownership" of the packet (usually
+//      just dropped it explicitly), "Send aborted" return!
+//  -   return > 0, This return value is not used at this
+//      point. Do not return with > 0!
+//
+TInt TIpsecNATT::ApplyL(CNatTraversal* aNatTraversal, RMBufSendPacket &aPacket, RMBufSendInfo &aInfo)
+    {
+    RMBufChain payload;         
+    TInt result = KErrNone;
+
+    if ( !aNatTraversal ) {
+       return result;     // No NAT traversal required  
+    }
+
+    TInt ipv4hlen;
+    TUint8 *next_header = GetNextheaderL(aPacket, ipv4hlen);
+    if ( !next_header ) {
+       return KErrNone;   // Not A IPV4 packet. NAT-Traversal not required
+    }   
+    aPacket.SplitL(ipv4hlen, payload); // <-- Must have "split > 0" due SplitL "feature"!! -- msa
+    for (;;)        // Only for easy exits...
+        {
+        //
+        // Compute the required extensions to UDP header
+        //
+        const TInt udphlen = TInet6HeaderUDP::MinHeaderLength();
+
+        TRAP(result, if (payload.IsEmpty()) payload.AllocL(udphlen); else payload.PrependL(udphlen));
+        if (result != KErrNone)
+            break;
+        aInfo.iLength += udphlen;
+        
+        TInet6Packet<TInet6HeaderUDP> udp;
+        udp.Set(payload, 0, udphlen);
+        if (udp.iHdr == NULL) {
+           result = EIpsec_RMBUF;
+           break;
+        }
+        //
+        // Fill NAT Traversal UDP header fields
+        // UDP checksum is NOT used, zero value shall be used
+        // 
+        udp.iHdr->SetDstPort(aNatTraversal->GetDestPort());
+        udp.iHdr->SetSrcPort(aNatTraversal->GetSrcPort());
+        udp.iHdr->SetLength(aInfo.iLength - ipv4hlen);          
+        udp.iHdr->SetChecksum(0);     /* No UDP checksum used */
+        //
+        // Update the next header chaining (in IP header)
+        //
+        *next_header = KProtocolInetUdp;
+        
+        TInet6Checksum<TInet6HeaderIP4> ip(aPacket);
+        if (ip.iHdr == NULL) {
+            result = EIpsec_RMBUF;
+            break;
+        }
+    /**--------------------------------------------------------------------------------
+     *
+     * Change destination IP address in IP header if defined so in NAT traversal object
+     * This means that the peer station of SA is behind a NAT device
+     *
+     *-------------------------------------------------------------------------------*/
+        if ( aNatTraversal->UseDestIPAddr() ) {
+           TInetAddr* DestIPaddr = aNatTraversal->GetDestIPAddr();
+           ip.iHdr->SetDstAddr(DestIPaddr->Address());
+           TInetAddr::Cast(aInfo.iDstAddr).SetAddress(DestIPaddr->Address());
+        }
+        ip.iHdr->SetTotalLength(aInfo.iLength);             
+        ip.ComputeChecksum();  //  Compute New IPV4 header checksum     
+        
+        break;      // -- NO REAL LOOP, BREAK ALWAYS --
+        }
+    
+    aPacket.Append(payload);
+    return result;
+    }
+
+//
+//  TIpsecNATT::ApplyL
+//  *****************
+//      Unwrap possible NAT traversal UDP header  from a received packet.
+//
+//  This is part of the IPv6 incoming packet hook system, which means
+//  -   updating info is not required (actually should not be done),
+//      *UNLESS* the aHead.iPacket itself is modified! (info->iLength
+//      must always contain the length of the aHead.iPacket).
+//
+//  *   TIpsecNATT::ApplyL will change the info->iLength, if UDP is
+//  *   removed from the aHead.iPacket!
+//
+//  -   applied changes are made to the TPacketHead
+//  -   the current "scanning point" in the aHead.iPacket is indicated
+//      by the aHead.iOffset. The remaining "unscanned" packet length
+//      is info->iLength - aHead.iOffset.
+//  -   when a hook desides to advance in the aHead.iPacket (or if it
+//      changes the IP extension/upper layer header at iOffset), it
+//      must return the protocol id of the new header (it must not
+//      change the next header field of the aHead.ip6). This is done
+//      in the main loop and at entry this contains the protocol id
+//      of the current header indicated by the iOffset!
+//
+//
+//  *NOTE*
+//      A minor difference to the Hook semantics: < 0 return does not
+//      indicate the packet has been freed, but it is indication to
+//      the caller that this should be done.
+//
+//  Returns
+//  -   any Leave causes packet to be dropped
+//  -   return < 0, cause the packet to be dropped
+//  -   return = 0, pass the packet to the next hook of the same protocol
+//  -   return > 0, hook changed the iOffset/iPacket, topmost protocol
+//      has been changed
+//
+//
+TInt TIpsecNATT::ApplyL(CSecurityAssoc* &aSa, RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo)
+    {
+    RMBufChain payload;
+    TInt result = EIpsec_NotANATTPacket;  /* default */
+
+    //
+    // After SplitL plen MUST always be same as payload.Length()!
+    //
+    TInt plen = aInfo.iLength - aInfo.iOffset;
+    
+    //
+    // aHead.iOffset points to the beginning of the UDP. UDP needs to
+    // deleted from the middle, thus Split is required in any case.
+    //
+    aPacket.SplitL(aInfo.iOffset, payload); // <-- Must have "aInfo.iOffset > 0" due SplitL "feature"!! -- msa
+	if (payload.IsEmpty())
+		return result;	// Play safe, and just say "NO NAT"
+    
+    for (;;)        // Just for simple exits
+        {
+
+        TInet6Packet<TInet6HeaderUDP> udp(payload);
+        if (udp.iHdr == NULL)
+        {
+           result = EIpsec_CorruptPacketIn;         
+           break;
+        }
+        //
+        // Copy possible SPI data behind UDP header 
+        // If packet is a UDP encapsulated ESP packet the header is format UDP+ESP.
+        // The SPI value shall be found from the begin of ESP header (SPI length 4 octets)
+        //
+        //
+        const TInt udphlen = udp.iHdr->HeaderLength();
+        if ( plen < (TInt)(udphlen + sizeof(TInet6HeaderESP)) )
+        {
+            break; // NOT A NAT TRAVERSAL UDP PACKET                 
+        }            
+
+        TUint32 spi;
+        TPtr8 spiptr((TUint8*)&spi, 4, 4);
+        payload.CopyOut(spiptr, udphlen);
+        if ( spiptr.Length() != 4 )
+        {
+            break; // NOT A NAT TRAVERSAL UDP PACKET                 
+        }
+    /**--------------------------------------------------------------------------
+     *
+     * Check is the UDP packet destinated to the NAT Traversal port:
+     *  - Take SPI value from ESP header and find corresponding IPSEC SA
+     *  - If SA contains CNatTraversal object instance check that current UDP
+     *    destination port corresponds source port defined in there.
+     *  - If UDP port corresponds packet is handled as a UDP capsulated ESP packet.
+     *    Otherwise UDP packet is a "normal" UDP packet
+     * Special rule:
+     * SPI value 0x00000000 is a "non ESP marker" which means that is NOT an
+     * UDP capsulated ESP packet (it is a IKE message).
+     *
+     *---------------------------------------------------------------------------*/
+        if ( spi == NON_ESP_MARKER ) {  
+           break;  // NOT A NAT TRAVERSAL UDP PACKET                 
+        }
+        aSa = iManager->Lookup(SADB_SATYPE_ESP, spi, TIpAddress(aInfo.iDstAddr));       
+        if ( !aSa ) {
+           break;  // NOT A NAT TRAVERSAL UDP PACKET 
+        }
+        
+        CNatTraversal* NatTraversal = aSa->NatTraversal();
+        if ( !NatTraversal || (NatTraversal->GetSrcPort() != udp.iHdr->DstPort()) ) {
+           break;  // NOT A NAT TRAVERSAL UDP PACKET         
+        }   
+        //
+        // The packet is an UDP encapsulated ESP packet.
+        // Remove UDP header from packet.
+        // (NAT-T processing does not change the iPrevNextHdr offset value!)
+        //
+        result = KProtocolInetEsp;
+        aPacket.CopyIn(TPtrC8((TUint8 *)&result, 1), aInfo.iPrevNextHdr);
+        
+        payload.TrimStart(udphlen);
+        aInfo.iLength -= udphlen;
+        //
+        // The real packet length has been modified, modify the original outermost
+        // payload length in the IPv6 header (only for the case where an ICMP error
+        // might be generated from this packet!)
+        //
+        TInet6Packet<TInet6HeaderIP4> ip(aPacket);
+        if (ip.iHdr->Version() == 4)
+            ip.iHdr->SetTotalLength(aInfo.iLength);
+            // IPv4 header checksum not recomputed... 
+        else
+            ((TInet6HeaderIP *)ip.iHdr)->SetPayloadLength(aInfo.iLength - sizeof(TInet6HeaderIP));      
+        break;      // -- NO REAL LOOP, BREAK ALWAYS --
+        
+        }
+    aPacket.Append(payload);
+    
+    return result;
+    }