xref: /FreeRTOS-Plus-TCP-v4.0.0/source/FreeRTOS_IPv6_Utils.c (revision b23fa86ac476770d3224c07213bec32f5b1628bd)
1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * http://aws.amazon.com/freertos
25  * http://www.FreeRTOS.org
26  */
27 
28 /**
29  * @file FreeRTOS_IPv6_Utils.c
30  * @brief Implements the basic functionality for the FreeRTOS+TCP network stack functions for IPv6.
31  */
32 
33 /* Standard includes. */
34 #include <stdint.h>
35 #include <stdio.h>
36 
37 /* FreeRTOS includes. */
38 #include "FreeRTOS.h"
39 
40 /* FreeRTOS+TCP includes. */
41 #include "FreeRTOS_IP.h"
42 
43 /*-----------------------------------------------------------*/
44 
45 /* *INDENT-OFF* */
46 #if( ipconfigUSE_IPv6 != 0 )
47 /* *INDENT-ON* */
48 
49 /**
50  * @brief Set multicast MAC address.
51  *
52  * @param[in] pxAddress IPv6 address.
53  * @param[out] pxMACAddress Pointer to MAC address.
54  */
vSetMultiCastIPv6MacAddress(const IPv6_Address_t * pxAddress,MACAddress_t * pxMACAddress)55 void vSetMultiCastIPv6MacAddress( const IPv6_Address_t * pxAddress,
56                                   MACAddress_t * pxMACAddress )
57 {
58     pxMACAddress->ucBytes[ 0 ] = 0x33U;
59     pxMACAddress->ucBytes[ 1 ] = 0x33U;
60     pxMACAddress->ucBytes[ 2 ] = pxAddress->ucBytes[ 12 ];
61     pxMACAddress->ucBytes[ 3 ] = pxAddress->ucBytes[ 13 ];
62     pxMACAddress->ucBytes[ 4 ] = pxAddress->ucBytes[ 14 ];
63     pxMACAddress->ucBytes[ 5 ] = pxAddress->ucBytes[ 15 ];
64 }
65 /*-----------------------------------------------------------*/
66 
67 /** @brief Do the first IPv6 length checks at the IP-header level.
68  * @param[in] pucEthernetBuffer The buffer containing the packet.
69  * @param[in] uxBufferLength The number of bytes to be sent or received.
70  * @param[in] pxSet A struct describing this packet.
71  *
72  * @return Non-zero in case of an error.
73  */
prvChecksumIPv6Checks(uint8_t * pucEthernetBuffer,size_t uxBufferLength,struct xPacketSummary * pxSet)74 BaseType_t prvChecksumIPv6Checks( uint8_t * pucEthernetBuffer,
75                                   size_t uxBufferLength,
76                                   struct xPacketSummary * pxSet )
77 {
78     BaseType_t xReturn = 0;
79     size_t uxExtensionHeaderLength = 0;
80 
81     pxSet->xIsIPv6 = pdTRUE;
82 
83     pxSet->uxIPHeaderLength = ipSIZE_OF_IPv6_HEADER;
84 
85     /* Check for minimum packet size: ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER (54 bytes) */
86     if( uxBufferLength < sizeof( IPPacket_IPv6_t ) )
87     {
88         pxSet->usChecksum = ipINVALID_LENGTH;
89         xReturn = 1;
90     }
91     else
92     {
93         uxExtensionHeaderLength = usGetExtensionHeaderLength( pucEthernetBuffer, uxBufferLength, &pxSet->ucProtocol );
94 
95         if( uxExtensionHeaderLength >= uxBufferLength )
96         {
97             /* Error detected when parsing extension header. */
98             pxSet->usChecksum = ipINVALID_LENGTH;
99             xReturn = 3;
100         }
101         else
102         {
103             /* MISRA Ref 11.3.1 [Misaligned access] */
104             /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
105             /* coverity[misra_c_2012_rule_11_3_violation] */
106             pxSet->pxProtocolHeaders = ( ( ProtocolHeaders_t * ) &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + uxExtensionHeaderLength ] ) );
107             pxSet->usPayloadLength = FreeRTOS_ntohs( pxSet->pxIPPacket_IPv6->usPayloadLength );
108             /* For IPv6, the number of bytes in the protocol is indicated. */
109             pxSet->usProtocolBytes = ( uint16_t ) ( pxSet->usPayloadLength - uxExtensionHeaderLength );
110 
111             size_t uxNeeded = ( size_t ) pxSet->usPayloadLength;
112             uxNeeded += ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER;
113 
114             if( uxBufferLength < uxNeeded )
115             {
116                 /* The packet does not contain a complete IPv6 packet. */
117                 pxSet->usChecksum = ipINVALID_LENGTH;
118                 xReturn = 2;
119             }
120         }
121     }
122 
123     return xReturn;
124 }
125 /*-----------------------------------------------------------*/
126 
127 /**
128  * @brief Check the buffer lengths of an ICMPv6 packet.
129  * @param[in] uxBufferLength The total length of the packet.
130  * @param[in] pxSet A struct describing this packet.
131  * @return Non-zero in case of an error.
132  */
prvChecksumICMPv6Checks(size_t uxBufferLength,struct xPacketSummary * pxSet)133 BaseType_t prvChecksumICMPv6Checks( size_t uxBufferLength,
134                                     struct xPacketSummary * pxSet )
135 {
136     BaseType_t xReturn = 0;
137     size_t xICMPLength;
138 
139     switch( pxSet->pxProtocolHeaders->xICMPHeaderIPv6.ucTypeOfMessage )
140     {
141         case ipICMP_PING_REQUEST_IPv6:
142         case ipICMP_PING_REPLY_IPv6:
143             xICMPLength = sizeof( ICMPEcho_IPv6_t );
144             break;
145 
146         case ipICMP_ROUTER_SOLICITATION_IPv6:
147             xICMPLength = sizeof( ICMPRouterSolicitation_IPv6_t );
148             break;
149 
150         default:
151             xICMPLength = ipSIZE_OF_ICMPv6_HEADER;
152             break;
153     }
154 
155     if( uxBufferLength < ( ipSIZE_OF_ETH_HEADER + pxSet->uxIPHeaderLength + xICMPLength ) )
156     {
157         pxSet->usChecksum = ipINVALID_LENGTH;
158         xReturn = 10;
159     }
160 
161     if( xReturn == 0 )
162     {
163         pxSet->uxProtocolHeaderLength = xICMPLength;
164         #if ( ipconfigHAS_DEBUG_PRINTF != 0 )
165             {
166                 pxSet->pcType = "ICMP_IPv6";
167             }
168         #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */
169     }
170 
171     return xReturn;
172 }
173 /*-----------------------------------------------------------*/
174 
175 /**
176  * @brief Get total length of all extension headers in IPv6 packet.
177  *
178  * @param[in] pucEthernetBuffer The buffer containing the packet.
179  * @param[in] uxBufferLength The number of bytes to be sent or received.
180  * @param[out] pucProtocol The L4 protocol, such as TCP/UDP/ICMPv6.
181  *
182  * @return The total length of all extension headers, or whole buffer length when error detected.
183  */
usGetExtensionHeaderLength(const uint8_t * pucEthernetBuffer,size_t uxBufferLength,uint8_t * pucProtocol)184 size_t usGetExtensionHeaderLength( const uint8_t * pucEthernetBuffer,
185                                    size_t uxBufferLength,
186                                    uint8_t * pucProtocol )
187 {
188     uint8_t ucCurrentHeader;
189     const IPPacket_IPv6_t * pxIPPacket_IPv6;
190     uint8_t ucNextHeader = 0U;
191     size_t uxIndex = ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER;
192     size_t uxHopSize = 0U;
193     BaseType_t xCurrentOrder = 0;
194     BaseType_t xNextOrder = 0;
195     size_t uxReturn = uxBufferLength;
196 
197     if( ( pucEthernetBuffer != NULL ) && ( pucProtocol != NULL ) )
198     {
199         /* MISRA Ref 11.3.1 [Misaligned access] */
200         /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
201         /* coverity[misra_c_2012_rule_11_3_violation] */
202         pxIPPacket_IPv6 = ( ( const IPPacket_IPv6_t * ) pucEthernetBuffer );
203         ucCurrentHeader = pxIPPacket_IPv6->xIPHeader.ucNextHeader;
204 
205         /* Check if packet has extension header. */
206         if( xGetExtensionOrder( ucCurrentHeader, 0U ) > 0 )
207         {
208             while( ( uxIndex + 8U ) < uxBufferLength )
209             {
210                 ucNextHeader = pucEthernetBuffer[ uxIndex ];
211 
212                 xCurrentOrder = xGetExtensionOrder( ucCurrentHeader, ucNextHeader );
213 
214                 /* To avoid compile warning if debug print is disabled. */
215                 ( void ) xCurrentOrder;
216 
217                 /* Read the length expressed in number of octets. */
218                 uxHopSize = ( size_t ) pucEthernetBuffer[ uxIndex + 1U ];
219                 /* And multiply by 8 and add the minimum size of 8. */
220                 uxHopSize = ( uxHopSize * 8U ) + 8U;
221 
222                 if( ( uxIndex + uxHopSize ) >= uxBufferLength )
223                 {
224                     FreeRTOS_debug_printf( ( "The length %lu + %lu of extension header is larger than buffer size %lu \n", uxIndex, uxHopSize, uxBufferLength ) );
225                     break;
226                 }
227 
228                 uxIndex = uxIndex + uxHopSize;
229 
230                 if( ( ucNextHeader == ipPROTOCOL_TCP ) ||
231                     ( ucNextHeader == ipPROTOCOL_UDP ) ||
232                     ( ucNextHeader == ipPROTOCOL_ICMP_IPv6 ) )
233                 {
234                     FreeRTOS_debug_printf( ( "Stop at header %u\n", ucNextHeader ) );
235 
236                     uxReturn = uxIndex - ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER );
237                     *pucProtocol = ucNextHeader;
238                     break;
239                 }
240 
241                 xNextOrder = xGetExtensionOrder( ucNextHeader, pucEthernetBuffer[ uxIndex ] );
242 
243                 FreeRTOS_debug_printf( ( "Going from header %2u (%d) to %2u (%d)\n",
244                                          ucCurrentHeader,
245                                          ( int ) xCurrentOrder,
246                                          ucNextHeader,
247                                          ( int ) xNextOrder ) );
248 
249                 /*
250                  * IPv6 nodes must accept and attempt to process extension headers in
251                  * any order and occurring any number of times in the same packet,
252                  * except for the Hop-by-Hop Options header which is restricted to
253                  * appear immediately after an IPv6 header only. Outlined
254                  * by RFC 2460 section 4.1  Extension Header Order.
255                  */
256                 if( xNextOrder == 1 ) /* ipIPv6_EXT_HEADER_HOP_BY_HOP */
257                 {
258                     FreeRTOS_printf( ( "Wrong order. Hop-by-Hop Options header restricted to appear immediately after an IPv6 header\n" ) );
259                     break;
260                 }
261                 else if( xNextOrder < 0 )
262                 {
263                     FreeRTOS_printf( ( "Invalid extension header detected\n" ) );
264                     break;
265                 }
266                 else
267                 {
268                     /* Do nothing, coverity happy. */
269                 }
270 
271                 ucCurrentHeader = ucNextHeader;
272             }
273         }
274         else
275         {
276             /* No extension headers. */
277             *pucProtocol = ucCurrentHeader;
278             uxReturn = 0;
279         }
280     }
281 
282     return uxReturn;
283 }
284 /*-----------------------------------------------------------*/
285 
286 /* *INDENT-OFF* */
287 #endif /* ( ipconfigUSE_IPv6 != 0 ) */
288 /* *INDENT-ON* */
289