⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nat.c

📁 代码在ti的c67系列单片机上实现了完整的TCPIP协议栈
💻 C
字号:
//--------------------------------------------------------------------------
// Ip Stack
//--------------------------------------------------------------------------
// NAT.C
//
// Network Address Translation
//
// Author: Michael A. Denio
// Copyright 1999, 2000 by Texas Instruments Inc.
//-------------------------------------------------------------------------
#include <stkmain.h>
#ifndef _INCLUDE_NAT_CODE
void NatMsg( uint Msg ) { (void)Msg; }
#else
#include "nat.h"

// Head of linked lists for TIME, IN, and LOCAL
static NATENTRY *pneTIME;
static NATENTRY *pneMAPPED[32];
static NATENTRY *pneLOCAL[32];
static uint     EphemUsed;               // Number of ephemeral ports used

// Next port to use in mapping
static UINT16  PortNextMapped = SOCK_RESPORT_FIRST;

static void  NatTimeoutCheck();
static void  NatFlush();

// Hash with 3 groups of 5 bits each (approximately)
#define HASHPORT(a) (((a>>10)+(a>>5)+a)&0x1f)

//--------------------------------------------------------------------
// NatMsg()
//
// Sevices initialization, resource and timer messages
//--------------------------------------------------------------------
void NatMsg( uint Msg )
{
    static HANDLE hTimer;
    int           i;

    switch( Msg )
    {
    // System Initialization
    case MSG_EXEC_SYSTEM_INIT:
        // The odd timer period helps stagger it from other timers
        hTimer = TimerNew( &NatMsg, TIMER_TICKS_NAT, MSG_NAT_TIMER );
        mmZeroInit( &nats, sizeof(NATSTATS) );
        pneTIME = 0;
        for(i=0; i<32; i++)
        {
            pneMAPPED[i]  = 0;
            pneLOCAL[i] = 0;
        }
        EphemUsed = 0;
        break;

    // System Shutdown
    case MSG_EXEC_SYSTEM_SHUTDOWN:
        NatFlush();
        if( hTimer )
            TimerFree( hTimer );
        break;

    // Low Resoruces
    case MSG_EXEC_LOW_RESOURCES:
        NatFlush();
        break;

    // Long Term Timer
    case MSG_NAT_TIMER:
        NatTimeoutCheck();
        break;
    }
}

//--------------------------------------------------------------------
// NatFlush - Flush all NAT entries
//--------------------------------------------------------------------
static void NatFlush()
{
    // Kill Everything
    while( pneTIME )
    {
        NatFree( pneTIME );
    }
}

//--------------------------------------------------------------------
// NatTimeoutCheck - Check for Timed-out Entry
//--------------------------------------------------------------------
static void NatTimeoutCheck()
{
    NATENTRY *pne,*pneTmp;
    UINT32  TimeNow;

    TimeNow = llTimerGetTime(0);

    pne = pneTIME;
    while( pne )
    {
        // Get next entry
        pneTmp = pne->pNextTIME;

        // Check for expired entry
        if( pne->NI.Timeout <= TimeNow )
        {
            // Free this entry
            NatFree( pne );
        }

        // Setup for next loop
        pne = pneTmp;
    }
}

//--------------------------------------------------------------------
// NatNew - Create a NAT entry
//
// IPLocal      - IP of Local Machine
// PortLocal    - Port of Local Machine
// IPForeign    - IP of (Foreign) Peer
// PortForeign  - Port of (Foreign) Peer
// Protocol     - Protocol
// PortMapped   - Public Port (0=Reserved Ephemeral)
// Timeout      - Timeout is seconds (0=Static Entry)
//
//--------------------------------------------------------------------
HANDLE NatNew( IPN IPLocal, UINT16 PortLocal,
               IPN IPForeign, UINT16 PortForeign,
               UINT8 Protocol, UINT16 PortMapped,
               UINT32 Timeout )
{
    NATENTRY *pne;
    UINT16   Index;
    uint     Flags = 0;

    // Check for illegal arguments
    if( !PortLocal || !IPLocal )
        return(0);

    // Check for artificial limit on NAT entries
    if( nats.dwEntries >= NAT_MAXENTRIES )
        return(0);

    // If this is going to be an Ephemeral mapping, make sure
    // we have a free port
    if( !PortMapped || (PortMapped >= SOCK_RESPORT_FIRST &&
                        PortMapped <= SOCK_RESPORT_LAST) )
    {
        // If too many ephem used, error out
        if( EphemUsed > (SOCK_RESPORT_LAST-SOCK_RESPORT_FIRST) )
            return(0);
        EphemUsed++;
        Flags |= NI_FLG_RESERVED;
    }

    if( !(pne = mmAlloc(sizeof(NATENTRY))) )
    {
        DbgPrintf(DBG_WARN,"NatNew: OOM");
        ExecLowResource();
        return(0);
    }

    // Initialize type and flags
    pne->Type     = HTYPE_NAT;
    pne->Flags    = Flags;

    if( PortMapped )
    {
        if( !IPForeign || !PortForeign )
            pne->Flags |= NI_FLG_WILD;
    }
    // Else use ephemeral port - find one now
    else
    {
        do
        {
            PortMapped = PortNextMapped;
            if( ++PortNextMapped > SOCK_RESPORT_LAST )
                PortNextMapped = SOCK_RESPORT_FIRST;
        } while( NatFindPNI( 0, 0, 0, 0, 0, PortMapped ) );
    }

    // Set PNI information
    pne->NI.TcpState    = NI_TCP_CLOSED;
    pne->NI.IPLocal     = IPLocal;
    pne->NI.PortLocal   = PortLocal;
    pne->NI.IPForeign   = IPForeign;
    pne->NI.PortForeign = PortForeign;
    pne->NI.Protocol    = Protocol;
    pne->NI.PortMapped  = PortMapped;
    pne->NI.Timeout     = llTimerGetTime(0) + Timeout;
    pne->NI.hProxyEntry = 0;

    // Flag static entries
    if( !Timeout )
        pne->Flags |= NI_FLG_STATIC;

    // Init forward pointers
    pne->pNextTIME   = 0;
    pne->pNextMAPPED = 0;
    pne->pNextLOCAL  = 0;

    // If static, clear the timeout list
    // Else add it to the timeout list
    if( pne->Flags & NI_FLG_STATIC )
        pne->ppPrevTIME = 0;
    else
    {
        // If there's a "next" entry, point to it and point it
        // back to us
        if( pneTIME )
        {
            pne->pNextTIME             = pneTIME;
            pne->pNextTIME->ppPrevTIME = &pne->pNextTIME;
        }

        // Put us at head of the TIME list
        pneTIME = pne;
        pne->ppPrevTIME = &pneTIME;
    }

    // Hash for LOCAL
    Index = HASHPORT(PortLocal);

    // If there's a "next" entry, point to it and point it
    // back to us
    if( pneLOCAL[Index] )
    {
        pne->pNextLOCAL            = pneLOCAL[Index];
        pne->pNextLOCAL->ppPrevLOCAL = &pne->pNextLOCAL;
    }

    // Put us at head of the LOCAL list
    pneLOCAL[Index]  = pne;
    pne->ppPrevLOCAL = &pneLOCAL[Index];

    // Hash for MAPPED
    Index = HASHPORT(PortMapped);

    // If there's a "next" entry, point to it and point it
    // back to us
    if( pneMAPPED[Index] )
    {
        pne->pNextMAPPED               = pneMAPPED[Index];
        pne->pNextMAPPED->ppPrevMAPPED = &pne->pNextMAPPED;
    }

    // Put us at head of the MAPPED list
    pneMAPPED[Index]  = pne;
    pne->ppPrevMAPPED = &pneMAPPED[Index];

    // Keep stats
    nats.dwEntries++;
    if( nats.dwEntries > nats.dwMaxEntries )
        nats.dwMaxEntries = nats.dwEntries;

    return( pne );
}

//--------------------------------------------------------------------
// NatFree - Free a NAT entry
//--------------------------------------------------------------------
void NatFree( HANDLE hNat )
{
    NATENTRY *pne = (NATENTRY *)hNat;

#ifdef _STRONG_CHECKING
    if( pne->Type != HTYPE_NAT )
    {
        DbgPrintf(DBG_ERROR,"NatFree: HTYPE %04x",pne->Type);
        return;
    }
#endif

    // If entry contains a proxy, signal its destruction
    if( pne->NI.hProxyEntry )
        ProxyEnable( &pne->NI, 0 );

    // Remove Entry from all three linked lists
    if( pne->ppPrevTIME )
    {
        *pne->ppPrevTIME = pne->pNextTIME;
        if( pne->pNextTIME )
            pne->pNextTIME->ppPrevTIME = pne->ppPrevTIME;
    }
    *pne->ppPrevMAPPED   = pne->pNextMAPPED;
    if( pne->pNextMAPPED )
        pne->pNextMAPPED->ppPrevMAPPED = pne->ppPrevMAPPED;
    *pne->ppPrevLOCAL  = pne->pNextLOCAL;
    if( pne->pNextLOCAL )
        pne->pNextLOCAL->ppPrevLOCAL = pne->ppPrevLOCAL;

    // If used an ephemeral port, track it
    if( pne->Flags & NI_FLG_RESERVED )
        EphemUsed--;

    // If this entry had a proxy entry, free it
    if( pne->NI.hProxyEntry )
        ProxyEntryFree( pne->NI.hProxyEntry );

    // If in the long-term TCP state, update the stats
    if( pne->NI.TcpState == NI_TCP_ESTAB )
        nats.dwLongTerm--;

    // Update normal stats
    nats.dwEntries--;

    // Zap it
    pne->Type = 0;
    mmFree( pne );
}

//
// NatGetPNI - Return PNI from Nat Handle
//
NATINFO *NatGetPNI( HANDLE hNat )
{
    NATENTRY *pne = (NATENTRY *)hNat;

#ifdef _STRONG_CHECKING
    if( pne->Type != HTYPE_NAT )
    {
        DbgPrintf(DBG_ERROR,"NatGetPNI: HTYPE %04x",pne->Type);
        return(0);
    }
#endif

    return( &pne->NI );
}

//
// NatFindPNI - Find PNI
//
// In RX mode, IPLocal and PortLocal are NULL
// In TX mode, PortMapped is NULL
//
// *** Any non-Zero parameter must match ***
//
NATINFO *NatFindPNI( IPN IPLocal, UINT16 PortLocal,
                     IPN IPForeign, UINT16 PortForeign,
                     UINT8 Protocol, UINT16 PortMapped )
{
    NATENTRY *pne,*pneWild = 0;
    int      UseProxy;
    UINT16   Index;
    HANDLE   hProxyEntry;

    //
    // All non-zero entries must match.
    //
    // "PortForeign" can be NULL. This occurs when a
    // non-proxied entry is created before the foreign port
    // is known. Since it is non-proxied, it does not need
    // to be unique.
    //

    // Load a linked list by HASHing PortMapped or PortLocal
    if( PortMapped )
    {
        Index = HASHPORT(PortMapped);
        pne = pneMAPPED[Index];

        while( pne )
        {
            // If PortMapped is non-Zero, match on PortMapped,
            // Protocol, IPForeign and PortForeign
            if( (!PortMapped  || PortMapped == pne->NI.PortMapped) &&
                (!Protocol    || Protocol   == pne->NI.Protocol) &&
                (!IPForeign   || !pne->NI.IPForeign ||
                                 IPForeign  == pne->NI.IPForeign) &&
                (!PortForeign || !pne->NI.PortForeign ||
                                 PortForeign == pne->NI.PortForeign) )
            {
                // If this is not a WILD, we match
                if( !(pne->Flags & NI_FLG_WILD) )
                    return( &pne->NI );
                // Else if we don't need an exact match we break
                else if( !IPForeign || !PortForeign )
                    return( &pne->NI );

                // For now, just remember we had a WC match
                pneWild = pne;
            }
            pne = pne->pNextMAPPED;
        }

    }
    else if( PortLocal )
    {
        Index = HASHPORT(PortLocal);
        pne = pneLOCAL[Index];

        while( pne )
        {
            // If PortLocal is non-Zero, match on PortLocal,
            // Protocol, IPForeign and PortForeign
            if( (!IPLocal    || IPLocal    == pne->NI.IPLocal) &&
                (!PortLocal  || PortLocal  == pne->NI.PortLocal) &&
                (!Protocol   || Protocol   == pne->NI.Protocol) &&
                (!IPForeign   || !pne->NI.IPForeign ||
                                 IPForeign  == pne->NI.IPForeign) &&
                (!PortForeign || !pne->NI.PortForeign ||
                                 PortForeign == pne->NI.PortForeign) )
            {
                // If this is not a WILD, we match
                if( !(pne->Flags & NI_FLG_WILD) )
                    return( &pne->NI );
                // Else if we don't need an exact match we break
                else if( !IPForeign || !PortForeign )
                    return( &pne->NI );

                // For now, just remember we had a WC match
                pneWild = pne;
            }
            pne = pne->pNextLOCAL;
        }
    }
    else
        return(0);

    //
    // Handle a WildCard Match
    //
    if( pneWild )
    {
        //
        // Since this entry is WILD and both IPForeign and PortForeign
        // are valid, then we spawn a new NATENTRY to handle this
        // connection. This is done so that the TCP state detection
        // remains valid. The parent entry is allowed to expire.
        //

        // Bump the timeout of the "parent"
        pne->NI.Timeout = NAT_IDLE_SECONDS;
        pne = (NATENTRY *) NatNew( pneWild->NI.IPLocal,
                                   pneWild->NI.PortLocal,
                                   IPForeign, PortForeign,
                                   pneWild->NI.Protocol,
                                   pneWild->NI.PortMapped,
                                   NAT_IDLE_SECONDS );
        if( !pne )
            return(0);
        return( &pne->NI );
    }

    //
    // We haven't found an entry yet. Try the installed Proxy entries
    //

    // If no protocol specified, we can't continue
    if( !Protocol )
        return(0);

    // We handle Rx and Tx a little different
    if( PortMapped )
    {
        // Attempt a proxy spawn on the destination port
        // On success the local IP destination is returned in IPLocal
        UseProxy = ProxyEntrySpawn( NAT_MODE_RX, Protocol, PortMapped,
                                    &hProxyEntry, &IPLocal );

        // On RX type proxies, the local port is the Mapped port.
        // Only IP address is altered.
        if( UseProxy )
            PortLocal = PortMapped;
    }
    else
    {
        // Attempt a proxy spawn on the destination port
        UseProxy = ProxyEntrySpawn( NAT_MODE_TX, Protocol, PortForeign,
                                    &hProxyEntry, 0 );
    }

    // If we got a proxy, create a NAT entry for it
    if( UseProxy )
    {
        pne = (NATENTRY *)NatNew( IPLocal, PortLocal,
                                  IPForeign, PortForeign,
                                  Protocol, PortMapped, NAT_IDLE_SECONDS );

        // Free Proxy entry if NAT entry not created
        if( !pne )
        {
            ProxyEntryFree( hProxyEntry );
            return( 0 );
        }

        pne->NI.hProxyEntry = hProxyEntry;

        // Signal creation of the entry
        if( !ProxyEnable( &pne->NI, 1 ) )
        {
            // The user rejected the entry
            ProxyEntryFree( hProxyEntry );
            pne->NI.hProxyEntry = 0;
            NatFree( pne );
            return(0);
        }

        return( &pne->NI );
    }

    // Here there was no Proxy. If PortMapped is NULL, this is an
    // outgoing packet. Thus we autocreate a PNI. This is for simple
    // non-proxied NAT.
    if( !PortMapped )
    {
        pne = (NATENTRY *)NatNew( IPLocal, PortLocal,
                                  IPForeign, PortForeign,
                                  Protocol, 0, NAT_IDLE_SECONDS );
        if( pne )
            return( &pne->NI );
    }
    return(0);
}


#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -