/* | |
* FreeRTOS+UDP V1.0.3 (C) 2014 Real Time Engineers ltd. | |
* All rights reserved | |
* | |
* This file is part of the FreeRTOS+UDP distribution. The FreeRTOS+UDP license | |
* terms are different to the FreeRTOS license terms. | |
* | |
* FreeRTOS+UDP uses a dual license model that allows the software to be used | |
* under a standard GPL open source license, or a commercial license. The | |
* standard GPL license (unlike the modified GPL license under which FreeRTOS | |
* itself is distributed) requires that all software statically linked with | |
* FreeRTOS+UDP is also distributed under the same GPL V2 license terms. | |
* Details of both license options follow: | |
* | |
* - Open source licensing - | |
* FreeRTOS+UDP is a free download and may be used, modified, evaluated and | |
* distributed without charge provided the user adheres to version two of the | |
* GNU General Public License (GPL) and does not remove the copyright notice or | |
* this text. The GPL V2 text is available on the gnu.org web site, and on the | |
* following URL: http://www.FreeRTOS.org/gpl-2.0.txt. | |
* | |
* - Commercial licensing - | |
* Businesses and individuals that for commercial or other reasons cannot comply | |
* with the terms of the GPL V2 license must obtain a commercial license before | |
* incorporating FreeRTOS+UDP into proprietary software for distribution in any | |
* form. Commercial licenses can be purchased from http://shop.freertos.org/udp | |
* and do not require any source files to be changed. | |
* | |
* FreeRTOS+UDP is distributed in the hope that it will be useful. You cannot | |
* use FreeRTOS+UDP unless you agree that you use the software 'as is'. | |
* FreeRTOS+UDP is provided WITHOUT ANY WARRANTY; without even the implied | |
* warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
* PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they | |
* implied, expressed, or statutory. | |
* | |
* 1 tab == 4 spaces! | |
* | |
* http://www.FreeRTOS.org | |
* http://www.FreeRTOS.org/udp | |
* | |
*/ | |
/* Standard includes. */ | |
#include <stdint.h> | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "queue.h" | |
#include "semphr.h" | |
#include "timers.h" | |
/* FreeRTOS+UDP includes. */ | |
#include "FreeRTOS_UDP_IP.h" | |
#include "FreeRTOS_IP_Private.h" | |
#include "FreeRTOS_Sockets.h" | |
#include "FreeRTOS_DHCP.h" | |
#include "NetworkInterface.h" | |
#include "NetworkBufferManagement.h" | |
/* Sanity check the configuration. */ | |
#if configUSE_TIMERS != 1 | |
#error configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h to use this file | |
#endif | |
#if configTICK_RATE_HZ > 1000 | |
#error configTICK_RATE_HZ must be less than 1000 to use FreeRTOS+UDP | |
#endif | |
#if ( ipconfigEVENT_QUEUE_LENGTH < ( ipconfigNUM_NETWORK_BUFFERS + 5 ) ) | |
#error The ipconfigEVENT_QUEUE_LENGTH parameter must be at least ipconfigNUM_NETWORK_BUFFERS + 5 | |
#endif | |
#if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1 && ipconfigSUPPORT_OUTGOING_PINGS == 1 | |
#error ipconfigSUPPORT_OUTGOING_PINGS can only be set to 1 if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS is set to 0 as IP fragmentation is not supported for ICMP (ping) packets | |
#endif | |
#if ( ipconfigNETWORK_MTU < 46 ) | |
#error ipconfigNETWORK_MTU must be at least 46. | |
#endif | |
/*-----------------------------------------------------------*/ | |
/* The IP header length in bytes. */ | |
#define ipIP_HEADER_LENGTH ( 20 ) | |
/* IP protocol definitions. */ | |
#define ipPROTOCOL_ICMP ( 1 ) | |
#define ipPROTOCOL_UDP ( 17 ) | |
/* ICMP protocol definitions. */ | |
#define ipICMP_ECHO_REQUEST ( ( uint16_t ) 8 ) | |
#define ipICMP_ECHO_REPLY ( ( uint16_t ) 0 ) | |
/* The expected IP version and header length coded into the IP header itself. */ | |
#define ipIP_VERSION_AND_HEADER_LENGTH_BYTE ( ( uint8_t ) 0x45 ) | |
/* Time delay between repeated attempts to initialise the network hardware. */ | |
#define ipINITIALISATION_RETRY_DELAY ( ( ( portTickType ) 3000 ) / portTICK_RATE_MS ) | |
/* The local MAC address is accessed from within xDefaultPartUDPPacketHeader, | |
rather than duplicated in its own variable. */ | |
#define ipLOCAL_MAC_ADDRESS ( xDefaultPartUDPPacketHeader ) | |
/* The local IP address is accessed from within xDefaultPartUDPPacketHeader, | |
rather than duplicated in its own variable. */ | |
#define ipLOCAL_IP_ADDRESS_POINTER ( ( uint32_t * ) &( xDefaultPartUDPPacketHeader[ 20 ] ) ) | |
/* Defines how often the ARP timer callback function is executed. The time is | |
shorted in the Windows simulator as simulated time is not real time. */ | |
#ifdef _WINDOWS_ | |
#define ipARP_TIMER_PERIOD_MS ( 500 ) /* For windows simulator builds. */ | |
#else | |
#define ipARP_TIMER_PERIOD_MS ( 10000 ) | |
#endif | |
/* 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. In this case ipCONSIDER_FRAME_FOR_PROCESSING() can | |
be #defined away. If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 0 | |
then the Ethernet driver will pass all received packets to the stack, and the | |
stack must do the filtering itself. In this case ipCONSIDER_FRAME_FOR_PROCESSING | |
needs to call eConsiderFrameForProcessing. */ | |
#if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 | |
#define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) | |
#else | |
#define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer | |
#endif | |
/* When the age of an entry in the ARP table reaches this value (it counts down | |
to zero, so this is an old entry) an ARP request will be sent to see if the | |
entry is still valid and can therefore be refreshed. */ | |
#define ipMAX_ARP_AGE_BEFORE_NEW_ARP_REQUEST ( 3 ) | |
/* Number of bits to shift to divide by 8. Used to remove the need for a | |
divide. */ | |
#define ipSHIFT_TO_DIVIDE_BY_8 ( 3U ) | |
/* The bit set in the IP header flags to indicate that the IP packet contains | |
a fragment of the eventual total payload, and that more fragments will follow. */ | |
#define ipMORE_FRAGMENTS_FLAG_BIT ( 0x2000U ) | |
/* ICMP packets are sent using the same function as UDP packets. The port | |
number is used to distinguish between the two, as 0 is an invalid UDP port. */ | |
#define ipPACKET_CONTAINS_ICMP_DATA ( 0 ) | |
/* The character used to fill ICMP echo requests, and therefore also the | |
character expected to fill ICMP echo replies. */ | |
#define ipECHO_DATA_FILL_BYTE 'x' | |
#if( ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN ) | |
#define ipFRAGMENT_OFFSET_BIT_MASK ( ( uint16_t ) 0xff0f ) /* The bits in the two byte IP header field that make up the fragment offset value. */ | |
#else | |
#define ipFRAGMENT_OFFSET_BIT_MASK ( ( uint16_t ) 0x0fff ) /* The bits in the two byte IP header field that make up the fragment offset value. */ | |
#if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1 | |
#warning Fragment offsets have not been tested on big endian machines. | |
#endif /* ipconfigCAN_FRAGMENT_OUTGOING_PACKETS */ | |
#endif /* ipconfigBYTE_ORDER */ | |
/*-----------------------------------------------------------*/ | |
/* Miscellaneous structure and definitions. */ | |
/*-----------------------------------------------------------*/ | |
typedef struct xARP_CACHE_TABLE_ROW | |
{ | |
uint32_t ulIPAddress; /* The IP address of an ARP cache entry. */ | |
xMACAddress_t xMACAddress; /* The MAC address of an ARP cache entry. */ | |
uint8_t ucAge; /* A value that is periodically decremented but can also be refreshed by active communication. The ARP cache entry is removed if the value reaches zero. */ | |
} xARPCacheRow_t; | |
typedef enum | |
{ | |
eARPCacheMiss = 0, /* An ARP table lookup did not find a valid entry. */ | |
eARPCacheHit, /* An ARP table lookup found a valid entry. */ | |
eCantSendPacket /* There is no IP address, or an ARP is still in progress, so the packet cannot be sent. */ | |
} eARPLookupResult_t; | |
typedef enum | |
{ | |
eNotFragment = 0, /* The IP packet being sent is not part of a fragment. */ | |
eFirstFragment, /* The IP packet being sent is the first in a set of fragmented packets. */ | |
eFollowingFragment /* The IP packet being sent is part of a set of fragmented packets. */ | |
} eIPFragmentStatus_t; | |
/*-----------------------------------------------------------*/ | |
/* | |
* Called when new data is available from the network interface. | |
*/ | |
static void prvProcessEthernetPacket( xNetworkBufferDescriptor_t * const pxNetworkBuffer ); | |
/* | |
* Called when the application has generated a UDP packet to send. | |
*/ | |
static void prvProcessGeneratedPacket( xNetworkBufferDescriptor_t * const pxNetworkBuffer ); | |
/* | |
* Processes incoming ARP packets. | |
*/ | |
static eFrameProcessingResult_t prvProcessARPPacket( xARPPacket_t * const pxARPFrame ); | |
/* | |
* Process incoming IP packets. | |
*/ | |
static eFrameProcessingResult_t prvProcessIPPacket( const xIPPacket_t * const pxIPPacket, xNetworkBufferDescriptor_t * const pxNetworkBuffer ); | |
/* | |
* Process incoming ICMP packets. | |
*/ | |
#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) | |
static eFrameProcessingResult_t prvProcessICMPPacket( xICMPPacket_t * const pxICMPPacket ); | |
#endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ | |
/* | |
* Swap the source and destination addresses in an already constructed Ethernet | |
* frame, and send the frame to the network. | |
*/ | |
static void prvReturnEthernetFrame( xNetworkBufferDescriptor_t * const pxNetworkBuffer ); | |
/* | |
* Return the checksum generated over usDataLengthBytes from pucNextData. | |
*/ | |
static uint16_t prvGenerateChecksum( const uint8_t * const pucNextData, const uint16_t usDataLengthBytes, portBASE_TYPE xChecksumIsOffloaded ); | |
/* | |
* The callback function that is assigned to all periodic processing timers - | |
* namely the DHCP timer and the ARP timer. | |
*/ | |
void vIPFunctionsTimerCallback( xTimerHandle xTimer ); | |
/* | |
* Reduce the age count in each entry within the ARP cache. An entry is no | |
* longer considered valid and is deleted if its age reaches zero. | |
*/ | |
static void prvAgeARPCache( void ); | |
/* | |
* If ulIPAddress is already in the ARP cache table then reset the age of the | |
* entry back to its maximum value. If ulIPAddress is not already in the ARP | |
* cache table then add it - replacing the oldest current entry if there is not | |
* a free space available. | |
*/ | |
static void prvRefreshARPCacheEntry( const xMACAddress_t * const pxMACAddress, const uint32_t ulIPAddress ); | |
/* | |
* Creates the pseudo header necessary then generate the checksum over the UDP | |
* packet. Returns the calculated checksum. | |
*/ | |
static uint16_t prvGenerateUDPChecksum( const xUDPPacket_t * const pxUDPPacket, portBASE_TYPE xChecksumIsOffloaded ); | |
/* | |
* Look for ulIPAddress in the ARP cache. If the IP address exists, copy the | |
* associated MAC address into pxMACAddress, refresh the ARP cache entry's | |
* age, and return eARPCacheHit. If the IP address does not exist in the ARP | |
* cache return eARPCacheMiss. If the packet cannot be sent for any reason | |
* (maybe DHCP is still in process, or the addressing needs a gateway but there | |
* isn't a gateway defined) then return eCantSendPacket. | |
*/ | |
static eARPLookupResult_t prvGetARPCacheEntry( uint32_t *pulIPAddress, xMACAddress_t * const pxMACAddress ); | |
/* | |
* The main UDP/IP stack processing task. This task receives commands/events | |
* from the network hardware drivers, tasks that are using sockets, and software | |
* timers (such as the ARP timer). | |
*/ | |
static void prvIPTask( void *pvParameters ); | |
/* | |
* Send out an ARP request for the IP address contained in pxNetworkBuffer, and | |
* add an entry into the ARP table that indicates that an ARP reply is | |
* outstanding so re-transmissions can be generated. | |
*/ | |
static void prvGenerateARPRequestPacket( xNetworkBufferDescriptor_t * const pxNetworkBuffer ); | |
/* | |
* Called when outgoing packets are fragmented and require a fragment offset in | |
* their IP headers. Set the fragment offset (which includes the IP flags) and | |
* length from the data passed in the pxFragmentParameters structure. | |
*/ | |
#if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1 | |
static void prvCalculateFragmentOffsetAndLength( xIPFragmentParameters_t *pxFragmentParameters, uint16_t *pusFragmentOffset, uint16_t *pusFragmentLength ); | |
#endif /* ipconfigCAN_FRAGMENT_OUTGOING_PACKETS */ | |
/* | |
* Complete the pxUDPPacket header with the information passed in | |
* pxNetworkBuffer. ucSocketOptions are passed in case the options include | |
* disabling the checksum. | |
*/ | |
static void prvCompleteUDPHeader( xNetworkBufferDescriptor_t *pxNetworkBuffer, xUDPPacket_t *pxUDPPacket, uint8_t ucSocketOptions ); | |
/* | |
* Send the event eEvent to the IP task event queue, using a block time of | |
* zero. Return pdPASS if the message was sent successfully, otherwise return | |
* pdFALSE. | |
*/ | |
static portBASE_TYPE prvSendEventToIPTask( eIPEvent_t eEvent ); | |
/* | |
* Generate and send an ARP request for the IP address passed in ulIPAddress. | |
*/ | |
static void prvOutputARPRequest( uint32_t ulIPAddress ); | |
/* | |
* Turns around an incoming ping request to convert it into a ping reply. | |
*/ | |
#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) | |
static eFrameProcessingResult_t prvProcessICMPEchoRequest( xICMPPacket_t * const pxICMPPacket ); | |
#endif /* ipconfigREPLY_TO_INCOMING_PINGS */ | |
/* | |
* Processes incoming ping replies. The application callback function | |
* vApplicationPingReplyHook() is called with the results. | |
*/ | |
#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) | |
static void prvProcessICMPEchoReply( xICMPPacket_t * const pxICMPPacket ); | |
#endif /* ipconfigSUPPORT_OUTGOING_PINGS */ | |
/* | |
* Called to create a network connection when the stack is first started, or | |
* when the network connection is lost. | |
*/ | |
static void prvProcessNetworkDownEvent( void ); | |
/*-----------------------------------------------------------*/ | |
/* The queue used to pass events into the UDP task for processing. */ | |
xQueueHandle xNetworkEventQueue = NULL; | |
/* The ARP cache. */ | |
static xARPCacheRow_t xARPCache[ ipconfigARP_CACHE_ENTRIES ]; | |
/* The timer that triggers ARP events. */ | |
static xTimerHandle xARPTimer = NULL; | |
/* Used to ensure network down events cannot be missed when they cannot be | |
posted to the network event queue because the network event queue is already | |
full. */ | |
static portBASE_TYPE xNetworkDownEventPending = pdFALSE; | |
/* For convenience, a MAC address of all zeros and another of all 0xffs are | |
defined const for quick reference. */ | |
static const xMACAddress_t xNullMACAddress = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; | |
static const xMACAddress_t xBroadcastMACAddress = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; | |
/* Part of the Ethernet and IP headers are always constant when sending an IPv4 | |
UDP packet. This array defines the constant parts, allowing this part of the | |
packet to be filled in using a simple memcpy() instead of individual writes. */ | |
uint8_t xDefaultPartUDPPacketHeader[] = | |
{ | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Ethernet source MAC address. */ | |
0x08, 0x00, /* Ethernet frame type. */ | |
ipIP_VERSION_AND_HEADER_LENGTH_BYTE, /* ucVersionHeaderLength. */ | |
0x00, /* ucDifferentiatedServicesCode. */ | |
0x00, 0x00, /* usLength. */ | |
0x00, 0x00, /* usIdentification. */ | |
0x00, 0x00, /* usFragmentOffset. */ | |
updconfigIP_TIME_TO_LIVE, /* ucTimeToLive */ | |
ipPROTOCOL_UDP, /* ucProtocol. */ | |
0x00, 0x00, /* usHeaderChecksum. */ | |
0x00, 0x00, 0x00, 0x00 /* Source IP address. */ | |
}; | |
/* Part of the Ethernet and ARP headers are always constant when sending an IPv4 | |
ARP packet. This array defines the constant parts, allowing this part of the | |
packet to be filled in using a simple memcpy() instead of individual writes. */ | |
static const uint8_t xDefaultPartARPPacketHeader[] = | |
{ | |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Ethernet destination address. */ | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Ethernet source address. */ | |
0x08, 0x06, /* Ethernet frame type (ipARP_TYPE). */ | |
0x00, 0x01, /* usHardwareType (ipARP_HARDWARE_TYPE_ETHERNET). */ | |
0x08, 0x00, /* usProtocolType. */ | |
ipMAC_ADDRESS_LENGTH_BYTES, /* ucHardwareAddressLength. */ | |
ipIP_ADDRESS_LENGTH_BYTES, /* ucProtocolAddressLength. */ | |
0x00, 0x01, /* usOperation (ipARP_REQUEST). */ | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* xSenderHardwareAddress. */ | |
0x00, 0x00, 0x00, 0x00, /* ulSenderProtocolAddress. */ | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* xTargetHardwareAddress. */ | |
}; | |
/* Structure that stores the netmask, gateway address and DNS server addresses. */ | |
static xNetworkAddressingParameters_t xNetworkAddressing = { 0, 0, 0, 0 }; | |
/*-----------------------------------------------------------*/ | |
static void prvIPTask( void *pvParameters ) | |
{ | |
xIPStackEvent_t xReceivedEvent; | |
/* Just to prevent compiler warnings about unused parameters. */ | |
( void ) pvParameters; | |
/* Create the ARP timer, but don't start it until the network has | |
connected. */ | |
xARPTimer = xTimerCreate( "ARPTimer", ( ipARP_TIMER_PERIOD_MS / portTICK_RATE_MS ), pdTRUE, ( void * ) eARPTimerEvent, vIPFunctionsTimerCallback ); | |
configASSERT( xARPTimer ); | |
/* Generate a dummy message to say that the network connection has gone | |
down. This will cause this task to initialise the network interface. After | |
this it is the responsibility of the network interface hardware driver to | |
send this message if a previously connected network is disconnected. */ | |
FreeRTOS_NetworkDown(); | |
/* Loop, processing IP events. */ | |
for( ;; ) | |
{ | |
/* Wait until there is something to do. */ | |
if( xQueueReceive( xNetworkEventQueue, ( void * ) &xReceivedEvent, portMAX_DELAY ) == pdPASS ) | |
{ | |
iptraceNETWORK_EVENT_RECEIVED( xReceivedEvent.eEventType ); | |
switch( xReceivedEvent.eEventType ) | |
{ | |
case eNetworkDownEvent : | |
/* Attempt to establish a connection. */ | |
prvProcessNetworkDownEvent(); | |
break; | |
case eEthernetRxEvent : | |
/* The network hardware driver has received a new packet. | |
A pointer to the received buffer is located in the pvData | |
member of the received event structure. */ | |
prvProcessEthernetPacket( ( xNetworkBufferDescriptor_t * ) ( xReceivedEvent.pvData ) ); | |
break; | |
case eARPTimerEvent : | |
/* The ARP timer has expired, process the ARP cache. */ | |
prvAgeARPCache(); | |
break; | |
case eStackTxEvent : | |
/* The network stack has generated a packet to send. A | |
pointer to the generated buffer is located in the pvData | |
member of the received event structure. */ | |
prvProcessGeneratedPacket( ( xNetworkBufferDescriptor_t * ) ( xReceivedEvent.pvData ) ); | |
break; | |
case eDHCPEvent: | |
/* The DHCP state machine needs processing. */ | |
#if ipconfigUSE_DHCP == 1 | |
{ | |
vDHCPProcess( pdFALSE, ( xMACAddress_t * ) ipLOCAL_MAC_ADDRESS, ipLOCAL_IP_ADDRESS_POINTER, &xNetworkAddressing ); | |
} | |
#endif | |
break; | |
default : | |
/* Should not get here. */ | |
break; | |
} | |
if( xNetworkDownEventPending != pdFALSE ) | |
{ | |
/* A network down event could not be posted to the network | |
event queue because the queue was full. Try posting again. */ | |
FreeRTOS_NetworkDown(); | |
} | |
} | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
void FreeRTOS_NetworkDown( void ) | |
{ | |
static const xIPStackEvent_t xNetworkDownEvent = { eNetworkDownEvent, NULL }; | |
const portTickType xDontBlock = 0; | |
/* Simply send the network task the appropriate event. */ | |
if( xQueueSendToBack( xNetworkEventQueue, &xNetworkDownEvent, xDontBlock ) != pdPASS ) | |
{ | |
xNetworkDownEventPending = pdTRUE; | |
} | |
else | |
{ | |
xNetworkDownEventPending = pdFALSE; | |
} | |
iptraceNETWORK_DOWN(); | |
} | |
/*-----------------------------------------------------------*/ | |
portBASE_TYPE FreeRTOS_NetworkDownFromISR( void ) | |
{ | |
static const xIPStackEvent_t xNetworkDownEvent = { eNetworkDownEvent, NULL }; | |
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; | |
/* Simply send the network task the appropriate event. */ | |
if( xQueueSendToBackFromISR( xNetworkEventQueue, &xNetworkDownEvent, &xHigherPriorityTaskWoken ) != pdPASS ) | |
{ | |
xNetworkDownEventPending = pdTRUE; | |
} | |
else | |
{ | |
xNetworkDownEventPending = pdFALSE; | |
} | |
iptraceNETWORK_DOWN(); | |
return xHigherPriorityTaskWoken; | |
} | |
/*-----------------------------------------------------------*/ | |
void *FreeRTOS_GetUDPPayloadBuffer( size_t xRequestedSizeBytes, portTickType xBlockTimeTicks ) | |
{ | |
xNetworkBufferDescriptor_t *pxNetworkBuffer; | |
void *pvReturn; | |
/* Cap the block time. The reason for this is explained where | |
ipconfigMAX_SEND_BLOCK_TIME_TICKS is defined (assuming an official | |
FreeRTOSIPConfig.h header file is being used). */ | |
if( xBlockTimeTicks > ipconfigMAX_SEND_BLOCK_TIME_TICKS ) | |
{ | |
xBlockTimeTicks = ipconfigMAX_SEND_BLOCK_TIME_TICKS; | |
} | |
/* Obtain a network buffer with the required amount of storage. */ | |
pxNetworkBuffer = pxNetworkBufferGet( sizeof( xUDPPacket_t ) + xRequestedSizeBytes, xBlockTimeTicks ); | |
if( pxNetworkBuffer != NULL ) | |
{ | |
/* Leave space for the UPD header. */ | |
pvReturn = ( void * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET ] ); | |
} | |
else | |
{ | |
pvReturn = NULL; | |
} | |
return ( void * ) pvReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
void FreeRTOS_ReleaseUDPPayloadBuffer( void *pvBuffer ) | |
{ | |
uint8_t *pucBuffer; | |
/* Obtain the network buffer from the zero copy pointer. */ | |
pucBuffer = ( uint8_t * ) pvBuffer; | |
pucBuffer -= ( ipBUFFER_PADDING + sizeof( xUDPPacket_t ) ); | |
vNetworkBufferRelease( * ( ( xNetworkBufferDescriptor_t ** ) pucBuffer ) ); | |
} | |
/*-----------------------------------------------------------*/ | |
uint8_t * FreeRTOS_GetMACAddress( void ) | |
{ | |
return ipLOCAL_MAC_ADDRESS; | |
} | |
/*-----------------------------------------------------------*/ | |
portBASE_TYPE FreeRTOS_IPInit( const uint8_t ucIPAddress[ ipIP_ADDRESS_LENGTH_BYTES ], const uint8_t ucNetMask[ ipIP_ADDRESS_LENGTH_BYTES ], const uint8_t ucGatewayAddress[ ipIP_ADDRESS_LENGTH_BYTES ], const uint8_t ucDNSServerAddress[ ipIP_ADDRESS_LENGTH_BYTES ], const uint8_t ucMACAddress[ ipMAC_ADDRESS_LENGTH_BYTES ] ) | |
{ | |
static portBASE_TYPE xReturn = pdFALSE; | |
/* Only create the IP event queue if it has not already been created, in | |
case this function is called more than once. */ | |
if( xNetworkEventQueue == NULL ) | |
{ | |
xNetworkEventQueue = xQueueCreate( ipconfigEVENT_QUEUE_LENGTH, sizeof( xIPStackEvent_t ) ); | |
configASSERT( xNetworkEventQueue ); | |
vQueueAddToRegistry( xNetworkEventQueue, "NetEvnt" ); | |
} | |
if( xNetworkBuffersInitialise() == pdPASS ) | |
{ | |
if( xNetworkEventQueue != NULL ) | |
{ | |
/* xReturn is static to ensure the network interface is not | |
initialised twice. */ | |
if( xReturn == pdFALSE ) | |
{ | |
/* Store the local IP and MAC address. */ | |
xNetworkAddressing.ulDefaultIPAddress = FreeRTOS_inet_addr_quick( ucIPAddress[ 0 ], ucIPAddress[ 1 ], ucIPAddress[ 2 ], ucIPAddress[ 3 ] ); | |
xNetworkAddressing.ulNetMask = FreeRTOS_inet_addr_quick( ucNetMask[ 0 ], ucNetMask[ 1 ], ucNetMask[ 2 ], ucNetMask[ 3 ] ); | |
xNetworkAddressing.ulGatewayAddress = FreeRTOS_inet_addr_quick( ucGatewayAddress[ 0 ], ucGatewayAddress[ 1 ], ucGatewayAddress[ 2 ], ucGatewayAddress[ 3 ] ); | |
xNetworkAddressing.ulDNSServerAddress = FreeRTOS_inet_addr_quick( ucDNSServerAddress[ 0 ], ucDNSServerAddress[ 1 ], ucDNSServerAddress[ 2 ], ucDNSServerAddress[ 3 ] ); | |
#if ipconfigUSE_DHCP == 1 | |
{ | |
/* The IP address is not set until DHCP completes. */ | |
*ipLOCAL_IP_ADDRESS_POINTER = 0x00UL; | |
} | |
#else | |
{ | |
*ipLOCAL_IP_ADDRESS_POINTER = xNetworkAddressing.ulDefaultIPAddress; | |
/* Ensure the gateway is on the same subnet as the IP | |
address. */ | |
configASSERT( ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) == ( xNetworkAddressing.ulGatewayAddress & xNetworkAddressing.ulNetMask ) ); | |
} | |
#endif /* ipconfigUSE_DHCP == 1 */ | |
/* The MAC address is stored in the start of the default packet | |
header fragment, which is used when sending UDP packets. */ | |
memcpy( ( void * ) ipLOCAL_MAC_ADDRESS, ( void * ) ucMACAddress, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); | |
/* Prepare the sockets interface. */ | |
FreeRTOS_SocketsInit(); | |
/* Create the task that processes Ethernet and stack events. */ | |
xReturn = xTaskCreate( prvIPTask, "UDP/IP", ipconfigUDP_TASK_STACK_SIZE_WORDS, NULL, ipconfigUDP_TASK_PRIORITY, NULL ); | |
} | |
} | |
} | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
void FreeRTOS_GetAddressConfiguration( uint32_t *pulIPAddress, uint32_t *pulNetMask, uint32_t *pulGatewayAddress, uint32_t *pulDNSServerAddress ) | |
{ | |
if( pulIPAddress != NULL ) | |
{ | |
*pulIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; | |
} | |
if( pulNetMask != NULL ) | |
{ | |
*pulNetMask = xNetworkAddressing.ulNetMask; | |
} | |
if( pulGatewayAddress != NULL ) | |
{ | |
*pulGatewayAddress = xNetworkAddressing.ulGatewayAddress; | |
} | |
if( pulDNSServerAddress != NULL ) | |
{ | |
*pulDNSServerAddress = xNetworkAddressing.ulDNSServerAddress; | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) | |
portBASE_TYPE FreeRTOS_SendPingRequest( uint32_t ulIPAddress, size_t xNumberOfBytesToSend, portTickType xBlockTimeTicks ) | |
{ | |
xNetworkBufferDescriptor_t *pxNetworkBuffer; | |
xICMPHeader_t *pxICMPHeader; | |
portBASE_TYPE xReturn = pdFAIL; | |
static uint16_t usSequenceNumber = 0; | |
uint8_t *pucChar; | |
xIPStackEvent_t xStackTxEvent = { eStackTxEvent, NULL }; | |
if( xNumberOfBytesToSend < ( ( ipconfigNETWORK_MTU - sizeof( xIPHeader_t ) ) - sizeof( xICMPHeader_t ) ) ) | |
{ | |
pxNetworkBuffer = pxNetworkBufferGet( xNumberOfBytesToSend + sizeof( xICMPPacket_t ), xBlockTimeTicks ); | |
if( pxNetworkBuffer != NULL ) | |
{ | |
pxICMPHeader = ( xICMPHeader_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipIP_PAYLOAD_OFFSET ] ); | |
usSequenceNumber++; | |
/* Fill in the basic header information. */ | |
pxICMPHeader->ucTypeOfMessage = ipICMP_ECHO_REQUEST; | |
pxICMPHeader->ucTypeOfService = 0; | |
pxICMPHeader->usIdentifier = usSequenceNumber; | |
pxICMPHeader->usSequenceNumber = usSequenceNumber; | |
pxICMPHeader->usChecksum = 0; | |
/* Find the start of the data. */ | |
pucChar = ( uint8_t * ) pxICMPHeader; | |
pucChar += sizeof( xICMPHeader_t ); | |
/* Just memset the data to a fixed value. */ | |
memset( ( void * ) pucChar, ( int ) ipECHO_DATA_FILL_BYTE, xNumberOfBytesToSend ); | |
/* The message is complete, calculate the checksum. */ | |
pxICMPHeader->usChecksum = prvGenerateChecksum( ( uint8_t * ) pxICMPHeader, ( uint16_t ) ( xNumberOfBytesToSend + sizeof( xICMPHeader_t ) ), pdFALSE ); | |
/* Complete the network buffer information. */ | |
pxNetworkBuffer->ulIPAddress = ulIPAddress; | |
pxNetworkBuffer->usPort = ipPACKET_CONTAINS_ICMP_DATA; | |
pxNetworkBuffer->xDataLength = xNumberOfBytesToSend + sizeof( xICMPHeader_t ); | |
/* Send to the stack. */ | |
xStackTxEvent.pvData = pxNetworkBuffer; | |
if( xQueueSendToBack( xNetworkEventQueue, &xStackTxEvent, xBlockTimeTicks ) != pdPASS ) | |
{ | |
vNetworkBufferRelease( pxNetworkBuffer ); | |
iptraceSTACK_TX_EVENT_LOST( ipSTACK_TX_EVENT ); | |
} | |
else | |
{ | |
xReturn = usSequenceNumber; | |
} | |
} | |
} | |
else | |
{ | |
/* The requested number of bytes will not fit in the available space | |
in the network buffer. Outgoing fragmentation is only supported for | |
UDP packets. */ | |
} | |
return xReturn; | |
} | |
#endif /* ipconfigSUPPORT_OUTGOING_PINGS == 1 */ | |
/*-----------------------------------------------------------*/ | |
static portBASE_TYPE prvSendEventToIPTask( eIPEvent_t eEvent ) | |
{ | |
xIPStackEvent_t xEventMessage; | |
const portTickType xDontBlock = 0; | |
portBASE_TYPE xReturn; | |
xEventMessage.eEventType = eEvent; | |
xReturn = xQueueSendToBack( xNetworkEventQueue, &xEventMessage, xDontBlock ); | |
if( xReturn != pdPASS ) | |
{ | |
iptraceSTACK_TX_EVENT_LOST( ipARP_TIMER_EVENT ); | |
} | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
void vIPFunctionsTimerCallback( xTimerHandle xTimer ) | |
{ | |
eIPEvent_t eMessage; | |
/* This time can be used to send more than one type of message to the IP | |
task. The message ID is stored in the ID of the timer. The strange | |
casting is to avoid compiler warnings. */ | |
eMessage = ( eIPEvent_t ) ( ( portBASE_TYPE ) pvTimerGetTimerID( xTimer ) ); | |
prvSendEventToIPTask( eMessage ); | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvOutputARPRequest( uint32_t ulIPAddress ) | |
{ | |
xNetworkBufferDescriptor_t *pxNetworkBuffer; | |
/* This is called from the context of the IP event task, so a block time | |
must not be used. */ | |
pxNetworkBuffer = pxNetworkBufferGet( sizeof( xARPPacket_t ), 0 ); | |
if( pxNetworkBuffer != NULL ) | |
{ | |
pxNetworkBuffer->ulIPAddress = ulIPAddress; | |
prvGenerateARPRequestPacket( pxNetworkBuffer ); | |
xNetworkInterfaceOutput( pxNetworkBuffer ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvAgeARPCache( void ) | |
{ | |
portBASE_TYPE x; | |
/* Loop through each entry in the ARP cache. */ | |
for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) | |
{ | |
/* If the entry is valid (its age is greater than zero). */ | |
if( xARPCache[ x ].ucAge > 0U ) | |
{ | |
/* Decrement the age value of the entry in this ARP cache table row. | |
When the age reaches zero it is no longer considered valid. */ | |
( xARPCache[ x ].ucAge )--; | |
/* If the entry has a MAC address of 0, then it is waiting an ARP | |
reply, and the ARP request should be retransmitted. */ | |
if( memcmp( ( void * ) &xNullMACAddress, ( void * ) &( xARPCache[ x ].xMACAddress ), sizeof( xMACAddress_t ) ) == 0 ) | |
{ | |
prvOutputARPRequest( xARPCache[ x ].ulIPAddress ); | |
} | |
else if( xARPCache[ x ].ucAge <= ipMAX_ARP_AGE_BEFORE_NEW_ARP_REQUEST ) | |
{ | |
/* This entry will get removed soon. See if the MAC address is | |
still valid to prevent this happening. */ | |
iptraceARP_TABLE_ENTRY_WILL_EXPIRE( xARPCache[ x ].ulIPAddress ); | |
prvOutputARPRequest( xARPCache[ x ].ulIPAddress ); | |
} | |
else | |
{ | |
/* The age has just ticked down, with nothing to do. */ | |
} | |
if( xARPCache[ x ].ucAge == 0 ) | |
{ | |
/* The entry is no longer valid. Wipe it out. */ | |
iptraceARP_TABLE_ENTRY_EXPIRED( xARPCache[ x ].ulIPAddress ); | |
xARPCache[ x ].ulIPAddress = 0UL; | |
} | |
} | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static eARPLookupResult_t prvGetARPCacheEntry( uint32_t *pulIPAddress, xMACAddress_t * const pxMACAddress ) | |
{ | |
portBASE_TYPE x; | |
eARPLookupResult_t eReturn; | |
uint32_t ulAddressToLookup; | |
if( *pulIPAddress == ipBROADCAST_IP_ADDRESS ) | |
{ | |
/* This is a broadcast so uses the broadcast MAC address. */ | |
memcpy( ( void * ) pxMACAddress, &xBroadcastMACAddress, sizeof( xMACAddress_t ) ); | |
eReturn = eARPCacheHit; | |
} | |
else if( *ipLOCAL_IP_ADDRESS_POINTER == 0UL ) | |
{ | |
/* The IP address has not yet been assigned, so there is nothing that | |
can be done. */ | |
eReturn = eCantSendPacket; | |
} | |
else | |
{ | |
if( ( *pulIPAddress & xNetworkAddressing.ulNetMask ) != ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) ) | |
{ | |
/* The IP address is off the local network, so look up the hardware | |
address of the router, if any. */ | |
ulAddressToLookup = xNetworkAddressing.ulGatewayAddress; | |
} | |
else | |
{ | |
/* The IP address is on the local network, so lookup the requested | |
IP address directly. */ | |
ulAddressToLookup = *pulIPAddress; | |
} | |
if( ulAddressToLookup == 0UL ) | |
{ | |
/* The address is not on the local network, and there is not a | |
router. */ | |
eReturn = eCantSendPacket; | |
} | |
else | |
{ | |
eReturn = eARPCacheMiss; | |
/* Loop through each entry in the ARP cache. */ | |
for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) | |
{ | |
/* Does this row in the ARP cache table hold an entry for the IP | |
address being queried? */ | |
if( xARPCache[ x ].ulIPAddress == ulAddressToLookup ) | |
{ | |
/* The IP address matched. Is there a valid MAC address? */ | |
if( memcmp( ( void * ) &xNullMACAddress, ( void * ) &( xARPCache[ x ].xMACAddress ), sizeof( xMACAddress_t ) ) == 0 ) | |
{ | |
/* This entry is waiting an ARP reply, so is not valid. */ | |
eReturn = eCantSendPacket; | |
} | |
else | |
{ | |
/* A valid entry was found. */ | |
memcpy( pxMACAddress, &( xARPCache[ x ].xMACAddress ), sizeof( xMACAddress_t ) ); | |
eReturn = eARPCacheHit; | |
} | |
} | |
if( eReturn != eARPCacheMiss ) | |
{ | |
break; | |
} | |
} | |
if( eReturn == eARPCacheMiss ) | |
{ | |
/* It might be that the ARP has to go to the gateway. */ | |
*pulIPAddress = ulAddressToLookup; | |
} | |
} | |
} | |
return eReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvRefreshARPCacheEntry( const xMACAddress_t * const pxMACAddress, const uint32_t ulIPAddress ) | |
{ | |
portBASE_TYPE x, xEntryFound = pdFALSE, xOldestEntry = 0; | |
uint8_t ucMinAgeFound = 0U; | |
/* Only process the IP address if it is on the local network. */ | |
if( ( ulIPAddress & xNetworkAddressing.ulNetMask ) == ( ( *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) ) | |
{ | |
/* Start with the maximum possible number. */ | |
ucMinAgeFound--; | |
/* For each entry in the ARP cache table. */ | |
for( x = 0; x < ipconfigARP_CACHE_ENTRIES; x++ ) | |
{ | |
/* Does this line in the cache table hold an entry for the IP | |
address being queried? */ | |
if( xARPCache[ x ].ulIPAddress == ulIPAddress ) | |
{ | |
/* If the MAC address is all zeros then the refresh is due to | |
an ARP reply, so in effect this is a new entry in the ARP | |
cache. */ | |
if( memcmp( &( xARPCache[ x ].xMACAddress ), &xNullMACAddress, sizeof( xMACAddress_t ) ) == 0 ) | |
{ | |
iptraceARP_TABLE_ENTRY_CREATED( xARPCache[ x ].ulIPAddress, *pxMACAddress ); | |
} | |
/* Refresh the cache entry so the entry's age is back to its | |
maximum value. */ | |
xARPCache[ x ].ucAge = ipconfigMAX_ARP_AGE; | |
memcpy( &( xARPCache[ x ].xMACAddress ), pxMACAddress, sizeof( xMACAddress_t ) ); | |
xEntryFound = pdTRUE; | |
break; | |
} | |
else | |
{ | |
/* As the table is traversed, remember the table row that | |
contains the oldest entry (the lowest age count, as ages are | |
decremented to zero) so the row can be re-used if this function | |
needs to add an entry that does not already exist. */ | |
if( xARPCache[ x ].ucAge < ucMinAgeFound ) | |
{ | |
ucMinAgeFound = xARPCache[ x ].ucAge; | |
xOldestEntry = x; | |
} | |
} | |
} | |
if( xEntryFound == pdFALSE ) | |
{ | |
/* The wanted entry does not already exist. Add the entry into the | |
cache, replacing the oldest entry (which might be an empty entry). */ | |
xARPCache[ xOldestEntry ].ulIPAddress = ulIPAddress; | |
memcpy( &( xARPCache[ xOldestEntry ].xMACAddress ), pxMACAddress, sizeof( xMACAddress_t ) ); | |
/* If the MAC address is all zeros, then this entry is not yet | |
complete but still waiting the reply from an ARP request. When this | |
is the case the age is set to a much lower value as an ARP | |
retransmission will be generated each time the ARP timer is called | |
while the reply is still outstanding. */ | |
if( pxMACAddress == &xNullMACAddress ) | |
{ | |
xARPCache[ xOldestEntry ].ucAge = ipconfigMAX_ARP_RETRANSMISSIONS; | |
} | |
else | |
{ | |
iptraceARP_TABLE_ENTRY_CREATED( xARPCache[ xOldestEntry ].ulIPAddress, xARPCache[ xOldestEntry ].xMACAddress ); | |
xARPCache[ xOldestEntry ].ucAge = ipconfigMAX_ARP_AGE; | |
} | |
} | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
#if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1 | |
static void prvCalculateFragmentOffsetAndLength( xIPFragmentParameters_t *pxFragmentParameters, uint16_t *pusFragmentOffset, uint16_t *pusFragmentLength ) | |
{ | |
*pusFragmentOffset = pxFragmentParameters->usFragmentedPacketOffset; | |
if( *pusFragmentOffset != 0 ) | |
{ | |
/* Take into account that the payload has had a UDP header added in the | |
first fragment of the set. */ | |
*pusFragmentOffset += sizeof( xUDPHeader_t ); | |
} | |
/* The offset is defined in multiples of 8 bytes. */ | |
*pusFragmentOffset >>= ipSHIFT_TO_DIVIDE_BY_8; | |
*pusFragmentLength = pxFragmentParameters->usFragmentLength; | |
if( ( pxFragmentParameters->ucSocketOptions & FREERTOS_NOT_LAST_IN_FRAGMENTED_PACKET ) != 0 ) | |
{ | |
/* Set the more fragments flag. */ | |
*pusFragmentOffset |= ipMORE_FRAGMENTS_FLAG_BIT; | |
} | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
static void prvCompleteUDPHeader( xNetworkBufferDescriptor_t *pxNetworkBuffer, xUDPPacket_t *pxUDPPacket, uint8_t ucSocketOptions ) | |
{ | |
xUDPHeader_t *pxUDPHeader; | |
pxUDPHeader = &( pxUDPPacket->xUDPHeader ); | |
pxUDPHeader->usDestinationPort = pxNetworkBuffer->usPort; | |
pxUDPHeader->usSourcePort = pxNetworkBuffer->usBoundPort; | |
pxUDPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( xUDPHeader_t ) ); | |
pxUDPHeader->usLength = FreeRTOS_htons( pxUDPPacket->xUDPHeader.usLength ); | |
pxUDPHeader->usChecksum = 0; | |
if( ( ucSocketOptions & FREERTOS_SO_UDPCKSUM_OUT ) != 0U ) | |
{ | |
pxUDPHeader->usChecksum = prvGenerateUDPChecksum( pxUDPPacket, ipconfigETHERNET_DRIVER_ADDS_UDP_CHECKSUM ); | |
if( pxUDPHeader->usChecksum == 0x00 ) | |
{ | |
/* A calculated checksum of 0 must be inverted as 0 means the | |
checksum is disabled. */ | |
pxUDPHeader->usChecksum = 0xffffU; | |
} | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
#if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1 | |
static void prvProcessGeneratedPacket( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) | |
{ | |
xUDPPacket_t *pxUDPPacket; | |
xUDPHeader_t *pxUDPHeader; | |
xIPHeader_t *pxIPHeader; | |
eARPLookupResult_t eReturned; | |
eIPFragmentStatus_t eFragmentStatus; | |
uint16_t usFragmentOffset = 0, usFragmentLength; | |
xIPFragmentParameters_t *pxFragmentParameters; | |
static uint16_t usPacketIdentifier = 0U; | |
/* Map the UDP packet onto the start of the frame. */ | |
pxUDPPacket = ( xUDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer; | |
/* Determine the ARP cache status for the requested IP address. */ | |
eReturned = prvGetARPCacheEntry( &( pxNetworkBuffer->ulIPAddress ), &( pxUDPPacket->xEthernetHeader.xDestinationAddress ) ); | |
if( eReturned != eCantSendPacket ) | |
{ | |
if( eReturned == eARPCacheHit ) | |
{ | |
iptraceSENDING_UDP_PACKET( pxNetworkBuffer->ulIPAddress ); | |
/* Create short cuts to the data within the packet. */ | |
pxUDPHeader = &( pxUDPPacket->xUDPHeader ); | |
pxIPHeader = &( pxUDPPacket->xIPHeader ); | |
pxFragmentParameters = ( xIPFragmentParameters_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipFRAGMENTATION_PARAMETERS_OFFSET ] ); | |
/* IP header source and destination addresses must be set | |
before the UDP checksum is calculated. */ | |
pxIPHeader->ulDestinationIPAddress = pxNetworkBuffer->ulIPAddress; | |
pxIPHeader->ulSourceIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; | |
/* If the packet is not fragmented, or if the packet is the | |
first in a fragmented packet, then a UDP header is required. */ | |
if( ( pxFragmentParameters->ucSocketOptions & FREERTOS_FRAGMENTED_PACKET ) == 0 ) | |
{ | |
eFragmentStatus = eNotFragment; | |
#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) | |
{ | |
/* Is it possible that the packet is not actually a UDP | |
packet after all, but an ICMP packet. */ | |
if( pxNetworkBuffer->usPort != ipPACKET_CONTAINS_ICMP_DATA ) | |
{ | |
prvCompleteUDPHeader( pxNetworkBuffer, pxUDPPacket, pxFragmentParameters->ucSocketOptions ); | |
} | |
} | |
#else /* ipconfigSUPPORT_OUTGOING_PINGS */ | |
{ | |
prvCompleteUDPHeader( pxNetworkBuffer, pxUDPPacket, pxFragmentParameters->ucSocketOptions ); | |
} | |
#endif /* ipconfigSUPPORT_OUTGOING_PINGS */ | |
usFragmentLength = 0U; | |
/* The identifier is incremented as this is a new and | |
unfragmented IP packet. */ | |
usPacketIdentifier++; | |
} | |
else if( pxFragmentParameters->usFragmentedPacketOffset == 0 ) | |
{ | |
eFragmentStatus = eFirstFragment; | |
prvCalculateFragmentOffsetAndLength( pxFragmentParameters, &usFragmentOffset, &usFragmentLength ); | |
/* Note FREERTOS_SO_UDPCKSUM_OUT is used because checksums | |
cannot currently be used on fragmented packets. */ | |
pxFragmentParameters->ucSocketOptions &= ~FREERTOS_SO_UDPCKSUM_OUT; | |
prvCompleteUDPHeader( pxNetworkBuffer, pxUDPPacket, pxFragmentParameters->ucSocketOptions ); | |
/* The identifier is incremented because, although this is a | |
fragmented packet, it is the first in the fragmentation | |
set. */ | |
usPacketIdentifier++; | |
} | |
else | |
{ | |
eFragmentStatus = eFollowingFragment; | |
prvCalculateFragmentOffsetAndLength( pxFragmentParameters, &usFragmentOffset, &usFragmentLength ); | |
} | |
/* memcpy() the constant parts of the header information into the | |
correct location within the packet. This fills in: | |
xEthernetHeader.xSourceAddress | |
xEthernetHeader.usFrameType | |
xIPHeader.ucVersionHeaderLength | |
xIPHeader.ucDifferentiatedServicesCode | |
xIPHeader.usLength | |
xIPHeader.usIdentification | |
xIPHeader.usFragmentOffset | |
xIPHeader.ucTimeToLive | |
xIPHeader.ucProtocol | |
and | |
xIPHeader.usHeaderChecksum | |
*/ | |
memcpy( ( void *) &( pxUDPPacket->xEthernetHeader.xSourceAddress ), ( void * ) xDefaultPartUDPPacketHeader, sizeof( xDefaultPartUDPPacketHeader ) ); | |
/* The fragment status is used to complete the length and | |
fragment offset fields. */ | |
if( eFragmentStatus == eNotFragment ) | |
{ | |
pxIPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( xIPHeader_t ) + sizeof( xUDPHeader_t ) ); | |
} | |
else if( eFragmentStatus == eFirstFragment ) | |
{ | |
pxIPHeader->usFragmentOffset = FreeRTOS_htons( usFragmentOffset ); | |
pxIPHeader->usLength = ( uint16_t ) ( usFragmentLength + sizeof( xIPHeader_t ) + sizeof( xUDPHeader_t ) ); | |
} | |
else | |
{ | |
pxIPHeader->usFragmentOffset = FreeRTOS_htons( usFragmentOffset ); | |
pxIPHeader->usLength = ( uint16_t ) ( usFragmentLength + sizeof( xIPHeader_t ) ); | |
} | |
/* The total transmit size adds on the Ethernet header. */ | |
pxNetworkBuffer->xDataLength = pxIPHeader->usLength + sizeof( xEthernetHeader_t ); | |
pxIPHeader->usLength = FreeRTOS_htons( pxIPHeader->usLength ); | |
pxIPHeader->ulDestinationIPAddress = pxNetworkBuffer->ulIPAddress; | |
pxIPHeader->usIdentification = usPacketIdentifier; | |
pxIPHeader->usHeaderChecksum = prvGenerateChecksum( ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipIP_HEADER_LENGTH, ipconfigETHERNET_DRIVER_ADDS_IP_CHECKSUM ); | |
} | |
else if ( eReturned == eARPCacheMiss ) | |
{ | |
/* Send an ARP for the required IP address. */ | |
iptracePACKET_DROPPED_TO_GENERATE_ARP( pxNetworkBuffer->ulIPAddress ); | |
prvGenerateARPRequestPacket( pxNetworkBuffer ); | |
/* Add an entry to the ARP table with a null hardware address. | |
This allows the ARP timer to know that an ARP reply is | |
outstanding, and perform retransmissions if necessary. */ | |
prvRefreshARPCacheEntry( &xNullMACAddress, pxNetworkBuffer->ulIPAddress ); | |
} | |
else | |
{ | |
/* The lookup indicated that an ARP request has already been | |
sent out for the queried IP address. */ | |
eReturned = eCantSendPacket; | |
} | |
} | |
if( eReturned != eCantSendPacket ) | |
{ | |
/* The network driver is responsible for freeing the network buffer | |
after the packet has been sent. */ | |
xNetworkInterfaceOutput( pxNetworkBuffer ); | |
} | |
else | |
{ | |
/* The packet can't be sent (DHCP not completed?). Just drop the | |
packet. */ | |
vNetworkBufferRelease( pxNetworkBuffer ); | |
} | |
} | |
#else /* ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1 */ | |
static void prvProcessGeneratedPacket( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) | |
{ | |
xUDPPacket_t *pxUDPPacket; | |
xIPHeader_t *pxIPHeader; | |
eARPLookupResult_t eReturned; | |
/* Map the UDP packet onto the start of the frame. */ | |
pxUDPPacket = ( xUDPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer; | |
/* Determine the ARP cache status for the requested IP address. */ | |
eReturned = prvGetARPCacheEntry( &( pxNetworkBuffer->ulIPAddress ), &( pxUDPPacket->xEthernetHeader.xDestinationAddress ) ); | |
if( eReturned != eCantSendPacket ) | |
{ | |
if( eReturned == eARPCacheHit ) | |
{ | |
iptraceSENDING_UDP_PACKET( pxNetworkBuffer->ulIPAddress ); | |
/* Create short cuts to the data within the packet. */ | |
pxIPHeader = &( pxUDPPacket->xIPHeader ); | |
/* IP header source and destination addresses must be set before | |
the UDP checksum is calculated. The socket options, which | |
specify whether a checksum should be calculated or not, are | |
passed in the as yet unused part of the packet data. */ | |
pxIPHeader->ulDestinationIPAddress = pxNetworkBuffer->ulIPAddress; | |
pxIPHeader->ulSourceIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; | |
#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) | |
{ | |
/* Is it possible that the packet is not actually a UDP packet | |
after all, but an ICMP packet. */ | |
if( pxNetworkBuffer->usPort != ipPACKET_CONTAINS_ICMP_DATA ) | |
{ | |
prvCompleteUDPHeader( pxNetworkBuffer, pxUDPPacket, pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] ); | |
} | |
} | |
#else /* ipconfigSUPPORT_OUTGOING_PINGS */ | |
{ | |
prvCompleteUDPHeader( pxNetworkBuffer, pxUDPPacket, pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] ); | |
} | |
#endif /* ipconfigSUPPORT_OUTGOING_PINGS */ | |
/* memcpy() the constant parts of the header information into | |
the correct location within the packet. This fills in: | |
xEthernetHeader.xSourceAddress | |
xEthernetHeader.usFrameType | |
xIPHeader.ucVersionHeaderLength | |
xIPHeader.ucDifferentiatedServicesCode | |
xIPHeader.usLength | |
xIPHeader.usIdentification | |
xIPHeader.usFragmentOffset | |
xIPHeader.ucTimeToLive | |
xIPHeader.ucProtocol | |
and | |
xIPHeader.usHeaderChecksum | |
*/ | |
memcpy( ( void *) &( pxUDPPacket->xEthernetHeader.xSourceAddress ), ( void * ) xDefaultPartUDPPacketHeader, sizeof( xDefaultPartUDPPacketHeader ) ); | |
#if ipconfigSUPPORT_OUTGOING_PINGS == 1 | |
{ | |
if( pxNetworkBuffer->usPort == ipPACKET_CONTAINS_ICMP_DATA ) | |
{ | |
pxIPHeader->ucProtocol = ipPROTOCOL_ICMP; | |
pxIPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( xIPHeader_t ) ); | |
} | |
else | |
{ | |
pxIPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( xIPHeader_t ) + sizeof( xUDPHeader_t ) ); | |
} | |
} | |
#else /* ipconfigSUPPORT_OUTGOING_PINGS */ | |
{ | |
pxIPHeader->usLength = ( uint16_t ) ( pxNetworkBuffer->xDataLength + sizeof( xIPHeader_t ) + sizeof( xUDPHeader_t ) ); | |
} | |
#endif /* ipconfigSUPPORT_OUTGOING_PINGS */ | |
/* The total transmit size adds on the Ethernet header. */ | |
pxNetworkBuffer->xDataLength = pxIPHeader->usLength + sizeof( xEthernetHeader_t ); | |
pxIPHeader->usLength = FreeRTOS_htons( pxIPHeader->usLength ); | |
pxIPHeader->ulDestinationIPAddress = pxNetworkBuffer->ulIPAddress; | |
pxIPHeader->usHeaderChecksum = prvGenerateChecksum( ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipIP_HEADER_LENGTH, ipconfigETHERNET_DRIVER_ADDS_IP_CHECKSUM ); | |
} | |
else if ( eReturned == eARPCacheMiss ) | |
{ | |
/* Generate an ARP for the required IP address. */ | |
iptracePACKET_DROPPED_TO_GENERATE_ARP( pxNetworkBuffer->ulIPAddress ); | |
prvGenerateARPRequestPacket( pxNetworkBuffer ); | |
/* Add an entry to the ARP table with a null hardware address. | |
This allows the ARP timer to know that an ARP reply is | |
outstanding, and perform retransmissions if necessary. */ | |
prvRefreshARPCacheEntry( &xNullMACAddress, pxNetworkBuffer->ulIPAddress ); | |
} | |
else | |
{ | |
/* The lookup indicated that an ARP request has already been | |
sent out for the queried IP address. */ | |
eReturned = eCantSendPacket; | |
} | |
} | |
if( eReturned != eCantSendPacket ) | |
{ | |
/* The network driver is responsible for freeing the network buffer | |
after the packet has been sent. */ | |
xNetworkInterfaceOutput( pxNetworkBuffer ); | |
} | |
else | |
{ | |
/* The packet can't be sent (DHCP not completed?). Just drop the | |
packet. */ | |
vNetworkBufferRelease( pxNetworkBuffer ); | |
} | |
} | |
#endif /* ipconfigCAN_FRAGMENT_OUTGOING_PACKETS == 1 */ | |
/*-----------------------------------------------------------*/ | |
static void prvGenerateARPRequestPacket( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) | |
{ | |
xARPPacket_t *pxARPPacket; | |
pxARPPacket = ( xARPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer; | |
/* memcpy the const part of the header information into the correct | |
location in the packet. This copies: | |
xEthernetHeader.ulDestinationAddress | |
xEthernetHeader.usFrameType; | |
xARPHeader.usHardwareType; | |
xARPHeader.usProtocolType; | |
xARPHeader.ucHardwareAddressLength; | |
xARPHeader.ucProtocolAddressLength; | |
xARPHeader.usOperation; | |
xARPHeader.xTargetHardwareAddress; | |
*/ | |
memcpy( ( void * ) &( pxARPPacket->xEthernetHeader ), ( void * ) xDefaultPartARPPacketHeader, sizeof( xDefaultPartARPPacketHeader ) ); | |
memcpy( ( void * ) &( pxARPPacket->xEthernetHeader.xSourceAddress ) , ( void * ) ipLOCAL_MAC_ADDRESS, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); | |
memcpy( ( void * ) &( pxARPPacket->xARPHeader.xSenderHardwareAddress ), ( void * ) ipLOCAL_MAC_ADDRESS, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); | |
pxARPPacket->xARPHeader.ulSenderProtocolAddress = *ipLOCAL_IP_ADDRESS_POINTER; | |
pxARPPacket->xARPHeader.ulTargetProtocolAddress = pxNetworkBuffer->ulIPAddress; | |
pxNetworkBuffer->xDataLength = sizeof( xARPPacket_t ); | |
iptraceCREATING_ARP_REQUEST( pxNetworkBuffer->ulIPAddress ); | |
} | |
/*-----------------------------------------------------------*/ | |
eFrameProcessingResult_t eConsiderFrameForProcessing( const uint8_t * const pucEthernetBuffer ) | |
{ | |
eFrameProcessingResult_t eReturn; | |
const xEthernetHeader_t *pxEthernetHeader; | |
pxEthernetHeader = ( const xEthernetHeader_t * ) pucEthernetBuffer; | |
if( memcmp( ( void * ) &xBroadcastMACAddress, ( void * ) &( pxEthernetHeader->xDestinationAddress ), sizeof( xMACAddress_t ) ) == 0 ) | |
{ | |
/* The packet was a broadcast - process it. */ | |
eReturn = eProcessBuffer; | |
} | |
else if( memcmp( ( void * ) ipLOCAL_MAC_ADDRESS, ( void * ) &( pxEthernetHeader->xDestinationAddress ), sizeof( xMACAddress_t ) ) == 0 ) | |
{ | |
/* The packet was to this node directly - process it. */ | |
eReturn = eProcessBuffer; | |
} | |
else | |
{ | |
/* The packet was not a broadcast, or for this node, just release | |
the buffer without taking any other action. */ | |
eReturn = eReleaseBuffer; | |
} | |
#if ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES == 1 | |
{ | |
uint16_t usFrameType; | |
if( eReturn == eProcessBuffer ) | |
{ | |
usFrameType = pxEthernetHeader->usFrameType; | |
usFrameType = FreeRTOS_ntohs( usFrameType ); | |
if( usFrameType <= 0x600U ) | |
{ | |
/* Not an Ethernet II frame. */ | |
eReturn = eReleaseBuffer; | |
} | |
} | |
} | |
#endif /* ipconfigFILTER_OUT_NON_ETHERNET_II_FRAMES == 1 */ | |
return eReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvProcessNetworkDownEvent( void ) | |
{ | |
/* Stop the ARP timer while there is no network. */ | |
xTimerStop( xARPTimer, portMAX_DELAY ); | |
#if ipconfigUSE_NETWORK_EVENT_HOOK == 1 | |
{ | |
static portBASE_TYPE xCallEventHook = pdFALSE; | |
/* The first network down event is generated by the IP stack | |
itself to initialise the network hardware, so do not call the | |
network down event the first time through. */ | |
if( xCallEventHook == pdTRUE ) | |
{ | |
vApplicationIPNetworkEventHook( eNetworkDown ); | |
} | |
xCallEventHook = pdTRUE; | |
} | |
#endif | |
/* The network has been disconnected (or is being | |
initialised for the first time). Perform whatever hardware | |
processing is necessary to bring it up again, or wait for it | |
to be available again. This is hardware dependent. */ | |
if( xNetworkInterfaceInitialise() != pdPASS ) | |
{ | |
/* Ideally the network interface initialisation function | |
will only return when the network is available. In case | |
this is not the case, wait a while before retrying the | |
initialisation. */ | |
vTaskDelay( ipINITIALISATION_RETRY_DELAY ); | |
FreeRTOS_NetworkDown(); | |
} | |
else | |
{ | |
/* Start the ARP timer. */ | |
xTimerStart( xARPTimer, portMAX_DELAY ); | |
#if ipconfigUSE_DHCP == 1 | |
{ | |
/* The network is not up until DHCP has completed. */ | |
vDHCPProcess( pdTRUE, ( xMACAddress_t * ) ipLOCAL_MAC_ADDRESS, ipLOCAL_IP_ADDRESS_POINTER, &xNetworkAddressing ); | |
prvSendEventToIPTask( eDHCPEvent ); | |
} | |
#else | |
{ | |
#if ipconfigUSE_NETWORK_EVENT_HOOK == 1 | |
{ | |
vApplicationIPNetworkEventHook( eNetworkUp ); | |
} | |
#endif /* ipconfigUSE_NETWORK_EVENT_HOOK */ | |
/* Static configuration is being used, so the network is now up. */ | |
#if ipconfigFREERTOS_PLUS_NABTO == 1 | |
{ | |
/* Return value is used in configASSERT() inside the | |
function. */ | |
( void ) xStartNabtoTask(); | |
} | |
#endif /* ipconfigFREERTOS_PLUS_NABTO */ | |
} | |
#endif | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvProcessEthernetPacket( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) | |
{ | |
xEthernetHeader_t *pxEthernetHeader; | |
volatile eFrameProcessingResult_t eReturned; /* Volatile to prevent complier warnings when ipCONSIDER_FRAME_FOR_PROCESSING just sets it to eProcessBuffer. */ | |
configASSERT( pxNetworkBuffer ); | |
/* Interpret the Ethernet frame. */ | |
eReturned = ipCONSIDER_FRAME_FOR_PROCESSING( pxNetworkBuffer->pucEthernetBuffer ); | |
pxEthernetHeader = ( xEthernetHeader_t * ) pxNetworkBuffer->pucEthernetBuffer; | |
if( eReturned == eProcessBuffer ) | |
{ | |
/* Interpret the received Ethernet packet. */ | |
switch ( pxEthernetHeader->usFrameType ) | |
{ | |
case ipARP_TYPE : | |
/* The Ethernet frame contains an ARP packet. */ | |
eReturned = prvProcessARPPacket( ( xARPPacket_t * ) pxEthernetHeader ); | |
break; | |
case ipIP_TYPE : | |
/* The Ethernet frame contains an IP packet. */ | |
eReturned = prvProcessIPPacket( ( xIPPacket_t * ) pxEthernetHeader, pxNetworkBuffer ); | |
break; | |
default : | |
/* No other packet types are handled. Nothing to do. */ | |
eReturned = eReleaseBuffer; | |
break; | |
} | |
} | |
/* Perform any actions that resulted from processing the Ethernet | |
frame. */ | |
switch( eReturned ) | |
{ | |
case eReturnEthernetFrame : | |
/* The Ethernet frame will have been updated (maybe it was | |
an ARP request or a PING request?) and should be sent back to | |
its source. */ | |
prvReturnEthernetFrame( pxNetworkBuffer ); | |
/* The buffer must be released once | |
the frame has been transmitted. */ | |
break; | |
case eFrameConsumed : | |
/* The frame is in use somewhere, don't release the buffer | |
yet. */ | |
break; | |
default : | |
/* The frame is not being used anywhere, and the | |
xNetworkBufferDescriptor_t structure containing the frame should just be | |
released back to the list of free buffers. */ | |
vNetworkBufferRelease( pxNetworkBuffer ); | |
break; | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static eFrameProcessingResult_t prvProcessIPPacket( const xIPPacket_t * const pxIPPacket, xNetworkBufferDescriptor_t * const pxNetworkBuffer ) | |
{ | |
eFrameProcessingResult_t eReturn = eReleaseBuffer; | |
const xIPHeader_t * pxIPHeader; | |
xUDPPacket_t *pxUDPPacket; | |
portBASE_TYPE xChecksumIsCorrect; | |
pxIPHeader = &( pxIPPacket->xIPHeader ); | |
/* Is the packet for this node? */ | |
if( ( pxIPHeader->ulDestinationIPAddress == *ipLOCAL_IP_ADDRESS_POINTER ) || ( pxIPHeader->ulDestinationIPAddress == ipBROADCAST_IP_ADDRESS ) || ( *ipLOCAL_IP_ADDRESS_POINTER == 0 ) ) | |
{ | |
/* Ensure the frame is IPv4 with no options bytes, and that the incoming | |
packet is not fragmented (only outgoing packets can be fragmented) as | |
these are the only handled IP frames currently. */ | |
if( ( pxIPHeader->ucVersionHeaderLength == ipIP_VERSION_AND_HEADER_LENGTH_BYTE ) && ( ( pxIPHeader->usFragmentOffset & ipFRAGMENT_OFFSET_BIT_MASK ) == 0U ) ) | |
{ | |
/* Is the IP header checksum correct? */ | |
if( prvGenerateChecksum( ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipIP_HEADER_LENGTH, ipconfigETHERNET_DRIVER_CHECKS_IP_CHECKSUM ) == 0 ) | |
{ | |
/* Add the IP and MAC addresses to the ARP table if they are not | |
already there - otherwise refresh the age of the existing | |
entry. */ | |
prvRefreshARPCacheEntry( &( pxIPPacket->xEthernetHeader.xSourceAddress ), pxIPHeader->ulSourceIPAddress ); | |
switch( pxIPHeader->ucProtocol ) | |
{ | |
case ipPROTOCOL_ICMP : | |
/* The IP packet contained an ICMP frame. Don't bother | |
checking the ICMP checksum, as if it is wrong then the | |
wrong data will also be returned, and the source of the | |
ping will know something went wrong because it will not | |
be able to validate what it receives. */ | |
#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) | |
{ | |
if( pxIPHeader->ulDestinationIPAddress == *ipLOCAL_IP_ADDRESS_POINTER ) | |
{ | |
eReturn = prvProcessICMPPacket( ( xICMPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ) ); | |
} | |
} | |
#endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ | |
break; | |
case ipPROTOCOL_UDP : | |
/* The IP packet contained a UDP frame. */ | |
pxUDPPacket = ( xUDPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); | |
/* Note the header values required prior to the | |
checksum generation as the checksum pseudo header | |
may clobber some of these values. */ | |
pxNetworkBuffer->xDataLength = FreeRTOS_ntohs( pxUDPPacket->xUDPHeader.usLength ) - sizeof( xUDPHeader_t ); | |
pxNetworkBuffer->usPort = pxUDPPacket->xUDPHeader.usSourcePort; | |
pxNetworkBuffer->ulIPAddress = pxUDPPacket->xIPHeader.ulSourceIPAddress; | |
/* Is the checksum required? */ | |
if( pxUDPPacket->xUDPHeader.usChecksum == 0 ) | |
{ | |
xChecksumIsCorrect = pdTRUE; | |
} | |
else if( prvGenerateUDPChecksum( pxUDPPacket, ipconfigETHERNET_DRIVER_CHECKS_UDP_CHECKSUM ) == 0 ) | |
{ | |
xChecksumIsCorrect = pdTRUE; | |
} | |
else | |
{ | |
xChecksumIsCorrect = pdFALSE; | |
} | |
/* Is the checksum correct? */ | |
if( xChecksumIsCorrect == pdTRUE ) | |
{ | |
/* Pass the packet payload to the UDP sockets | |
implementation. */ | |
if( xProcessReceivedUDPPacket( pxNetworkBuffer, pxUDPPacket->xUDPHeader.usDestinationPort ) == pdPASS ) | |
{ | |
eReturn = eFrameConsumed; | |
} | |
} | |
break; | |
default : | |
/* Not a supported frame type. */ | |
break; | |
} | |
} | |
} | |
} | |
return eReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
static uint16_t prvGenerateUDPChecksum( const xUDPPacket_t * const pxUDPPacket, portBASE_TYPE xChecksumIsOffloaded ) | |
{ | |
xPseudoHeader_t *pxPseudoHeader; | |
uint16_t usLength, usReturn; | |
if( xChecksumIsOffloaded == pdFALSE ) | |
{ | |
/* Map the pseudo header into the correct place within the real IP | |
header. */ | |
pxPseudoHeader = ( xPseudoHeader_t * ) &( pxUDPPacket->xIPHeader.ucTimeToLive ); | |
/* Ordering here is important so as not to overwrite data that is required | |
but has not yet been used as the pseudo header overlaps the information | |
that is being copied into it. */ | |
pxPseudoHeader->ulSourceAddress = pxUDPPacket->xIPHeader.ulSourceIPAddress; | |
pxPseudoHeader->ulDestinationAddress = pxUDPPacket->xIPHeader.ulDestinationIPAddress; | |
pxPseudoHeader->ucZeros = 0x00; | |
pxPseudoHeader->ucProtocol = ipPROTOCOL_UDP; | |
pxPseudoHeader->usUDPLength = pxUDPPacket->xUDPHeader.usLength; | |
usLength = FreeRTOS_ntohs( pxPseudoHeader->usUDPLength ); | |
usReturn = prvGenerateChecksum( ( uint8_t * ) pxPseudoHeader, usLength + sizeof( xPseudoHeader_t ), pdFALSE ); | |
} | |
else | |
{ | |
/* The hardware will check the checksum. Returning 0 allows this | |
function to be used to both check an incoming checksum and set an | |
outgoing checksum in this case. */ | |
usReturn = 0; | |
} | |
return usReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) | |
static void prvProcessICMPEchoReply( xICMPPacket_t * const pxICMPPacket ) | |
{ | |
ePingReplyStatus_t eStatus = eSuccess; | |
uint16_t usDataLength, usCount; | |
uint8_t *pucByte; | |
/* Find the total length of the IP packet. */ | |
usDataLength = pxICMPPacket->xIPHeader.usLength; | |
usDataLength = FreeRTOS_ntohs( usDataLength ); | |
/* Remove the length of the IP headers to obtain the length of the ICMP | |
message itself. */ | |
usDataLength -= sizeof( xIPHeader_t ); | |
if( prvGenerateChecksum( ( uint8_t * ) &( pxICMPPacket->xICMPHeader ), usDataLength, pdFALSE ) != 0 ) | |
{ | |
eStatus = eInvalidChecksum; | |
} | |
else | |
{ | |
/* Remove the length of the ICMP header, to obtain the length of | |
data contained in the ping. */ | |
usDataLength -= sizeof( xICMPHeader_t ); | |
/* Find the first byte of the data within the ICMP packet. */ | |
pucByte = ( uint8_t * ) pxICMPPacket; | |
pucByte += sizeof( xICMPPacket_t ); | |
/* Check each byte. */ | |
for( usCount = 0; usCount < usDataLength; usCount++ ) | |
{ | |
if( *pucByte != ipECHO_DATA_FILL_BYTE ) | |
{ | |
eStatus = eInvalidData; | |
break; | |
} | |
pucByte++; | |
} | |
} | |
vApplicationPingReplyHook( eStatus, pxICMPPacket->xICMPHeader.usIdentifier ); | |
} | |
#endif | |
/*-----------------------------------------------------------*/ | |
#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) | |
static eFrameProcessingResult_t prvProcessICMPEchoRequest( xICMPPacket_t * const pxICMPPacket ) | |
{ | |
xICMPHeader_t *pxICMPHeader; | |
xIPHeader_t *pxIPHeader; | |
pxICMPHeader = &( pxICMPPacket->xICMPHeader ); | |
pxIPHeader = &( pxICMPPacket->xIPHeader ); | |
iptraceSENDING_PING_REPLY( pxIPHeader->ulSourceIPAddress ); | |
/* The checksum can be checked here - but a ping reply should be | |
returned even if the checksum is incorrect so the other end can | |
tell that the ping was received - even if the ping reply contains | |
invalid data. */ | |
pxICMPHeader->ucTypeOfMessage = ipICMP_ECHO_REPLY; | |
pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress; | |
pxIPHeader->ulSourceIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; | |
/* Update the checksum because the ucTypeOfMessage member in the | |
header has been changed to ipICMP_ECHO_REPLY. */ | |
if( pxICMPHeader->usChecksum >= FreeRTOS_htons( ( ( uint16_t ) 0xffffU ) - ( ipICMP_ECHO_REQUEST << ( ( uint16_t ) 8U ) ) ) ) | |
{ | |
pxICMPHeader->usChecksum += FreeRTOS_htons( ipICMP_ECHO_REQUEST << ( ( uint16_t ) 8U ) ) + ( uint16_t ) 1U; | |
} | |
else | |
{ | |
pxICMPHeader->usChecksum += FreeRTOS_htons( ipICMP_ECHO_REQUEST << ( ( uint16_t ) 8U ) ); | |
} | |
return eReturnEthernetFrame; | |
} | |
#endif /* ipconfigREPLY_TO_INCOMING_PINGS == 1 */ | |
/*-----------------------------------------------------------*/ | |
#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) | |
static eFrameProcessingResult_t prvProcessICMPPacket( xICMPPacket_t * const pxICMPPacket ) | |
{ | |
eFrameProcessingResult_t eReturn = eReleaseBuffer; | |
iptraceICMP_PACKET_RECEIVED(); | |
switch( pxICMPPacket->xICMPHeader.ucTypeOfMessage ) | |
{ | |
case ipICMP_ECHO_REQUEST : | |
#if ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) | |
{ | |
eReturn = prvProcessICMPEchoRequest( pxICMPPacket ); | |
} | |
#endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) */ | |
break; | |
case ipICMP_ECHO_REPLY : | |
#if ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) | |
{ | |
prvProcessICMPEchoReply( pxICMPPacket ); | |
} | |
#endif /* ipconfigSUPPORT_OUTGOING_PINGS */ | |
break; | |
default : | |
break; | |
} | |
return eReturn; | |
} | |
#endif /* ( ipconfigREPLY_TO_INCOMING_PINGS == 1 ) || ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) */ | |
/*-----------------------------------------------------------*/ | |
static uint16_t prvGenerateChecksum( const uint8_t * const pucNextData, const uint16_t usDataLengthBytes, portBASE_TYPE xChecksumIsOffloaded ) | |
{ | |
uint32_t ulChecksum = 0; | |
uint16_t us, usDataLength16BitWords, *pusNextData, usReturn; | |
if( xChecksumIsOffloaded == pdFALSE ) | |
{ | |
/* There are half as many 16 bit words than bytes. */ | |
usDataLength16BitWords = ( usDataLengthBytes >> 1U ); | |
pusNextData = ( uint16_t * ) pucNextData; | |
for( us = 0U; us < usDataLength16BitWords; us++ ) | |
{ | |
ulChecksum += ( uint32_t ) pusNextData[ us ]; | |
} | |
if( ( usDataLengthBytes & 0x01U ) != 0x00 ) | |
{ | |
/* There is one byte left over. */ | |
#if ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN | |
{ | |
ulChecksum += ( uint32_t ) pucNextData[ usDataLengthBytes - 1 ]; | |
} | |
#else | |
{ | |
us = ( uint16_t ) pucNextData[ usDataLengthBytes - 1 ]; | |
ulChecksum += ( uint32_t ) ( us << 8 ); | |
} | |
#endif | |
} | |
while( ( ulChecksum >> 16UL ) != 0x00UL ) | |
{ | |
ulChecksum = ( ulChecksum & 0xffffUL ) + ( ulChecksum >> 16UL ); | |
} | |
usReturn = ~( ( uint16_t ) ulChecksum ); | |
} | |
else | |
{ | |
/* The checksum is calculated by the hardware. Return 0 here to ensure | |
this works for both incoming and outgoing checksums. */ | |
usReturn = 0; | |
} | |
return usReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvReturnEthernetFrame( xNetworkBufferDescriptor_t * const pxNetworkBuffer ) | |
{ | |
xEthernetHeader_t *pxEthernetHeader; | |
pxEthernetHeader = ( xEthernetHeader_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); | |
/* Swap source and destination MAC addresses. */ | |
memcpy( ( void * ) &( pxEthernetHeader->xDestinationAddress ), ( void * ) &( pxEthernetHeader->xSourceAddress ), sizeof( pxEthernetHeader->xDestinationAddress ) ); | |
memcpy( ( void * ) &( pxEthernetHeader->xSourceAddress) , ( void * ) ipLOCAL_MAC_ADDRESS, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); | |
/* Send! */ | |
xNetworkInterfaceOutput( pxNetworkBuffer ); | |
} | |
/*-----------------------------------------------------------*/ | |
static eFrameProcessingResult_t prvProcessARPPacket( xARPPacket_t * const pxARPFrame ) | |
{ | |
eFrameProcessingResult_t eReturn = eReleaseBuffer; | |
xARPHeader_t *pxARPHeader; | |
pxARPHeader = &( pxARPFrame->xARPHeader ); | |
traceARP_PACKET_RECEIVED(); | |
/* Sanity check the protocol type. Don't do anything if the local IP | |
address is zero because that means a DHCP request has not completed. */ | |
if( ( pxARPHeader->usProtocolType == ipARP_PROTOCOL_TYPE ) && ( *ipLOCAL_IP_ADDRESS_POINTER != 0UL ) ) | |
{ | |
switch( pxARPHeader->usOperation ) | |
{ | |
case ipARP_REQUEST : | |
/* The packet contained an ARP request. Was it for the IP | |
address of the node running this code? */ | |
if( pxARPHeader->ulTargetProtocolAddress == *ipLOCAL_IP_ADDRESS_POINTER ) | |
{ | |
iptraceSENDING_ARP_REPLY( pxARPHeader->ulSenderProtocolAddress ); | |
/* The request is for the address of this node. Add the | |
entry into the ARP cache, or refresh the entry if it | |
already exists. */ | |
prvRefreshARPCacheEntry( &( pxARPHeader->xSenderHardwareAddress ), pxARPHeader->ulSenderProtocolAddress ); | |
/* Generate a reply payload in the same buffer. */ | |
pxARPHeader->usOperation = ipARP_REPLY; | |
memcpy( ( void * ) &( pxARPHeader->xTargetHardwareAddress ), ( void * ) &( pxARPHeader->xSenderHardwareAddress ), sizeof( xMACAddress_t ) ); | |
pxARPHeader->ulTargetProtocolAddress = pxARPHeader->ulSenderProtocolAddress; | |
memcpy( ( void * ) &( pxARPHeader->xSenderHardwareAddress ), ( void * ) ipLOCAL_MAC_ADDRESS, sizeof( xMACAddress_t ) ); | |
pxARPHeader->ulSenderProtocolAddress = *ipLOCAL_IP_ADDRESS_POINTER; | |
eReturn = eReturnEthernetFrame; | |
} | |
break; | |
case ipARP_REPLY : | |
iptracePROCESSING_RECEIVED_ARP_REPLY( pxARPHeader->ulTargetProtocolAddress ); | |
prvRefreshARPCacheEntry( &( pxARPHeader->xSenderHardwareAddress ), pxARPHeader->ulSenderProtocolAddress ); | |
break; | |
default : | |
/* Invalid. */ | |
break; | |
} | |
} | |
return eReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN ) | |
uint16_t FreeRTOS_htons( uint16_t usIn ) | |
{ | |
return ( ( usIn & ( uint16_t ) 0x00ff ) << ( uint16_t ) 8U ) | | |
( ( usIn & ( uint16_t ) 0xff00 ) >> ( uint16_t ) 8U ); | |
} | |
#endif /* ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN */ | |
/*-----------------------------------------------------------*/ | |
#if( ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN ) | |
uint32_t FreeRTOS_htonl( uint32_t ulIn ) | |
{ | |
return ( ( ulIn & 0x000000ffUL ) << 24UL ) | | |
( ( ulIn & 0x0000ff00UL ) << 8UL ) | | |
( ( ulIn & 0x00ff0000UL ) >> 8UL ) | | |
( ( ulIn & 0xff000000UL ) >> 24UL ); | |
} | |
#endif /* ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN */ | |
/*-----------------------------------------------------------*/ | |