/*
 * FreeRTOS+TCP V3.1.0
 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://aws.amazon.com/freertos
 * http://www.FreeRTOS.org
 */

#include <stdlib.h>

/* WinPCap includes. */
#define HAVE_REMOTE
#include "pcap.h"

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_IP_Private.h"
#include "NetworkBufferManagement.h"

/* Thread-safe circular buffers are being used to pass data to and from the PCAP
 * access functions. */
#include "Win32-Extensions.h"
#include "FreeRTOS_Stream_Buffer.h"

/* Sizes of the thread safe circular buffers used to pass data to and from the
 * WinPCAP Windows threads. */
#define xSEND_BUFFER_SIZE    32768
#define xRECV_BUFFER_SIZE    32768

/* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
 * driver will filter incoming packets and only pass the stack those packets it
 * considers need processing. */
#if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 )
    #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eProcessBuffer
#else
    #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
#endif

/* Used to insert test code only. */
#define niDISRUPT_PACKETS    0

/*-----------------------------------------------------------*/

/*
 * Windows threads that are outside of the control of the FreeRTOS simulator are
 * used to interface with the WinPCAP libraries.
 */
DWORD WINAPI prvWinPcapRecvThread( void * pvParam );
DWORD WINAPI prvWinPcapSendThread( void * pvParam );

/*
 * Print out a numbered list of network interfaces that are available on the
 * host computer.
 */
static pcap_if_t * prvPrintAvailableNetworkInterfaces( void );

/*
 * Open the network interface.  The number of the interface to be opened is set
 * by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h.
 */
static void prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces );
static int prvOpenInterface( const char * pucName );

/*
 * Configure the capture filter to allow blocking reads, and to filter out
 * packets that are not of interest to this demo.
 */
static void prvConfigureCaptureBehaviour( void );

/*
 * A function that simulates Ethernet interrupts by periodically polling the
 * WinPCap interface for new data.
 */
static void prvInterruptSimulatorTask( void * pvParameters );

/*
 * Create the buffers that are used to pass data between the FreeRTOS simulator
 * and the Win32 threads that manage WinPCAP.
 */
static void prvCreateThreadSafeBuffers( void );

/*
 * Utility function used to format print messages only.
 */
static const char * prvRemoveSpaces( char * pcBuffer,
                                     int aBuflen,
                                     const char * pcMessage );

/*-----------------------------------------------------------*/

/* Required by the WinPCap library. */
static char cErrorBuffer[ PCAP_ERRBUF_SIZE ];

/* An event used to wake up the Win32 thread that sends data through the WinPCAP
 * library. */
static void * pvSendEvent = NULL;

/* _HT_ made the PCAP interface number configurable through the program's
 * parameters in order to test in different machines. */
static BaseType_t xConfigNetworkInterfaceToUse = configNETWORK_INTERFACE_TO_USE;

/* Handles to the Windows threads that handle the PCAP IO. */
static HANDLE vWinPcapRecvThreadHandle = NULL;
static HANDLE vWinPcapSendThreadHandle = NULL;

/* The interface being used by WinPCap. */
static pcap_t * pxOpenedInterfaceHandle = NULL;

/* Circular buffers used by the PCAP Win32 threads. */
static StreamBuffer_t * xSendBuffer = NULL;
static StreamBuffer_t * xRecvBuffer = NULL;

/* Logs the number of WinPCAP send failures, for viewing in the debugger only. */
static volatile uint32_t ulWinPCAPSendFailures = 0;

/*-----------------------------------------------------------*/

BaseType_t xNetworkInterfaceInitialise( void )
{
    BaseType_t xReturn = pdFALSE;
    pcap_if_t * pxAllNetworkInterfaces;

    /* Query the computer the simulation is being executed on to find the
     * network interfaces it has installed. */
    pxAllNetworkInterfaces = prvPrintAvailableNetworkInterfaces();

    /* Open the network interface.  The number of the interface to be opened is
     * set by the configNETWORK_INTERFACE_TO_USE constant in FreeRTOSConfig.h.
     * Calling this function will set the pxOpenedInterfaceHandle variable.  If,
     * after calling this function, pxOpenedInterfaceHandle is equal to NULL, then
     * the interface could not be opened. */
    if( pxAllNetworkInterfaces != NULL )
    {
        prvOpenSelectedNetworkInterface( pxAllNetworkInterfaces );
    }

    if( pxOpenedInterfaceHandle != NULL )
    {
        xReturn = pdPASS;
    }

    return xReturn;
}
/*-----------------------------------------------------------*/

static void prvCreateThreadSafeBuffers( void )
{
    /* The buffer used to pass data to be transmitted from a FreeRTOS task to
     * the Win32 thread that sends via the WinPCAP library. */
    if( xSendBuffer == NULL )
    {
        xSendBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) + xSEND_BUFFER_SIZE + 1 );
        configASSERT( xSendBuffer );
        memset( xSendBuffer, '\0', sizeof( *xSendBuffer ) - sizeof( xSendBuffer->ucArray ) );
        xSendBuffer->LENGTH = xSEND_BUFFER_SIZE + 1;
    }

    /* The buffer used to pass received data from the Win32 thread that receives
     * via the WinPCAP library to the FreeRTOS task. */
    if( xRecvBuffer == NULL )
    {
        xRecvBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) + xRECV_BUFFER_SIZE + 1 );
        configASSERT( xRecvBuffer );
        memset( xRecvBuffer, '\0', sizeof( *xRecvBuffer ) - sizeof( xRecvBuffer->ucArray ) );
        xRecvBuffer->LENGTH = xRECV_BUFFER_SIZE + 1;
    }
}
/*-----------------------------------------------------------*/

BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer,
                                    BaseType_t bReleaseAfterSend )
{
    size_t xSpace;

    iptraceNETWORK_INTERFACE_TRANSMIT();
    configASSERT( xIsCallingFromIPTask() == pdTRUE );

    /* Both the length of the data being sent and the actual data being sent
     *  are placed in the thread safe buffer used to pass data between the FreeRTOS
     *  tasks and the Win32 thread that sends data via the WinPCAP library.  Drop
     *  the packet if there is insufficient space in the buffer to hold both. */
    xSpace = uxStreamBufferGetSpace( xSendBuffer );

    if( ( pxNetworkBuffer->xDataLength <= ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) &&
        ( xSpace >= ( pxNetworkBuffer->xDataLength + sizeof( pxNetworkBuffer->xDataLength ) ) ) )
    {
        /* First write in the length of the data, then write in the data
         * itself. */
        uxStreamBufferAdd( xSendBuffer, 0, ( const uint8_t * ) &( pxNetworkBuffer->xDataLength ), sizeof( pxNetworkBuffer->xDataLength ) );
        uxStreamBufferAdd( xSendBuffer, 0, ( const uint8_t * ) pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength );
    }
    else
    {
        FreeRTOS_debug_printf( ( "xNetworkInterfaceOutput: send buffers full to store %lu\n", pxNetworkBuffer->xDataLength ) );
    }

    /* Kick the Tx task in either case in case it doesn't know the buffer is
     * full. */
    SetEvent( pvSendEvent );

    /* The buffer has been sent so can be released. */
    if( bReleaseAfterSend != pdFALSE )
    {
        vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
    }

    return pdPASS;
}
/*-----------------------------------------------------------*/

static pcap_if_t * prvPrintAvailableNetworkInterfaces( void )
{
    pcap_if_t * pxAllNetworkInterfaces = NULL, * xInterface;
    int32_t lInterfaceNumber = 1;
    char cBuffer[ 512 ];
    static BaseType_t xInvalidInterfaceDetected = pdFALSE;

    if( xInvalidInterfaceDetected == pdFALSE )
    {
        if( pcap_findalldevs_ex( PCAP_SRC_IF_STRING, NULL, &pxAllNetworkInterfaces, cErrorBuffer ) == -1 )
        {
            printf( "Could not obtain a list of network interfaces\n%s\n", cErrorBuffer );
            pxAllNetworkInterfaces = NULL;
        }
        else
        {
            printf( "\r\n\r\nThe following network interfaces are available:\r\n\r\n" );
        }

        if( pxAllNetworkInterfaces != NULL )
        {
            /* Print out the list of network interfaces.  The first in the list
             * is interface '1', not interface '0'. */
            for( xInterface = pxAllNetworkInterfaces; xInterface != NULL; xInterface = xInterface->next )
            {
                /* The descriptions of the devices can be full of spaces, clean them
                 * a little.  printf() can only be used here because the network is not
                 * up yet - so no other network tasks will be running. */
                printf( "Interface %d - %s\n", lInterfaceNumber, prvRemoveSpaces( cBuffer, sizeof( cBuffer ), xInterface->name ) );
                printf( "              (%s)\n", prvRemoveSpaces( cBuffer, sizeof( cBuffer ), xInterface->description ? xInterface->description : "No description" ) );
                printf( "\n" );
                lInterfaceNumber++;
            }
        }

        if( lInterfaceNumber == 1 )
        {
            /* The interface number was never incremented, so the above for() loop
             * did not execute meaning no interfaces were found. */
            printf( " \nNo network interfaces were found.\n" );
            pxAllNetworkInterfaces = NULL;
        }

        printf( "\r\nThe interface that will be opened is set by " );
        printf( "\"configNETWORK_INTERFACE_TO_USE\", which\r\nshould be defined in FreeRTOSConfig.h\r\n" );

        if( ( xConfigNetworkInterfaceToUse < 1L ) || ( xConfigNetworkInterfaceToUse >= lInterfaceNumber ) )
        {
            printf( "\r\nERROR:  configNETWORK_INTERFACE_TO_USE is set to %d, which is an invalid value.\r\n", xConfigNetworkInterfaceToUse );
            printf( "Please set configNETWORK_INTERFACE_TO_USE to one of the interface numbers listed above,\r\n" );
            printf( "then re-compile and re-start the application.  Only Ethernet (as opposed to WiFi)\r\n" );
            printf( "interfaces are supported.\r\n\r\nHALTING\r\n\r\n\r\n" );
            xInvalidInterfaceDetected = pdTRUE;

            if( pxAllNetworkInterfaces != NULL )
            {
                /* Free the device list, as no devices are going to be opened. */
                pcap_freealldevs( pxAllNetworkInterfaces );
                pxAllNetworkInterfaces = NULL;
            }
        }
        else
        {
            printf( "Attempting to open interface number %d.\n", xConfigNetworkInterfaceToUse );
        }
    }

    return pxAllNetworkInterfaces;
}
/*-----------------------------------------------------------*/

static int prvOpenInterface( const char * pucName )
{
    static char pucInterfaceName[ 256 ];

    if( pucName != NULL )
    {
        strncpy( pucInterfaceName, pucName, sizeof( pucInterfaceName ) );
    }

    pxOpenedInterfaceHandle = pcap_open( pucInterfaceName,            /* The name of the selected interface. */
                                         ipTOTAL_ETHERNET_FRAME_SIZE, /* The size of the packet to capture. */
                                         PCAP_OPENFLAG_PROMISCUOUS,   /* Open in promiscuous mode as the MAC and
                                                                       * IP address is going to be "simulated", and
                                                                       * not be the real MAC and IP address.  This allows
                                                                       * traffic to the simulated IP address to be routed
                                                                       * to uIP, and traffic to the real IP address to be
                                                                       * routed to the Windows TCP/IP stack. */
                                         100,
                                         NULL,                        /* No authentication is required as this is
                                                                       * not a remote capture session. */
                                         cErrorBuffer
                                         );

    if( pxOpenedInterfaceHandle == NULL )
    {
        printf( "\n%s is not supported by WinPcap and cannot be opened\n", pucInterfaceName );
        return 1;
    }
    else
    {
        /* Configure the capture filter to allow blocking reads, and to filter
         * out packets that are not of interest to this demo. */
        prvConfigureCaptureBehaviour();
    }

    return 0;
}
/*-----------------------------------------------------------*/

static void prvOpenSelectedNetworkInterface( pcap_if_t * pxAllNetworkInterfaces )
{
    pcap_if_t * pxInterface;
    int32_t x;

    /* Walk the list of devices until the selected device is located. */
    pxInterface = pxAllNetworkInterfaces;

    for( x = 0L; x < ( xConfigNetworkInterfaceToUse - 1L ); x++ )
    {
        pxInterface = pxInterface->next;
    }

    /* Open the selected interface. */
    if( prvOpenInterface( pxInterface->name ) == 0 )
    {
        printf( "Successfully opened interface number %d.\n", x + 1 );
    }
    else
    {
        printf( "Failed to open interface number %d.\n", x + 1 );
    }

    /* The device list is no longer required. */
    pcap_freealldevs( pxAllNetworkInterfaces );
}
/*-----------------------------------------------------------*/

static void prvConfigureCaptureBehaviour( void )
{
    struct bpf_program xFilterCode;
    uint32_t ulNetMask;

    /* Set up a filter so only the packets of interest are passed to the IP
     * stack.  cErrorBuffer is used for convenience to create the string.  Don't
     * confuse this with an error message. */
    sprintf( cErrorBuffer, "broadcast or multicast or ether host %x:%x:%x:%x:%x:%x",
             ipLOCAL_MAC_ADDRESS[ 0 ], ipLOCAL_MAC_ADDRESS[ 1 ], ipLOCAL_MAC_ADDRESS[ 2 ], ipLOCAL_MAC_ADDRESS[ 3 ], ipLOCAL_MAC_ADDRESS[ 4 ], ipLOCAL_MAC_ADDRESS[ 5 ] );

    ulNetMask = ( configNET_MASK3 << 24UL ) | ( configNET_MASK2 << 16UL ) | ( configNET_MASK1 << 8L ) | configNET_MASK0;

    if( pcap_compile( pxOpenedInterfaceHandle, &xFilterCode, cErrorBuffer, 1, ulNetMask ) < 0 )
    {
        printf( "\nThe packet filter string is invalid\n" );
    }
    else
    {
        if( pcap_setfilter( pxOpenedInterfaceHandle, &xFilterCode ) < 0 )
        {
            printf( "\nAn error occurred setting the packet filter.\n" );
        }

        /* When pcap_compile() succeeds, it allocates memory for the memory pointed to by the bpf_program struct
         * parameter.pcap_freecode() will free that memory. */
        pcap_freecode( &xFilterCode );
    }

    /* Create the buffers used to pass packets between the FreeRTOS simulator
     * and the Win32 threads that are handling WinPCAP. */
    prvCreateThreadSafeBuffers();

    if( pvSendEvent == NULL )
    {
        /* Create event used to signal the Win32 WinPCAP Tx thread. */
        pvSendEvent = CreateEvent( NULL, FALSE, TRUE, NULL );

        /* Create the Win32 thread that handles WinPCAP Rx. */
        vWinPcapRecvThreadHandle = CreateThread(
            NULL,                 /* Pointer to thread security attributes. */
            0,                    /* Initial thread stack size, in bytes. */
            prvWinPcapRecvThread, /* Pointer to thread function. */
            NULL,                 /* Argument for new thread. */
            0,                    /* Creation flags. */
            NULL );

        /* Use the cores that are not used by the FreeRTOS tasks. */
        SetThreadAffinityMask( vWinPcapRecvThreadHandle, ~0x01u );

        /* Create the Win32 thread that handlers WinPCAP Tx. */
        vWinPcapSendThreadHandle = CreateThread(
            NULL,                 /* Pointer to thread security attributes. */
            0,                    /* initial thread stack size, in bytes. */
            prvWinPcapSendThread, /* Pointer to thread function. */
            NULL,                 /* Argument for new thread. */
            0,                    /* Creation flags. */
            NULL );

        /* Use the cores that are not used by the FreeRTOS tasks. */
        SetThreadAffinityMask( vWinPcapSendThreadHandle, ~0x01u );

        /* Create a task that simulates an interrupt in a real system.  This will
         * block waiting for packets, then send a message to the IP task when data
         * is available. */
        xTaskCreate( prvInterruptSimulatorTask, "MAC_ISR", configMINIMAL_STACK_SIZE, NULL, configMAC_ISR_SIMULATOR_PRIORITY, NULL );
    }
}
/*-----------------------------------------------------------*/

/* WinPCAP function. */
void pcap_callback( u_char * user,
                    const struct pcap_pkthdr * pkt_header,
                    const u_char * pkt_data )
{
    ( void ) user;

    /* THIS IS CALLED FROM A WINDOWS THREAD - DO NOT ATTEMPT ANY FREERTOS CALLS
     * OR TO PRINT OUT MESSAGES HERE. */

    /* Pass data to the FreeRTOS simulator on a thread safe circular buffer. */
    if( ( pkt_header->caplen <= ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ) ) &&
        ( uxStreamBufferGetSpace( xRecvBuffer ) >= ( ( ( size_t ) pkt_header->caplen ) + sizeof( *pkt_header ) ) ) )
    {
        /* The received packets will be written to a C source file,
         * only if 'ipconfigUSE_DUMP_PACKETS' is defined.
         * Otherwise, there is no action. */
        iptraceDUMP_PACKET( ( const uint8_t * ) pkt_data, ( size_t ) pkt_header->caplen, pdTRUE );

        uxStreamBufferAdd( xRecvBuffer, 0, ( const uint8_t * ) pkt_header, sizeof( *pkt_header ) );
        uxStreamBufferAdd( xRecvBuffer, 0, ( const uint8_t * ) pkt_data, ( size_t ) pkt_header->caplen );
    }
}
/*-----------------------------------------------------------*/

DWORD WINAPI prvWinPcapRecvThread( void * pvParam )
{
    ( void ) pvParam;

    /* THIS IS A WINDOWS THREAD - DO NOT ATTEMPT ANY FREERTOS CALLS	OR TO PRINT
     * OUT MESSAGES HERE. */

    for( ; ; )
    {
        pcap_dispatch( pxOpenedInterfaceHandle, 1, pcap_callback, ( u_char * ) "mydata" );
    }
}
/*-----------------------------------------------------------*/

DWORD WINAPI prvWinPcapSendThread( void * pvParam )
{
    size_t xLength;
    uint8_t ucBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ];
    static char cErrorMessage[ 1024 ];
    const DWORD xMaxMSToWait = 1000;

    /* THIS IS A WINDOWS THREAD - DO NOT ATTEMPT ANY FREERTOS CALLS	OR TO PRINT
     * OUT MESSAGES HERE. */

    /* Remove compiler warnings about unused parameters. */
    ( void ) pvParam;

    for( ; ; )
    {
        /* Wait until notified of something to send. */
        WaitForSingleObject( pvSendEvent, xMaxMSToWait );

        /* Is there more than the length value stored in the circular buffer
         * used to pass data from the FreeRTOS simulator into this Win32 thread? */
        while( uxStreamBufferGetSize( xSendBuffer ) > sizeof( xLength ) )
        {
            uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) &xLength, sizeof( xLength ), pdFALSE );
            uxStreamBufferGet( xSendBuffer, 0, ( uint8_t * ) ucBuffer, xLength, pdFALSE );

            /* The packets sent will be written to a C source file,
             * only if 'ipconfigUSE_DUMP_PACKETS' is defined.
             * Otherwise, there is no action. */
            iptraceDUMP_PACKET( ucBuffer, xLength, pdFALSE );

            if( pcap_sendpacket( pxOpenedInterfaceHandle, ucBuffer, xLength ) != 0 )
            {
                ulWinPCAPSendFailures++;
            }
        }
    }
}
/*-----------------------------------------------------------*/

static BaseType_t xPacketBouncedBack( const uint8_t * pucBuffer )
{
    EthernetHeader_t * pxEtherHeader;
    BaseType_t xResult;

    pxEtherHeader = ( EthernetHeader_t * ) pucBuffer;

    /* Sometimes, packets are bounced back by the driver and we need not process them. Check
     * whether this packet is one such packet. */
    if( memcmp( ipLOCAL_MAC_ADDRESS, pxEtherHeader->xSourceAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES ) == 0 )
    {
        xResult = pdTRUE;
    }
    else
    {
        xResult = pdFALSE;
    }

    return xResult;
}
/*-----------------------------------------------------------*/

static void prvInterruptSimulatorTask( void * pvParameters )
{
    struct pcap_pkthdr xHeader;
    static struct pcap_pkthdr * pxHeader;
    const uint8_t * pucPacketData;
    uint8_t ucRecvBuffer[ ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ];
    NetworkBufferDescriptor_t * pxNetworkBuffer;
    IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
    eFrameProcessingResult_t eResult;

    /* Remove compiler warnings about unused parameters. */
    ( void ) pvParameters;

    for( ; ; )
    {
        /* Does the circular buffer used to pass data from the Win32 thread that
         * handles WinPCAP Rx into the FreeRTOS simulator contain another packet? */
        if( uxStreamBufferGetSize( xRecvBuffer ) > sizeof( xHeader ) )
        {
            /* Get the next packet. */
            uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) &xHeader, sizeof( xHeader ), pdFALSE );
            uxStreamBufferGet( xRecvBuffer, 0, ( uint8_t * ) ucRecvBuffer, ( size_t ) xHeader.len, pdFALSE );
            pucPacketData = ucRecvBuffer;
            pxHeader = &xHeader;

            iptraceNETWORK_INTERFACE_RECEIVE();

            /* Check for minimal size. */
            if( pxHeader->len >= sizeof( EthernetHeader_t ) )
            {
                eResult = ipCONSIDER_FRAME_FOR_PROCESSING( pucPacketData );
            }
            else
            {
                eResult = eReleaseBuffer;
            }

            if( eResult == eProcessBuffer )
            {
                /* Will the data fit into the frame buffer? */
                if( pxHeader->len <= ipTOTAL_ETHERNET_FRAME_SIZE )
                {
                    /* Obtain a buffer into which the data can be placed.  This
                     * is only	an interrupt simulator, not a real interrupt, so it
                     * is ok to call the task level function here, but note that
                     * some buffer implementations cannot be called from a real
                     * interrupt. */
                    if( xPacketBouncedBack( pucPacketData ) == pdFALSE )
                    {
                        pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( pxHeader->len, 0 );
                    }
                    else
                    {
                        pxNetworkBuffer = NULL;
                    }

                    if( pxNetworkBuffer != NULL )
                    {
                        memcpy( pxNetworkBuffer->pucEthernetBuffer, pucPacketData, pxHeader->len );
                        pxNetworkBuffer->xDataLength = ( size_t ) pxHeader->len;

                        #if ( niDISRUPT_PACKETS == 1 )
                            {
                                pxNetworkBuffer = vRxFaultInjection( pxNetworkBuffer, pucPacketData );
                            }
                        #endif /* niDISRUPT_PACKETS */

                        if( pxNetworkBuffer != NULL )
                        {
                            xRxEvent.pvData = ( void * ) pxNetworkBuffer;

                            /* Data was received and stored.  Send a message to
                             * the IP task to let it know. */
                            if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL )
                            {
                                /* The buffer could not be sent to the stack so
                                 * must be released again.  This is only an
                                 * interrupt simulator, not a real interrupt, so it
                                 * is ok to use the task level function here, but
                                 * note no all buffer implementations will allow
                                 * this function to be executed from a real
                                 * interrupt. */
                                vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
                                iptraceETHERNET_RX_EVENT_LOST();
                            }
                        }
                        else
                        {
                            /* The packet was already released or stored inside
                             * vRxFaultInjection().  Don't release it here. */
                        }
                    }
                    else
                    {
                        iptraceETHERNET_RX_EVENT_LOST();
                    }
                }
                else
                {
                    /* Log that a packet was dropped because it would have
                     * overflowed the buffer, but there may be more buffers to
                     * process. */
                }
            }
        }
        else
        {
            /* There is no real way of simulating an interrupt.  Make sure
             * other tasks can run. */
            vTaskDelay( configWINDOWS_MAC_INTERRUPT_SIMULATOR_DELAY );
        }
    }
}
/*-----------------------------------------------------------*/

static const char * prvRemoveSpaces( char * pcBuffer,
                                     int aBuflen,
                                     const char * pcMessage )
{
    char * pcTarget = pcBuffer;

    /* Utility function used to format messages being printed only. */
    while( ( *pcMessage != 0 ) && ( pcTarget < ( pcBuffer + aBuflen - 1 ) ) )
    {
        *( pcTarget++ ) = *pcMessage;

        if( isspace( *pcMessage ) != pdFALSE )
        {
            while( isspace( *pcMessage ) != pdFALSE )
            {
                pcMessage++;
            }
        }
        else
        {
            pcMessage++;
        }
    }

    *pcTarget = '\0';

    return pcBuffer;
}

#define BUFFER_SIZE               ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING )
#define BUFFER_SIZE_ROUNDED_UP    ( ( BUFFER_SIZE + 7 ) & ~0x07UL )

void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
{
    static uint8_t * pucNetworkPacketBuffers = NULL;
    size_t uxIndex;

    if( pucNetworkPacketBuffers == NULL )
    {
        pucNetworkPacketBuffers = ( uint8_t * ) malloc( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * BUFFER_SIZE_ROUNDED_UP );
    }

    if( pucNetworkPacketBuffers == NULL )
    {
        FreeRTOS_printf( ( "Failed to allocate memory for pxNetworkBuffers" ) );
        configASSERT( 0 );
    }
    else
    {
        for( uxIndex = 0; uxIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; uxIndex++ )
        {
            size_t uxOffset = uxIndex * BUFFER_SIZE_ROUNDED_UP;
            NetworkBufferDescriptor_t ** ppDescriptor;

            /* At the beginning of each pbuff is a pointer to the relevant descriptor */
            ppDescriptor = ( NetworkBufferDescriptor_t ** ) &( pucNetworkPacketBuffers[ uxOffset ] );

            /* Set this pointer to the address of the correct descriptor */
            *ppDescriptor = &( pxNetworkBuffers[ uxIndex ] );

            /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the
             * beginning of the allocated buffer. */
            pxNetworkBuffers[ uxIndex ].pucEthernetBuffer = &( pucNetworkPacketBuffers[ uxOffset + ipBUFFER_PADDING ] );
        }
    }
}
