xref: /FreeRTOS-Plus-TCP-v4.0.0/source/FreeRTOS_IPv4.c (revision f5ecc5f201ba569467d090b79b19c5e0baead322)
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_IPv4.c
30  * @brief Implements the basic functionality for the FreeRTOS+TCP network stack.
31  */
32 
33 /* Standard includes. */
34 #include <stdint.h>
35 #include <stdio.h>
36 #include <string.h>
37 
38 /* FreeRTOS includes. */
39 #include "FreeRTOS.h"
40 
41 /* FreeRTOS+TCP includes. */
42 #include "FreeRTOS_IP.h"
43 #include "FreeRTOS_IPv4.h"
44 
45 /* IPv4 multi-cast addresses range from 224.0.0.0.0 to 240.0.0.0. */
46 #define ipFIRST_MULTI_CAST_IPv4    0xE0000000U          /**< Lower bound of the IPv4 multicast address. */
47 #define ipLAST_MULTI_CAST_IPv4     0xF0000000U          /**< Higher bound of the IPv4 multicast address. */
48 
49 /* Just make sure the contents doesn't get compiled if IPv4 is not enabled. */
50 /* *INDENT-OFF* */
51     #if( ipconfigUSE_IPv4 != 0 )
52 /* *INDENT-ON* */
53 
54 #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 )
55     /* Check IPv4 packet length. */
56     static BaseType_t xCheckIPv4SizeFields( const void * const pvEthernetBuffer,
57                                             size_t uxBufferLength );
58 #endif /* ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) */
59 
60 
61 #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 )
62 
63 /**
64  * @brief Check IPv4 packet length.
65  *
66  * @param[in] pvEthernetBuffer The Ethernet packet received.
67  * @param[in] uxBufferLength The total number of bytes received.
68  *
69  * @return pdPASS when the length fields in the packet OK, pdFAIL when the packet
70  *         should be dropped.
71  */
xCheckIPv4SizeFields(const void * const pvEthernetBuffer,size_t uxBufferLength)72     static BaseType_t xCheckIPv4SizeFields( const void * const pvEthernetBuffer,
73                                             size_t uxBufferLength )
74     {
75         size_t uxLength;
76         UBaseType_t uxIPHeaderLength;
77         uint8_t ucProtocol;
78         uint16_t usLength;
79         uint16_t ucVersionHeaderLength;
80         size_t uxMinimumLength;
81         BaseType_t xResult = pdFAIL;
82 
83         /* Map the buffer onto a IP-Packet struct to easily access the
84          * fields of the IP packet. */
85         const IPPacket_t * const pxIPPacket = ( ( const IPPacket_t * const ) pvEthernetBuffer );
86 
87         DEBUG_DECLARE_TRACE_VARIABLE( BaseType_t, xLocation, 0 );
88 
89         do
90         {
91             /* Check for minimum packet size: Ethernet header and an IP-header, 34 bytes */
92             if( uxBufferLength < sizeof( IPPacket_t ) )
93             {
94                 DEBUG_SET_TRACE_VARIABLE( xLocation, 1 );
95                 break;
96             }
97 
98             ucVersionHeaderLength = pxIPPacket->xIPHeader.ucVersionHeaderLength;
99 
100             /* Test if the length of the IP-header is between 20 and 60 bytes,
101              * and if the IP-version is 4. */
102             if( ( ucVersionHeaderLength < ipIPV4_VERSION_HEADER_LENGTH_MIN ) ||
103                 ( ucVersionHeaderLength > ipIPV4_VERSION_HEADER_LENGTH_MAX ) )
104             {
105                 DEBUG_SET_TRACE_VARIABLE( xLocation, 2 );
106                 break;
107             }
108 
109             ucVersionHeaderLength = ( uint16_t ) ( ( ucVersionHeaderLength & ( uint8_t ) 0x0FU ) << 2U );
110             uxIPHeaderLength = ( UBaseType_t ) ucVersionHeaderLength;
111 
112             /* Check if the complete IP-header is transferred. */
113             if( uxBufferLength < ( ipSIZE_OF_ETH_HEADER + uxIPHeaderLength ) )
114             {
115                 DEBUG_SET_TRACE_VARIABLE( xLocation, 3 );
116                 break;
117             }
118 
119             /* Check if the complete IP-header plus protocol data have been transferred: */
120             usLength = pxIPPacket->xIPHeader.usLength;
121             usLength = FreeRTOS_ntohs( usLength );
122 
123             if( uxBufferLength < ( size_t ) ( ipSIZE_OF_ETH_HEADER + ( size_t ) usLength ) )
124             {
125                 DEBUG_SET_TRACE_VARIABLE( xLocation, 4 );
126                 break;
127             }
128 
129             /* Identify the next protocol. */
130             ucProtocol = pxIPPacket->xIPHeader.ucProtocol;
131 
132             /* Switch on the Layer 3/4 protocol. */
133             if( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP )
134             {
135                 /* Expect at least a complete UDP header. */
136                 uxMinimumLength = uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_UDP_HEADER;
137             }
138             else if( ucProtocol == ( uint8_t ) ipPROTOCOL_TCP )
139             {
140                 uxMinimumLength = uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_TCP_HEADER;
141             }
142             else if( ( ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP ) ||
143                      ( ucProtocol == ( uint8_t ) ipPROTOCOL_IGMP ) )
144             {
145                 uxMinimumLength = uxIPHeaderLength + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_ICMPv4_HEADER;
146             }
147             else
148             {
149                 /* Unhandled protocol, other than ICMP, IGMP, UDP, or TCP. */
150                 DEBUG_SET_TRACE_VARIABLE( xLocation, 5 );
151                 break;
152             }
153 
154             if( uxBufferLength < uxMinimumLength )
155             {
156                 DEBUG_SET_TRACE_VARIABLE( xLocation, 6 );
157                 break;
158             }
159 
160             uxLength = ( size_t ) usLength;
161             uxLength -= ( ( uint16_t ) uxIPHeaderLength ); /* normally, minus 20. */
162 
163             if( ( uxLength < ( ( size_t ) sizeof( UDPHeader_t ) ) ) ||
164                 ( uxLength > ( ( size_t ) ipconfigNETWORK_MTU - ( size_t ) uxIPHeaderLength ) ) )
165             {
166                 /* For incoming packets, the length is out of bound: either
167                  * too short or too long. For outgoing packets, there is a
168                  * serious problem with the format/length. */
169                 DEBUG_SET_TRACE_VARIABLE( xLocation, 7 );
170                 break;
171             }
172 
173             xResult = pdPASS;
174         } while( ipFALSE_BOOL );
175 
176         if( xResult != pdPASS )
177         {
178             /* NOP if ipconfigHAS_PRINTF != 1 */
179             FreeRTOS_printf( ( "xCheckIPv4SizeFields: location %ld\n", xLocation ) );
180         }
181 
182         return xResult;
183     }
184 
185 #endif /* ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) */
186 /*-----------------------------------------------------------*/
187 
188 /**
189  * @brief Is the IP address an IPv4 multicast address.
190  *
191  * @param[in] ulIPAddress The IP address being checked.
192  *
193  * @return pdTRUE if the IP address is a multicast address or else, pdFALSE.
194  */
xIsIPv4Multicast(uint32_t ulIPAddress)195 BaseType_t xIsIPv4Multicast( uint32_t ulIPAddress )
196 {
197     BaseType_t xReturn;
198     uint32_t ulIP = FreeRTOS_ntohl( ulIPAddress );
199 
200     if( ( ulIP >= ipFIRST_MULTI_CAST_IPv4 ) && ( ulIP < ipLAST_MULTI_CAST_IPv4 ) )
201     {
202         xReturn = pdTRUE;
203     }
204     else
205     {
206         xReturn = pdFALSE;
207     }
208 
209     return xReturn;
210 }
211 /*-----------------------------------------------------------*/
212 
213 /**
214  * @brief Check whether this IPv4 packet is to be allowed or to be dropped.
215  *
216  * @param[in] pxIPPacket The IP packet under consideration.
217  * @param[in] pxNetworkBuffer The whole network buffer.
218  * @param[in] uxHeaderLength The length of the header.
219  *
220  * @return Whether the packet should be processed or dropped.
221  */
prvAllowIPPacketIPv4(const struct xIP_PACKET * const pxIPPacket,const struct xNETWORK_BUFFER * const pxNetworkBuffer,UBaseType_t uxHeaderLength)222 enum eFrameProcessingResult prvAllowIPPacketIPv4( const struct xIP_PACKET * const pxIPPacket,
223                                                   const struct xNETWORK_BUFFER * const pxNetworkBuffer,
224                                                   UBaseType_t uxHeaderLength )
225 {
226     eFrameProcessingResult_t eReturn = eProcessBuffer;
227 
228     #if ( ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 0 ) || ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 ) )
229         const IPHeader_t * pxIPHeader = &( pxIPPacket->xIPHeader );
230     #else
231 
232         /* or else, the parameter won't be used and the function will be optimised
233          * away */
234         ( void ) pxIPPacket;
235     #endif
236 
237     #if ( ipconfigETHERNET_DRIVER_FILTERS_PACKETS == 0 )
238         {
239             /* In systems with a very small amount of RAM, it might be advantageous
240              * to have incoming messages checked earlier, by the network card driver.
241              * This method may decrease the usage of sparse network buffers. */
242             uint32_t ulDestinationIPAddress = pxIPHeader->ulDestinationIPAddress;
243             uint32_t ulSourceIPAddress = pxIPHeader->ulSourceIPAddress;
244 
245             /* Ensure that the incoming packet is not fragmented because the stack
246              * doesn't not support IP fragmentation. All but the last fragment coming in will have their
247              * "more fragments" flag set and the last fragment will have a non-zero offset.
248              * We need to drop the packet in either of those cases. */
249             if( ( ( pxIPHeader->usFragmentOffset & ipFRAGMENT_OFFSET_BIT_MASK ) != 0U ) || ( ( pxIPHeader->usFragmentOffset & ipFRAGMENT_FLAGS_MORE_FRAGMENTS ) != 0U ) )
250             {
251                 /* Can not handle, fragmented packet. */
252                 eReturn = eReleaseBuffer;
253             }
254 
255             /* Test if the length of the IP-header is between 20 and 60 bytes,
256              * and if the IP-version is 4. */
257             else if( ( pxIPHeader->ucVersionHeaderLength < ipIPV4_VERSION_HEADER_LENGTH_MIN ) ||
258                      ( pxIPHeader->ucVersionHeaderLength > ipIPV4_VERSION_HEADER_LENGTH_MAX ) )
259             {
260                 /* Can not handle, unknown or invalid header version. */
261                 eReturn = eReleaseBuffer;
262             }
263             else if(
264                 ( FreeRTOS_FindEndPointOnIP_IPv4( ulDestinationIPAddress, 4 ) == NULL ) &&
265                 ( pxNetworkBuffer->pxEndPoint == NULL ) &&
266                 /* Is it an IPv4 broadcast address x.x.x.255 ? */
267                 ( ( FreeRTOS_ntohl( ulDestinationIPAddress ) & 0xffU ) != 0xffU ) &&
268                 ( xIsIPv4Multicast( ulDestinationIPAddress ) == pdFALSE ) &&
269                 /* Or (during DHCP negotiation) we have no IP-address yet? */
270                 ( FreeRTOS_IsNetworkUp() != pdFALSE ) )
271             {
272                 /* Packet is not for this node, release it */
273                 eReturn = eReleaseBuffer;
274             }
275             /* Is the source address correct? */
276             else if( ( FreeRTOS_ntohl( ulSourceIPAddress ) & 0xffU ) == 0xffU )
277             {
278                 /* The source address cannot be broadcast address. Replying to this
279                  * packet may cause network storms. Drop the packet. */
280                 eReturn = eReleaseBuffer;
281             }
282             else if( ( memcmp( xBroadcastMACAddress.ucBytes,
283                                pxIPPacket->xEthernetHeader.xDestinationAddress.ucBytes,
284                                sizeof( MACAddress_t ) ) == 0 ) &&
285                      ( ( FreeRTOS_ntohl( ulDestinationIPAddress ) & 0xffU ) != 0xffU ) )
286             {
287                 /* Ethernet address is a broadcast address, but the IP address is not a
288                  * broadcast address. */
289                 eReturn = eReleaseBuffer;
290             }
291             else if( memcmp( xBroadcastMACAddress.ucBytes,
292                              pxIPPacket->xEthernetHeader.xSourceAddress.ucBytes,
293                              sizeof( MACAddress_t ) ) == 0 )
294             {
295                 /* Ethernet source is a broadcast address. Drop the packet. */
296                 eReturn = eReleaseBuffer;
297             }
298             else if( xIsIPv4Multicast( ulSourceIPAddress ) == pdTRUE )
299             {
300                 /* Source is a multicast IP address. Drop the packet in conformity with RFC 1112 section 7.2. */
301                 eReturn = eReleaseBuffer;
302             }
303             else
304             {
305                 /* Packet is not fragmented, destination is this device, source IP and MAC
306                  * addresses are correct. */
307             }
308         }
309     #endif /* ipconfigETHERNET_DRIVER_FILTERS_PACKETS */
310 
311     #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 )
312         {
313             /* Some drivers of NIC's with checksum-offloading will enable the above
314              * define, so that the checksum won't be checked again here */
315             if( eReturn == eProcessBuffer )
316             {
317                 const NetworkEndPoint_t * pxEndPoint = FreeRTOS_FindEndPointOnMAC( &( pxIPPacket->xEthernetHeader.xSourceAddress ), NULL );
318 
319                 /* Do not check the checksum of loop-back messages. */
320                 if( pxEndPoint == NULL )
321                 {
322                     /* Is the IP header checksum correct?
323                      *
324                      * NOTE: When the checksum of IP header is calculated while not omitting
325                      * the checksum field, the resulting value of the checksum always is 0xffff
326                      * which is denoted by ipCORRECT_CRC. See this wiki for more information:
327                      * https://en.wikipedia.org/wiki/IPv4_header_checksum#Verifying_the_IPv4_header_checksum
328                      * and this RFC: https://tools.ietf.org/html/rfc1624#page-4
329                      */
330                     if( usGenerateChecksum( 0U, ( const uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ( size_t ) uxHeaderLength ) != ipCORRECT_CRC )
331                     {
332                         /* Check sum in IP-header not correct. */
333                         eReturn = eReleaseBuffer;
334                     }
335                     /* Is the upper-layer checksum (TCP/UDP/ICMP) correct? */
336                     else if( usGenerateProtocolChecksum( ( uint8_t * ) ( pxNetworkBuffer->pucEthernetBuffer ), pxNetworkBuffer->xDataLength, pdFALSE ) != ipCORRECT_CRC )
337                     {
338                         /* Protocol checksum not accepted. */
339                         eReturn = eReleaseBuffer;
340                     }
341                     else
342                     {
343                         /* The checksum of the received packet is OK. */
344                     }
345                 }
346             }
347         }
348     #else /* if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 ) */
349         {
350             if( eReturn == eProcessBuffer )
351             {
352                 if( xCheckIPv4SizeFields( pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ) != pdPASS )
353                 {
354                     /* Some of the length checks were not successful. */
355                     eReturn = eReleaseBuffer;
356                 }
357             }
358 
359             #if ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 )
360                 {
361                     /* Check if this is a UDP packet without a checksum. */
362                     if( eReturn == eProcessBuffer )
363                     {
364                         uint8_t ucProtocol;
365                         const ProtocolHeaders_t * pxProtocolHeaders;
366 
367                         /* ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS is defined as 0,
368                          * and so UDP packets carrying a protocol checksum of 0, will
369                          * be dropped. */
370                         ucProtocol = pxIPPacket->xIPHeader.ucProtocol;
371                         /* MISRA Ref 11.3.1 [Misaligned access] */
372                         /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
373                         /* coverity[misra_c_2012_rule_11_3_violation] */
374                         pxProtocolHeaders = ( ( ProtocolHeaders_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + ( size_t ) ipSIZE_OF_IPv4_HEADER ] ) );
375 
376                         /* Identify the next protocol. */
377                         if( ucProtocol == ( uint8_t ) ipPROTOCOL_UDP )
378                         {
379                             if( pxProtocolHeaders->xUDPHeader.usChecksum == ( uint16_t ) 0U )
380                             {
381                                 #if ( ipconfigHAS_PRINTF != 0 )
382                                     {
383                                         static BaseType_t xCount = 0;
384 
385                                         /* Exclude this from branch coverage as this is only used for debugging. */
386                                         if( xCount < 5 ) /* LCOV_EXCL_BR_LINE */
387                                         {
388                                             FreeRTOS_printf( ( "prvAllowIPPacket: UDP packet from %xip without CRC dropped\n",
389                                                                FreeRTOS_ntohl( pxIPPacket->xIPHeader.ulSourceIPAddress ) ) );
390                                             xCount++;
391                                         }
392                                     }
393                                 #endif /* ( ipconfigHAS_PRINTF != 0 ) */
394 
395                                 /* Protocol checksum not accepted. */
396                                 eReturn = eReleaseBuffer;
397                             }
398                         }
399                     }
400                 }
401             #endif /* ( ipconfigUDP_PASS_ZERO_CHECKSUM_PACKETS == 0 ) */
402 
403             /* to avoid warning unused parameters */
404             ( void ) pxNetworkBuffer;
405             ( void ) uxHeaderLength;
406         }
407     #endif /* ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 */
408 
409     return eReturn;
410 }
411 
412 /*-----------------------------------------------------------*/
413 
414 
415 /** @brief Check if the IP-header is carrying options.
416  * @param[in] pxNetworkBuffer the network buffer that contains the packet.
417  *
418  * @return Either 'eProcessBuffer' or 'eReleaseBuffer'
419  */
prvCheckIP4HeaderOptions(struct xNETWORK_BUFFER * const pxNetworkBuffer)420 enum eFrameProcessingResult prvCheckIP4HeaderOptions( struct xNETWORK_BUFFER * const pxNetworkBuffer )
421 {
422     eFrameProcessingResult_t eReturn = eProcessBuffer;
423 
424     /* This function is only called for IPv4 packets, with an IP-header
425      * which is larger than 20 bytes.  The extra space is used for IP-options.
426      * The options will either be removed, or the packet shall be dropped,
427      * depending on a user define. */
428 
429     #if ( ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS != 0 )
430         {
431             /* MISRA Ref 11.3.1 [Misaligned access] */
432             /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
433             /* coverity[misra_c_2012_rule_11_3_violation] */
434             IPHeader_t * pxIPHeader = ( ( IPHeader_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER ] ) );
435 
436             /* All structs of headers expect a IP header size of 20 bytes
437              * IP header options were included, we'll ignore them and cut them out. */
438             size_t uxLength = ( size_t ) pxIPHeader->ucVersionHeaderLength;
439 
440             /* Check if the IP headers are acceptable and if it has our destination.
441              * The lowest four bits of 'ucVersionHeaderLength' indicate the IP-header
442              * length in multiples of 4. */
443             size_t uxHeaderLength = ( size_t ) ( ( uxLength & 0x0FU ) << 2 );
444 
445             /* Number of bytes contained in IPv4 header options. */
446             const size_t optlen = ( ( size_t ) uxHeaderLength ) - ipSIZE_OF_IPv4_HEADER;
447             /* From: the previous start of UDP/ICMP/TCP data. */
448             const uint8_t * pucSource = ( const uint8_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( EthernetHeader_t ) + uxHeaderLength ] );
449             /* To: the usual start of UDP/ICMP/TCP data at offset 20 (decimal ) from IP header. */
450             uint8_t * pucTarget = ( uint8_t * ) &( pxNetworkBuffer->pucEthernetBuffer[ sizeof( EthernetHeader_t ) + ipSIZE_OF_IPv4_HEADER ] );
451             /* How many: total length minus the options and the lower headers. */
452             const size_t xMoveLen = pxNetworkBuffer->xDataLength - ( optlen + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_ETH_HEADER );
453 
454             ( void ) memmove( pucTarget, pucSource, xMoveLen );
455             pxNetworkBuffer->xDataLength -= optlen;
456             /* Update the total length of the IP packet after removing options. */
457             pxIPHeader->usLength = FreeRTOS_htons( FreeRTOS_ntohs( pxIPHeader->usLength ) - optlen );
458 
459             /* Rewrite the Version/IHL byte to indicate that this packet has no IP options. */
460             pxIPHeader->ucVersionHeaderLength = ( uint8_t ) ( ( pxIPHeader->ucVersionHeaderLength & 0xF0U ) | /* High nibble is the version. */
461                                                               ( ( ipSIZE_OF_IPv4_HEADER >> 2 ) & 0x0FU ) );
462         }
463     #else /* if ( ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS != 0 ) */
464         {
465             /* 'ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS' is not set, so packets carrying
466              * IP-options will be dropped. */
467             eReturn = eReleaseBuffer;
468         }
469     #endif /* if ( ipconfigIP_PASS_PACKETS_WITH_IP_OPTIONS != 0 ) */
470 
471     return eReturn;
472 }
473 /*-----------------------------------------------------------*/
474 
475 /* *INDENT-OFF* */
476     #endif /* ipconfigUSE_IPv4 != 0 ) */
477 /* *INDENT-ON* */
478