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