networksecurity/ipsec/ipsec6/src/natt_eng6.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 26 Jan 2010 15:23:49 +0200
changeset 0 af10295192d8
permissions -rw-r--r--
Revision: 201004

// 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;
    }