forked from FreeRTOS/FreeRTOS-Plus-TCP
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFreeRTOS_IP_Utils.c
More file actions
1903 lines (1648 loc) · 69.1 KB
/
FreeRTOS_IP_Utils.c
File metadata and controls
1903 lines (1648 loc) · 69.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* FreeRTOS+TCP <DEVELOPMENT BRANCH>
* 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
*/
/**
* @file FreeRTOS_IP_Utils.c
* @brief Implements the basic functionality for the FreeRTOS+TCP network stack.
*/
/* Standard includes. */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_IP_Utils.h"
#include "FreeRTOS_IP_Timers.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_IP_Private.h"
#include "FreeRTOS_ARP.h"
#include "FreeRTOS_ND.h"
#include "FreeRTOS_UDP_IP.h"
#include "FreeRTOS_DHCP.h"
#include "NetworkInterface.h"
#include "NetworkBufferManagement.h"
#include "FreeRTOS_DNS.h"
#include "FreeRTOS_Routing.h"
/*-----------------------------------------------------------*/
/* Used to ensure the structure packing is having the desired effect. The
* 'volatile' is used to prevent compiler warnings about comparing a constant with
* a constant. */
#ifndef _lint
#define ipEXPECTED_EthernetHeader_t_SIZE ( ( size_t ) 14 ) /**< Ethernet Header size in bytes. */
#define ipEXPECTED_ARPHeader_t_SIZE ( ( size_t ) 28 ) /**< ARP header size in bytes. */
#define ipEXPECTED_IPHeader_t_SIZE ( ( size_t ) 20 ) /**< IP header size in bytes. */
#define ipEXPECTED_IGMPHeader_t_SIZE ( ( size_t ) 8 ) /**< IGMP header size in bytes. */
#define ipEXPECTED_ICMPHeader_t_SIZE ( ( size_t ) 8 ) /**< ICMP header size in bytes. */
#define ipEXPECTED_UDPHeader_t_SIZE ( ( size_t ) 8 ) /**< UDP header size in bytes. */
#define ipEXPECTED_TCPHeader_t_SIZE ( ( size_t ) 20 ) /**< TCP header size in bytes. */
#endif
/** @brief Time delay between repeated attempts to initialise the network hardware. */
#ifndef ipINITIALISATION_RETRY_DELAY
#define ipINITIALISATION_RETRY_DELAY ( pdMS_TO_TICKS( 3000U ) )
#endif
/** @brief The minimum value of TCP offset value. */
#define FREERTOS_MINIMUM_TCP_OFFSET ( 5U )
#if ( ipconfigHAS_PRINTF != 0 )
/** @brief Last value of minimum buffer count. */
static UBaseType_t uxLastMinBufferCount = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS;
/** @brief Last value of minimum size. Used in printing resource stats. */
static size_t uxMinLastSize = 0u;
#endif
#if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) && ( ipconfigHAS_PRINTF != 0 )
static UBaseType_t uxLastMinQueueSpace = 0;
#endif
/**
* Used in checksum calculation.
*/
typedef union xUnion32
{
uint32_t u32; /**< The 32-bit member of the union. */
uint16_t u16[ 2 ]; /**< The array of 2 16-bit members of the union. */
uint8_t u8[ 4 ]; /**< The array of 4 8-bit members of the union. */
} xUnion32_t;
/**
* Used in checksum calculation.
*/
typedef union xUnionPtr
{
const uint32_t * u32ptr; /**< The pointer member to a 32-bit variable. */
const uint16_t * u16ptr; /**< The pointer member to a 16-bit variable. */
const uint8_t * u8ptr; /**< The pointer member to an 8-bit variable. */
} xUnionPtr_t;
/*
* Returns the network buffer descriptor that owns a given packet buffer.
*/
static NetworkBufferDescriptor_t * prvPacketBuffer_to_NetworkBuffer( const void * pvBuffer,
size_t uxOffset );
static uintptr_t void_ptr_to_uintptr( const void * pvPointer );
static BaseType_t prvChecksumProtocolChecks( size_t uxBufferLength,
struct xPacketSummary * pxSet );
static BaseType_t prvChecksumProtocolMTUCheck( struct xPacketSummary * pxSet );
static void prvChecksumProtocolCalculate( BaseType_t xOutgoingPacket,
const uint8_t * pucEthernetBuffer,
struct xPacketSummary * pxSet );
static void prvChecksumProtocolSetChecksum( BaseType_t xOutgoingPacket,
const uint8_t * pucEthernetBuffer,
size_t uxBufferLength,
const struct xPacketSummary * pxSet );
static void prvSetChecksumInPacket( const struct xPacketSummary * pxSet,
uint16_t usChecksum );
static uint16_t prvGetChecksumFromPacket( const struct xPacketSummary * pxSet );
/**
* @brief Set checksum in the packet
*
* @param pxSet Pointer to the packet summary that describes the packet,
* to which the checksum will be set.
*
* @param usChecksum Checksum value to be set.
*/
static void prvSetChecksumInPacket( const struct xPacketSummary * pxSet,
uint16_t usChecksum )
{
if( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_UDP )
{
pxSet->pxProtocolHeaders->xUDPHeader.usChecksum = usChecksum;
}
else if( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_TCP )
{
pxSet->pxProtocolHeaders->xTCPHeader.usChecksum = usChecksum;
}
else if( ( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) ||
( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) )
{
pxSet->pxProtocolHeaders->xICMPHeader.usChecksum = usChecksum;
}
else if( ( pxSet->xIsIPv6 != pdFALSE ) && ( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP_IPv6 ) )
{
pxSet->pxProtocolHeaders->xICMPHeaderIPv6.usChecksum = usChecksum;
}
else
{
/* Unhandled protocol. */
}
}
/**
* @brief Get checksum from the packet summary
*
* @param pxSet Pointer to the packet summary that describes the packet,
* from which the checksum will be retrieved.
*
* @return Checksum value that is retrieved from pxSet.
*/
static uint16_t prvGetChecksumFromPacket( const struct xPacketSummary * pxSet )
{
uint16_t usChecksum;
if( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_UDP )
{
usChecksum = pxSet->pxProtocolHeaders->xUDPHeader.usChecksum;
}
else if( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_TCP )
{
usChecksum = pxSet->pxProtocolHeaders->xTCPHeader.usChecksum;
}
else if( ( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) ||
( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) )
{
usChecksum = pxSet->pxProtocolHeaders->xICMPHeader.usChecksum;
}
else if( ( pxSet->xIsIPv6 != pdFALSE ) && ( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP_IPv6 ) )
{
usChecksum = pxSet->pxProtocolHeaders->xICMPHeaderIPv6.usChecksum;
}
else
{
/* Unhandled protocol. */
usChecksum = ipUNHANDLED_PROTOCOL;
}
return usChecksum;
}
#if ( ( ipconfigUSE_DHCPv6 == 1 ) || ( ipconfigUSE_DHCP == 1 ) || ( ipconfigUSE_RA == 1 ) )
/**
* @brief Create a DHCP event.
*
* @return pdPASS or pdFAIL, depending on whether xSendEventStructToIPTask()
* succeeded.
* @param pxEndPoint The end-point that needs DHCP.
*/
BaseType_t xSendDHCPEvent( struct xNetworkEndPoint * pxEndPoint )
{
IPStackEvent_t xEventMessage;
const TickType_t uxDontBlock = 0U;
#if ( ( ipconfigUSE_DHCPv6 == 1 ) || ( ipconfigUSE_DHCP == 1 ) )
eDHCPState_t uxOption = eGetDHCPState( pxEndPoint );
#endif
xEventMessage.eEventType = eDHCPEvent;
/* MISRA Ref 11.6.1 [DHCP events and conversion to void] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-116 */
/* coverity[misra_c_2012_rule_11_6_violation] */
xEventMessage.pvData = ( void * ) pxEndPoint;
#if ( ( ipconfigUSE_DHCPv6 == 1 ) || ( ipconfigUSE_DHCP == 1 ) )
{
pxEndPoint->xDHCPData.eExpectedState = uxOption;
}
#endif
return xSendEventStructToIPTask( &xEventMessage, uxDontBlock );
}
#endif /* if ( ( ipconfigUSE_DHCPv6 == 1 ) || ( ipconfigUSE_DHCP == 1 ) || ( ipconfigUSE_RA == 1 ) ) */
/*-----------------------------------------------------------*/
/**
* @brief Duplicate the given network buffer descriptor with a modified length.
*
* @param[in] pxNetworkBuffer The network buffer to be duplicated.
* @param[in] uxNewLength The length for the new buffer.
*
* @return If properly duplicated, then the duplicate network buffer or else, NULL.
*/
NetworkBufferDescriptor_t * pxDuplicateNetworkBufferWithDescriptor( const NetworkBufferDescriptor_t * const pxNetworkBuffer,
size_t uxNewLength )
{
NetworkBufferDescriptor_t * pxNewBuffer;
size_t uxLengthToCopy = uxNewLength;
/* This function is only used when 'ipconfigZERO_COPY_TX_DRIVER' is set to 1.
* The transmit routine wants to have ownership of the network buffer
* descriptor, because it will pass the buffer straight to DMA. */
pxNewBuffer = pxGetNetworkBufferWithDescriptor( uxNewLength, ( TickType_t ) 0 );
if( pxNewBuffer != NULL )
{
configASSERT( pxNewBuffer->pucEthernetBuffer != NULL );
/* Get the minimum of both values to copy the data. */
if( uxLengthToCopy > pxNetworkBuffer->xDataLength )
{
uxLengthToCopy = pxNetworkBuffer->xDataLength;
}
/* Set the actual packet size in case a bigger buffer than requested
* was returned. */
pxNewBuffer->xDataLength = uxNewLength;
/* Copy the original packet information. */
pxNewBuffer->xIPAddress.ulIP_IPv4 = pxNetworkBuffer->xIPAddress.ulIP_IPv4;
pxNewBuffer->usPort = pxNetworkBuffer->usPort;
pxNewBuffer->usBoundPort = pxNetworkBuffer->usBoundPort;
pxNewBuffer->pxInterface = pxNetworkBuffer->pxInterface;
pxNewBuffer->pxEndPoint = pxNetworkBuffer->pxEndPoint;
( void ) memcpy( pxNewBuffer->pucEthernetBuffer, pxNetworkBuffer->pucEthernetBuffer, uxLengthToCopy );
#if ( ipconfigUSE_IPv6 != 0 )
if( uxIPHeaderSizePacket( pxNewBuffer ) == ipSIZE_OF_IPv6_HEADER )
{
( void ) memcpy( pxNewBuffer->xIPAddress.xIP_IPv6.ucBytes, pxNetworkBuffer->xIPAddress.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
}
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
}
return pxNewBuffer;
}
/*-----------------------------------------------------------*/
/**
* @brief Get the network buffer descriptor from the packet buffer.
*
* @param[in] pvBuffer The pointer to packet buffer.
* @param[in] uxOffset Additional offset (such as the packet length of UDP packet etc.).
*
* @return The network buffer descriptor if the alignment is correct. Else a NULL is returned.
*/
static NetworkBufferDescriptor_t * prvPacketBuffer_to_NetworkBuffer( const void * pvBuffer,
size_t uxOffset )
{
uintptr_t uxBuffer;
NetworkBufferDescriptor_t * pxResult;
if( pvBuffer == NULL )
{
pxResult = NULL;
}
else
{
/* Obtain the network buffer from the zero copy pointer. */
/* MISRA Ref 11.6.2 [Pointer arithmetic and hidden pointer] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-116 */
/* coverity[misra_c_2012_rule_11_6_violation] */
uxBuffer = void_ptr_to_uintptr( pvBuffer );
/* The input here is a pointer to a packet buffer plus some offset. Subtract
* this offset, and also the size of the header in the network buffer, usually
* 8 + 2 bytes. */
uxBuffer -= ( uxOffset + ipBUFFER_PADDING );
/* Here a pointer was placed to the network descriptor. As a
* pointer is dereferenced, make sure it is well aligned. */
if( ( uxBuffer & ( ( ( uintptr_t ) sizeof( uxBuffer ) ) - 1U ) ) == ( uintptr_t ) 0U )
{
/* MISRA Ref 11.4.2 [Validation of pointer alignment] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
/* coverity[misra_c_2012_rule_11_4_violation] */
pxResult = *( ( NetworkBufferDescriptor_t ** ) uxBuffer );
}
else
{
pxResult = NULL;
}
}
return pxResult;
}
/*-----------------------------------------------------------*/
/**
* @brief uintptr_t is an unsigned integer type that is capable of storing a data pointer.
* Therefore it is safe to convert from a void pointer to a uintptr_t, using a union.
*/
union uIntPtr
{
uintptr_t uxPtr; /**< THe numeric value. */
const void * pvPtr; /**< THe void pointer. */
};
/**
* @brief Helper function: cast a pointer to a numeric value 'uintptr_t',
* using a union as defined here above.
* @param[in] pvPointer A void pointer to be converted.
* @return The value of the void pointer as an unsigned number.
*/
static uintptr_t void_ptr_to_uintptr( const void * pvPointer )
{
/* The type 'uintptr_t' has the same size as a pointer.
* Therefore, it is safe to use a union to convert it. */
union uIntPtr intPtr;
intPtr.pvPtr = pvPointer;
return intPtr.uxPtr;
}
/*-----------------------------------------------------------*/
/** @brief Get and check the specific lengths depending on the protocol ( TCP/UDP/ICMP/IGMP ).
* @param[in] uxBufferLength The number of bytes to be sent or received.
* @param[in] pxSet A struct describing this packet.
*
* @return Non-zero in case of an error.
*/
static BaseType_t prvChecksumProtocolChecks( size_t uxBufferLength,
struct xPacketSummary * pxSet )
{
BaseType_t xReturn = 0;
/* Both in case of IPv4, as well as IPv6, it has been confirmed that the packet
* is long enough to contain the promised data. */
/* Switch on the Layer 3/4 protocol. */
if( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_UDP )
{
if( ( pxSet->usProtocolBytes < ipSIZE_OF_UDP_HEADER ) ||
( uxBufferLength < ( ipSIZE_OF_ETH_HEADER + pxSet->uxIPHeaderLength + ipSIZE_OF_UDP_HEADER ) ) )
{
pxSet->usChecksum = ipINVALID_LENGTH;
xReturn = 7;
}
if( xReturn == 0 )
{
pxSet->uxProtocolHeaderLength = sizeof( pxSet->pxProtocolHeaders->xUDPHeader );
#if ( ipconfigHAS_DEBUG_PRINTF != 0 )
{
pxSet->pcType = "UDP";
}
#endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
}
}
else if( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_TCP )
{
if( ( pxSet->usProtocolBytes < ipSIZE_OF_TCP_HEADER ) ||
( uxBufferLength < ( ipSIZE_OF_ETH_HEADER + pxSet->uxIPHeaderLength + ipSIZE_OF_TCP_HEADER ) ) )
{
pxSet->usChecksum = ipINVALID_LENGTH;
xReturn = 8;
}
if( xReturn == 0 )
{
uint8_t ucLength = pxSet->pxProtocolHeaders->xTCPHeader.ucTCPOffset >> 4U;
size_t uxOptionsLength;
if( ucLength < FREERTOS_MINIMUM_TCP_OFFSET )
{
pxSet->usChecksum = ipINVALID_LENGTH;
xReturn = 9;
}
else
{
uxOptionsLength = ( ( ( size_t ) ucLength - 5U ) << 2U );
pxSet->uxProtocolHeaderLength = ipSIZE_OF_TCP_HEADER + uxOptionsLength;
#if ( ipconfigHAS_DEBUG_PRINTF != 0 )
{
pxSet->pcType = "TCP";
}
#endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
}
}
}
else if( ( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) ||
( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) )
{
if( ( pxSet->usProtocolBytes < ipSIZE_OF_ICMPv4_HEADER ) ||
( uxBufferLength < ( ipSIZE_OF_ETH_HEADER + pxSet->uxIPHeaderLength + ipSIZE_OF_ICMPv4_HEADER ) ) )
{
pxSet->usChecksum = ipINVALID_LENGTH;
xReturn = 10;
}
if( xReturn == 0 )
{
pxSet->uxProtocolHeaderLength = sizeof( pxSet->pxProtocolHeaders->xICMPHeader );
#if ( ipconfigHAS_DEBUG_PRINTF != 0 )
{
if( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP )
{
pxSet->pcType = "ICMP";
}
else
{
pxSet->pcType = "IGMP";
}
}
#endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
}
}
else if( ( pxSet->xIsIPv6 != pdFALSE ) && ( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP_IPv6 ) )
{
#if ( ipconfigUSE_IPv6 != 0 )
xReturn = prvChecksumICMPv6Checks( uxBufferLength, pxSet );
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
}
else
{
/* Unhandled protocol, other than ICMP, IGMP, UDP, or TCP. */
pxSet->usChecksum = ipUNHANDLED_PROTOCOL;
xReturn = 11;
}
return xReturn;
}
/*-----------------------------------------------------------*/
/** @brief See if the packet doesn't get bigger than the value of MTU.
* @param[in] pxSet A struct describing this packet.
*
* @return Non-zero in case of an error.
*/
static BaseType_t prvChecksumProtocolMTUCheck( struct xPacketSummary * pxSet )
{
BaseType_t xReturn = 0;
/* Here, 'pxSet->usProtocolBytes' contains the size of the protocol data
* ( headers and payload ). */
/* The Ethernet header is excluded from the MTU. */
uint32_t ulMaxLength = ipconfigNETWORK_MTU;
ulMaxLength -= ( uint32_t ) pxSet->uxIPHeaderLength;
if( ( pxSet->usProtocolBytes < ( uint16_t ) pxSet->uxProtocolHeaderLength ) ||
( pxSet->usProtocolBytes > ulMaxLength ) )
{
#if ( ipconfigHAS_DEBUG_PRINTF != 0 )
{
FreeRTOS_debug_printf( ( "usGenerateProtocolChecksum[%s]: len invalid: %u\n", pxSet->pcType, pxSet->usProtocolBytes ) );
}
#endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
/* Again, in a 16-bit return value there is no space to indicate an
* error. For incoming packets, 0x1234 will cause dropping of the packet.
* For outgoing packets, there is a serious problem with the
* format/length */
pxSet->usChecksum = ipINVALID_LENGTH;
xReturn = 13;
}
return xReturn;
}
/*-----------------------------------------------------------*/
/** @brief Do the actual checksum calculations, both the pseudo header, and the payload.
* @param[in] xOutgoingPacket pdTRUE when the packet is to be sent.
* @param[in] pucEthernetBuffer The buffer containing the packet.
* @param[in] pxSet A struct describing this packet.
*/
static void prvChecksumProtocolCalculate( BaseType_t xOutgoingPacket,
const uint8_t * pucEthernetBuffer,
struct xPacketSummary * pxSet )
{
#if ( ipconfigUSE_IPv6 != 0 )
if( pxSet->xIsIPv6 != pdFALSE )
{
uint32_t pulHeader[ 2 ];
/* IPv6 has a 40-byte pseudo header:
* 0..15 Source IPv6 address
* 16..31 Target IPv6 address
* 32..35 Length of payload
* 36..38 three zero's
* 39 Next Header, i.e. the protocol type. */
pulHeader[ 0 ] = ( uint32_t ) pxSet->usProtocolBytes;
pulHeader[ 0 ] = FreeRTOS_htonl( pulHeader[ 0 ] );
pulHeader[ 1 ] = ( uint32_t ) pxSet->ucProtocol;
pulHeader[ 1 ] = FreeRTOS_htonl( pulHeader[ 1 ] );
pxSet->usChecksum = usGenerateChecksum( 0U,
&( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + offsetof( IPHeader_IPv6_t, xSourceAddress ) ] ),
( size_t ) ( 2U * sizeof( pxSet->pxIPPacket_IPv6->xSourceAddress ) ) );
pxSet->usChecksum = usGenerateChecksum( pxSet->usChecksum,
( const uint8_t * ) pulHeader,
( size_t ) ( sizeof( pulHeader ) ) );
}
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
if( ( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) || ( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) )
{
/* ICMP/IGMP do not have a pseudo header for CRC-calculation. */
pxSet->usChecksum = ( uint16_t )
( ~usGenerateChecksum( 0U, &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + pxSet->uxIPHeaderLength ] ), ( size_t ) pxSet->usProtocolBytes ) );
}
else if( ( pxSet->xIsIPv6 != pdFALSE ) && ( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP_IPv6 ) )
{
#if ( ipconfigUSE_IPv6 != 0 )
pxSet->usChecksum = ( uint16_t )
( ~usGenerateChecksum( pxSet->usChecksum,
( uint8_t * ) &( pxSet->pxProtocolHeaders->xTCPHeader ),
( size_t ) pxSet->usProtocolBytes ) );
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
}
else
{
/* Default case is impossible to reach because it's checked before calling this function. */
switch( pxSet->xIsIPv6 ) /* LCOV_EXCL_BR_LINE */
{
#if ( ipconfigUSE_IPv6 != 0 )
case pdTRUE:
/* The CRC of the IPv6 pseudo-header has already been calculated. */
pxSet->usChecksum = ( uint16_t )
( ~usGenerateChecksum( pxSet->usChecksum,
( uint8_t * ) &( pxSet->pxProtocolHeaders->xUDPHeader.usSourcePort ),
( size_t ) ( pxSet->usProtocolBytes ) ) );
break;
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
#if ( ipconfigUSE_IPv4 != 0 )
case pdFALSE:
{
/* The IPv4 pseudo header contains 2 IP-addresses, totalling 8 bytes. */
uint32_t ulByteCount = pxSet->usProtocolBytes;
ulByteCount += 2U * ipSIZE_OF_IPv4_ADDRESS;
/* For UDP and TCP, sum the pseudo header, i.e. IP protocol + length
* fields */
pxSet->usChecksum = ( uint16_t ) ( pxSet->usProtocolBytes + ( ( uint16_t ) pxSet->ucProtocol ) );
/* And then continue at the IPv4 source and destination addresses. */
pxSet->usChecksum = ( uint16_t )
( ~usGenerateChecksum( pxSet->usChecksum,
( const uint8_t * ) &( pxSet->pxIPPacket->xIPHeader.ulSourceIPAddress ),
ulByteCount ) );
}
break;
#endif /* ( ipconfigUSE_IPv4 != 0 ) */
/* Default case is impossible to reach because it's checked before calling this function. */
default: /* LCOV_EXCL_LINE */
/* Shouldn't reach here */
/* MISRA 16.4 Compliance */
break; /* LCOV_EXCL_LINE */
}
/* Sum TCP header and data. */
}
if( xOutgoingPacket == pdFALSE )
{
/* This is in incoming packet. If the CRC is correct, it should be zero. */
if( pxSet->usChecksum == 0U )
{
pxSet->usChecksum = ( uint16_t ) ipCORRECT_CRC;
}
else
{
pxSet->usChecksum = ( uint16_t ) ipWRONG_CRC;
}
}
else
{
if( ( pxSet->usChecksum == 0U ) && ( pxSet->ucProtocol == ( uint8_t ) ipPROTOCOL_UDP ) )
{
/* In case of UDP, a calculated checksum of 0x0000 is transmitted
* as 0xffff. A value of zero would mean that the checksum is not used. */
pxSet->usChecksum = ( uint16_t ) 0xffffu;
}
}
pxSet->usChecksum = FreeRTOS_htons( pxSet->usChecksum );
}
/*-----------------------------------------------------------*/
/** @brief For outgoing packets, set the checksum in the packet,
* for incoming packets: show logging in case an error occurred.
* @param[in] xOutgoingPacket Non-zero if this is an outgoing packet.
* @param[in] pucEthernetBuffer The buffer containing the packet.
* @param[in] uxBufferLength the total number of bytes received, or the number of bytes written
* @param[in] pxSet A struct describing this packet.
*/
static void prvChecksumProtocolSetChecksum( BaseType_t xOutgoingPacket,
const uint8_t * pucEthernetBuffer,
size_t uxBufferLength,
const struct xPacketSummary * pxSet )
{
if( xOutgoingPacket != pdFALSE )
{
prvSetChecksumInPacket( pxSet, pxSet->usChecksum );
}
#if ( ipconfigHAS_DEBUG_PRINTF != 0 )
else if( pxSet->usChecksum != ipCORRECT_CRC )
{
uint16_t usGot;
usGot = prvGetChecksumFromPacket( pxSet );
FreeRTOS_debug_printf( ( "usGenerateProtocolChecksum[%s]: len %d ID %04X: from %xip to %xip cal %04X got %04X\n",
pxSet->pcType,
pxSet->usProtocolBytes,
FreeRTOS_ntohs( pxSet->pxIPPacket->xIPHeader.usIdentification ),
( unsigned ) FreeRTOS_ntohl( pxSet->pxIPPacket->xIPHeader.ulSourceIPAddress ),
( unsigned ) FreeRTOS_ntohl( pxSet->pxIPPacket->xIPHeader.ulDestinationIPAddress ),
FreeRTOS_ntohs( pxSet->usChecksum ),
FreeRTOS_ntohs( usGot ) ) );
}
else
{
/* This is an incoming packet and it doesn't need debug logging. */
}
#endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
/* Mention parameters that are not used by the function. */
( void ) uxBufferLength;
( void ) pucEthernetBuffer;
}
/*-----------------------------------------------------------*/
#if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) || ( ipconfigZERO_COPY_RX_DRIVER != 0 )
/**
* @brief Get the network buffer from the packet buffer.
*
* @param[in] pvBuffer Pointer to the packet buffer.
*
* @return The network buffer if the alignment is correct. Else a NULL is returned.
*/
NetworkBufferDescriptor_t * pxPacketBuffer_to_NetworkBuffer( const void * pvBuffer )
{
return prvPacketBuffer_to_NetworkBuffer( pvBuffer, 0U );
}
#endif /* ( ipconfigZERO_COPY_TX_DRIVER != 0 ) || ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */
/*-----------------------------------------------------------*/
/**
* @brief Get the network buffer from the UDP Payload buffer.
*
* @param[in] pvBuffer Pointer to the UDP payload buffer.
*
* @return The network buffer if the alignment is correct. Else a NULL is returned.
*/
NetworkBufferDescriptor_t * pxUDPPayloadBuffer_to_NetworkBuffer( const void * pvBuffer )
{
NetworkBufferDescriptor_t * pxResult;
if( pvBuffer == NULL )
{
pxResult = NULL;
}
else
{
size_t uxOffset;
/* The input here is a pointer to a payload buffer. Subtract
* the total size of a UDP/IP packet plus the size of the header in
* the network buffer, usually 8 + 2 bytes. */
uintptr_t uxTypeOffset;
const uint8_t * pucIPType;
uint8_t ucIPType;
/* When IPv6 is supported, find out the type of the packet.
* It is stored 48 bytes before the payload buffer as 0x40 or 0x60. */
uxTypeOffset = void_ptr_to_uintptr( pvBuffer );
uxTypeOffset -= ipUDP_PAYLOAD_IP_TYPE_OFFSET;
/* MISRA Ref 11.4.3 [Casting pointer to int for verification] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
/* coverity[misra_c_2012_rule_11_4_violation] */
pucIPType = ( const uint8_t * ) uxTypeOffset;
/* For an IPv4 packet, pucIPType points to 6 bytes before the pucEthernetBuffer,
* for a IPv6 packet, pucIPType will point to the first byte of the IP-header: 'ucVersionTrafficClass'. */
ucIPType = pucIPType[ 0 ] & 0xf0U;
/* To help the translation from a UDP payload pointer to a networkBuffer,
* a byte was stored at a certain negative offset (-48 bytes).
* It must have a value of either 0x4x or 0x6x. */
configASSERT( ( ucIPType == ipTYPE_IPv4 ) || ( ucIPType == ipTYPE_IPv6 ) );
switch( ucIPType ) /* LCOV_EXCL_BR_LINE */
{
#if ( ipconfigUSE_IPv6 != 0 )
case ipTYPE_IPv6:
uxOffset = sizeof( UDPPacket_IPv6_t );
break;
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
#if ( ipconfigUSE_IPv4 != 0 )
case ipTYPE_IPv4:
uxOffset = sizeof( UDPPacket_t );
break;
#endif /* ( ipconfigUSE_IPv4 != 0 ) */
default:
FreeRTOS_debug_printf( ( "pxUDPPayloadBuffer_to_NetworkBuffer: Undefined ucIPType \n" ) );
uxOffset = sizeof( UDPPacket_t );
break;
}
pxResult = prvPacketBuffer_to_NetworkBuffer( pvBuffer, uxOffset );
}
return pxResult;
}
/*-----------------------------------------------------------*/
/**
* @brief Function to check whether the current context belongs to
* the IP-task.
*
* @return If the current context belongs to the IP-task, then pdTRUE is
* returned. Else pdFALSE is returned.
*
* @note Very important: the IP-task is not allowed to call its own API's,
* because it would easily get into a dead-lock.
*/
BaseType_t xIsCallingFromIPTask( void )
{
BaseType_t xReturn;
const struct tskTaskControlBlock * const xCurrentHandle = xTaskGetCurrentTaskHandle();
const struct tskTaskControlBlock * const xCurrentIPTaskHandle = FreeRTOS_GetIPTaskHandle();
if( xCurrentHandle == xCurrentIPTaskHandle )
{
xReturn = pdTRUE;
}
else
{
xReturn = pdFALSE;
}
return xReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Process a 'Network down' event and complete required processing.
* @param pxInterface The interface that goes down.
*/
/* MISRA Ref 8.9.1 [File scoped variables] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
/* coverity[misra_c_2012_rule_8_9_violation] */
/* coverity[single_use] */
void prvProcessNetworkDownEvent( struct xNetworkInterface * pxInterface )
{
NetworkEndPoint_t * pxEndPoint;
configASSERT( pxInterface != NULL );
configASSERT( pxInterface->pfInitialise != NULL );
/* Stop the Address Resolution timer while there is no network. */
#if ipconfigIS_ENABLED( ipconfigUSE_IPv4 )
vIPSetARPTimerEnableState( pdFALSE );
#endif
#if ipconfigIS_ENABLED( ipconfigUSE_IPv6 )
vIPSetNDTimerEnableState( pdFALSE );
#endif
/* 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. */
for( pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface );
pxEndPoint != NULL;
pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint ) )
{
/* The bit 'bEndPointUp' stays low until vIPNetworkUpCalls() is called. */
pxEndPoint->bits.bEndPointUp = ipFALSE_BOOL;
if( pxEndPoint->bits.bIPv6 == ipTRUE_BOOL )
{
/* IPv6 end-points have a solicited-node address that needs extra housekeeping. */
#if ( ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) )
vManageSolicitedNodeAddress( pxEndPoint, pdFALSE );
#endif
}
#if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 )
{
if( pxEndPoint->bits.bCallDownHook != ipFALSE_BOOL )
{
#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 )
{
vApplicationIPNetworkEventHook( eNetworkDown );
}
#else
{
vApplicationIPNetworkEventHook_Multi( eNetworkDown, pxEndPoint );
}
#endif
}
else
{
/* The next time NetworkEventHook will be called for this end-point. */
pxEndPoint->bits.bCallDownHook = ipTRUE_BOOL;
}
}
#endif /* ipconfigUSE_NETWORK_EVENT_HOOK */
#if ipconfigIS_ENABLED( ipconfigUSE_IPv4 )
/* Per the ARP Cache Validation section of https://tools.ietf.org/html/rfc1122
* treat network down as a "delivery problem" and flush the ARP cache for this
* interface. */
FreeRTOS_ClearARP( pxEndPoint );
#endif
#if ipconfigIS_ENABLED( ipconfigUSE_IPv6 )
FreeRTOS_ClearND( pxEndPoint );
#endif
#if ( ipconfigUSE_DHCP == 1 )
if( END_POINT_USES_DHCP( pxEndPoint ) )
{
#if ( ( ipconfigUSE_DHCPv6 != 0 ) && ( ipconfigUSE_IPv6 != 0 ) )
if( pxEndPoint->bits.bIPv6 != ipFALSE_BOOL )
{
vDHCPv6Stop( pxEndPoint );
}
else
#endif /* (( ipconfigUSE_DHCPv6 != 0 ) && ( ipconfigUSE_IPv6 != 0 )) */
{
/* Stop the DHCP process for this end-point. */
vDHCPStop( pxEndPoint );
}
}
#endif /* ( ipconfigUSE_DHCP == 1 ) */
#if ( ( ipconfigUSE_RA != 0 ) && ( ipconfigUSE_IPv6 != 0 ) )
if( END_POINT_USES_RA( pxEndPoint ) )
{
/* Stop the RA/SLAAC process for this end-point. */
vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE );
}
#endif /* ( (ipconfigUSE_RA != 0) && ( ipconfigUSE_IPv6 != 0 )) */
}
/* 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( pxInterface->pfInitialise( pxInterface ) == pdPASS )
{
pxInterface->bits.bInterfaceUp = ipTRUE_BOOL;
/* Set remaining time to 0 so it will become active immediately. */
/* The network is not up until DHCP has completed.
* Start it now for all associated end-points. */
for( pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface );
pxEndPoint != NULL;
pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint ) )
{
#if ( ipconfigUSE_DHCP == 1 )
if( END_POINT_USES_DHCP( pxEndPoint ) )
{
#if ( ( ipconfigUSE_DHCPv6 != 0 ) && ( ipconfigUSE_IPv6 != 0 ) )
if( pxEndPoint->bits.bIPv6 != ipFALSE_BOOL )
{
vDHCPv6Process( pdTRUE, pxEndPoint );
}
else
#endif /* (( ipconfigUSE_DHCPv6 != 0 ) && ( ipconfigUSE_IPv6 != 0 )) */
{
/* Reset the DHCP process for this end-point. */
vDHCPProcess( pdTRUE, pxEndPoint );
}
}
else /* Yes this else ought to be here. */
#endif /* ( ipconfigUSE_DHCP == 1 ) */
#if ( ( ipconfigUSE_RA != 0 ) && ( ipconfigUSE_IPv6 != 0 ) )
if( END_POINT_USES_RA( pxEndPoint ) )
{
/* Reset the RA/SLAAC process for this end-point. */
vRAProcess( pdTRUE, pxEndPoint );
}
else
#endif /* ( (ipconfigUSE_RA != 0) && ( ipconfigUSE_IPv6 != 0 )) */
{
switch( pxEndPoint->bits.bIPv6 ) /* LCOV_EXCL_BR_LINE */
{
#if ( ipconfigUSE_IPv4 != 0 )
case ipFALSE_BOOL:
( void ) memcpy( &( pxEndPoint->ipv4_settings ), &( pxEndPoint->ipv4_defaults ), sizeof( pxEndPoint->ipv4_settings ) );
break;
#endif /* ( ipconfigUSE_IPv4 != 0 ) */
#if ( ipconfigUSE_IPv6 != 0 )
case ipTRUE_BOOL:
( void ) memcpy( &( pxEndPoint->ipv6_settings ), &( pxEndPoint->ipv6_defaults ), sizeof( pxEndPoint->ipv6_settings ) );
break;
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
default:
/* MISRA 16.4 Compliance */
break;
}
/* DHCP or Router Advertisement are not enabled for this end-point.
* Perform any necessary 'network up' processing. */
vIPNetworkUpCalls( pxEndPoint );
}
}
}
else
{
/* At least one interface is down. */
vSetAllNetworksUp( pdFALSE );
/* Nothing else to do. When the 'xNetworkTimer' expires, all interfaces
* with bits.bInterfaceUp cleared will get a new 'eNetworkDownEvent' */
}
}
/*-----------------------------------------------------------*/
/**
* @brief Check the values of configuration options and assert on it. Also verify that the IP-task
* has not already been initialized.
*/
void vPreCheckConfigs( void )
{
/* This function should only be called once. */
configASSERT( xIPIsNetworkTaskReady() == pdFALSE );
configASSERT( xNetworkEventQueue == NULL );